def contribute_to_class(self, cls, name): # To support multiple relations to self, it's useful to have a non-None # related name on symmetrical relations for internal reasons. The # concept doesn't make a lot of sense externally ("you want me to # specify *what* on my non-reversible relation?!"), so we set it up # automatically. The funky name reduces the chance of an accidental # clash. if self.rel.symmetrical and (self.rel.to == "self" or self.rel.to == cls._meta.object_name): self.rel.related_name = "%s_rel_+" % name super(ManyToManyField, self).contribute_to_class(cls, name) # The intermediate m2m model is not auto created if: # 1) There is a manually specified intermediate, or # 2) The class owning the m2m field is abstract. # 3) The class owning the m2m field has been swapped out. if not self.rel.through and not cls._meta.abstract and not getattr(cls._meta, 'swapped', False): self.rel.through = create_many_to_many_intermediary_model(self, cls) # Add the descriptor for the m2m relation setattr(cls, self.name, ReverseManyRelatedObjectsDescriptor(self)) # Set up the accessor for the m2m table name for the relation self.m2m_db_table = curry(self._get_m2m_db_table, cls._meta) # Populate some necessary rel arguments so that cross-app relations # work correctly. if isinstance(self.rel.through, six.string_types): def resolve_through_model(field, model, cls): field.rel.through = model add_lazy_relation(cls, self, self.rel.through, resolve_through_model)
def get_intermediate_model_meta_class(self, klass, from_field_name, to_field_name, sort_value_field_name): managed = True to_model = self.rel.to if isinstance(self.rel.to, six.string_types): if self.rel.to != RECURSIVE_RELATIONSHIP_CONSTANT: def set_managed(field, model, cls): field.rel.through._meta.managed = model._meta.managed or cls._meta.managed add_lazy_relation(klass, self, to_model, set_managed) else: managed = klass._meta.managed else: managed = klass._meta.managed or to_model._meta.managed options = { 'db_table': self._get_m2m_db_table(klass._meta), 'managed': managed, 'auto_created': klass, 'app_label': klass._meta.app_label, 'db_tablespace': klass._meta.db_tablespace, 'unique_together': ((from_field_name, to_field_name),), 'ordering': (sort_value_field_name,), 'verbose_name': '%(from)s-%(to)s relationship' % {'from': from_field_name, 'to': to_field_name}, 'verbose_name_plural': '%(from)s-%(to)s relationships' % {'from': from_field_name, 'to': to_field_name}, } # Django 1.6 support. if hasattr(self.model._meta, 'apps'): options.update({ 'apps': self.model._meta.apps, }) return type(str('Meta'), (object,), options)
def contribute_to_class(self, cls, name): if VERSION < (1, 7): self.name = self.column = self.attname = name else: self.set_attributes_from_name(name) self.model = cls cls._meta.add_field(self) setattr(cls, name, self) if not cls._meta.abstract: if isinstance(self.rel.to, six.string_types): def resolve_related_class(field, model, cls): field.rel.to = model add_lazy_relation(cls, self, self.rel.to, resolve_related_class) if isinstance(self.through, six.string_types): def resolve_related_class(field, model, cls): self.through = model self.rel.through = model self.post_through_setup(cls) add_lazy_relation(cls, self, self.through, resolve_related_class) else: self.post_through_setup(cls)
def contribute_to_class(self, source, name): if not issubclass(source, NodeModel): raise TypeError("Relationships may only extend from Nodes.") self.creation_counter = source.creation_counter # make sure this relationship doesn't overlap with another of the same # type and direction if hasattr(source._meta, '_relationships'): for r in source._meta._relationships.values(): if r.rel_type == self.name and r.direction == self.direction: import warnings warnings.warn('`%s` and `%s` share a relationship type and ' 'direction. Is this what you meant to do?' % (r.name, name)) bound = self._get_new_bound_relationship(source, name) source._meta.add_field(bound) if not hasattr(source._meta, '_relationships'): source._meta._relationships = {} source._meta._relationships[name] = bound setattr(source, name, bound) if isinstance(self.__target, basestring): def setup(field, target, source): if not issubclass(target, NodeModel): raise TypeError("Relationships may only extend from Nodes.") # replace the string target with the real target self.__target = target bound._setup_reversed(target) add_lazy_relation(source, self, self.__target, setup) target = self.__target if not self.__is_reversed: bound._setup_reversed(target)
def contribute_to_class(self, cls, name): self.name = self.column = self.attname = name if self.column: self.concrete = True self.model = cls # Put together permission codename using the models name self.codename = "%s_%s" % ( self.permission, self.model._meta.object_name.lower() ) cls._meta.add_field(self) setattr(cls, name, self) # Store the opts for related_query_name() self.opts = cls._meta if not cls._meta.abstract: if isinstance(self.through, basestring): def resolve_related_class(field, model, cls): self.through = model self.post_through_setup(cls) add_lazy_relation( cls, self, self.through, resolve_related_class ) else: self.post_through_setup(cls)
def contribute_to_class(self, cls, name, virtual_only=False): # We need to skip RelatedField in the mro, so we can't use `super()` Field.contribute_to_class( self, cls, name, virtual_only=virtual_only ) self.opts = cls._meta if not cls._meta.abstract and self.rel.related_name: related_name = force_text(self.rel.related_name) % { 'class': cls.__name__.lower(), 'app_label': cls._meta.app_label.lower() } self.rel.related_name = related_name for state in system_layout.allowed_values: with system_layout.as_value(state): other = self.to if other is None: # This should only happen if `cls` is not going to be # used for `state`. Just leave the null value there continue if isinstance(other, str) or other._meta.pk is None: def resolve_related_class(field, model, cls, state=state): with system_layout.as_value(state): field.to = model field.do_related_class(model, cls) add_lazy_relation(cls, self, other, resolve_related_class) else: self.do_related_class(other, cls)
def contribute_to_class(self, cls, name, virtual_only=False): # We need to skip RelatedField in the mro, so we can't use `super()` Field.contribute_to_class(self, cls, name, virtual_only=virtual_only) self.opts = cls._meta if not cls._meta.abstract and self.rel.related_name: related_name = force_text(self.rel.related_name) % { 'class': cls.__name__.lower(), 'app_label': cls._meta.app_label.lower() } self.rel.related_name = related_name for state in system_layout.allowed_values: with system_layout.as_value(state): other = self.to if other is None: # This should only happen if `cls` is not going to be # used for `state`. Just leave the null value there continue if isinstance(other, str) or other._meta.pk is None: def resolve_related_class(field, model, cls, state=state): with system_layout.as_value(state): field.to = model field.do_related_class(model, cls) add_lazy_relation(cls, self, other, resolve_related_class) else: self.do_related_class(other, cls)
def _prepare(cls): """ Creates some methods once self._meta has been populated. """ opts = cls._meta opts._prepare(cls) if opts.order_with_respect_to: cls.get_next_in_order = curry(cls._get_next_or_previous_in_order, is_next=True) cls.get_previous_in_order = curry(cls._get_next_or_previous_in_order, is_next=False) # defer creating accessors on the foreign class until we are # certain it has been created def make_foreign_order_accessors(field, model, cls): setattr(field.rel.to, "get_%s_order" % cls.__name__.lower(), curry(method_get_order, cls)) setattr(field.rel.to, "set_%s_order" % cls.__name__.lower(), curry(method_set_order, cls)) add_lazy_relation( cls, opts.order_with_respect_to, opts.order_with_respect_to.rel.to, make_foreign_order_accessors ) # Give the class a docstring -- its definition. if cls.__doc__ is None: cls.__doc__ = "%s(%s)" % (cls.__name__, ", ".join(f.attname for f in opts.fields)) if hasattr(cls, "get_absolute_url"): cls.get_absolute_url = update_wrapper( curry(get_absolute_url, opts, cls.get_absolute_url), cls.get_absolute_url ) signals.class_prepared.send(sender=cls)
def setup_versioned_models(sender, **kargs): from versions.models import VersionsModel if issubclass(sender, VersionsModel): # Register this model with the version registry. qn = connection.ops.quote_name _versions_table_mappings[qn(sender._meta.db_table)] = sender try: name_map = sender._meta._name_map except AttributeError: name_map = sender._meta.init_name_map() for name, data in name_map.items(): field = data[0] if isinstance(field, (related.ForeignKey, related.ManyToManyField)): if isinstance(field, related.ForeignKey): setattr(sender, name, VersionsReverseSingleRelatedObjectDescriptor(field)) else: setattr(sender, name, VersionsReverseManyRelatedObjectsDescriptor(field)) if isinstance(field.rel.to, basestring): def resolve_related_class(field, model, cls): field.rel.to = model field.do_related_class(model, cls) setattr(field.rel.to, field.related.get_accessor_name(), VersionsForeignRelatedObjectsDescriptor(field.related)) related.add_lazy_relation(sender, field, field.rel.to, resolve_related_class) else: setattr(field.rel.to, field.related.get_accessor_name(), VersionsForeignRelatedObjectsDescriptor(field.related)) # Clean up after ourselves so that no previously initialized field caches are invalid. for cache_name in ('_related_many_to_many_cache', '_name_map', '_related_objects_cache', '_m2m_cache', '_field_cache',): try: delattr(sender._meta, cache_name) except: pass
def contribute_to_class(self, source, name): if not issubclass(source, NodeModel): raise TypeError("Relationships may only extend from Nodes.") self.creation_counter = source.creation_counter # make sure this relationship doesn't overlap with another of the same # type and direction if hasattr(source._meta, '_relationships'): for r in source._meta._relationships.values(): if r.rel_type == self.name and r.direction == self.direction: import warnings warnings.warn( '`%s` and `%s` share a relationship type and ' 'direction. Is this what you meant to do?' % (r.name, name)) bound = self._get_new_bound_relationship(source, name) source._meta.add_field(bound) if not hasattr(source._meta, '_relationships'): source._meta._relationships = {} source._meta._relationships[name] = bound setattr(source, name, bound) if isinstance(self.__target, basestring): def setup(field, target, source): if not issubclass(target, NodeModel): raise TypeError( "Relationships may only extend from Nodes.") # replace the string target with the real target self.__target = target bound._setup_reversed(target) add_lazy_relation(source, self, self.__target, setup) target = self.__target if not self.__is_reversed: bound._setup_reversed(target)
def create_one_to_many_intermediary_model(field, klass): """ Identical to the 'create_many_to_many_intermediary_model' implementation from django.db.models.fields.related, but with a 'unique' constraint added to the receiving relation. """ from django.db import models managed = True if isinstance( field.rel.to, basestring) and field.rel.to != RECURSIVE_RELATIONSHIP_CONSTANT: to_model = field.rel.to to = to_model.split('.')[-1] def set_managed(field, model, cls): field.rel.through._meta.managed = model._meta.managed or cls._meta.managed add_lazy_relation(klass, field, to_model, set_managed) elif isinstance(field.rel.to, basestring): to = klass._meta.object_name to_model = klass managed = klass._meta.managed else: to = field.rel.to._meta.object_name to_model = field.rel.to managed = klass._meta.managed or to_model._meta.managed name = '%s_%s' % (klass._meta.object_name, field.name) if field.rel.to == RECURSIVE_RELATIONSHIP_CONSTANT or to == klass._meta.object_name: from_ = 'from_%s' % to.lower() to = 'to_%s' % to.lower() else: from_ = klass._meta.object_name.lower() to = to.lower() meta = type( 'Meta', (object, ), { 'db_table': field._get_m2m_db_table(klass._meta), 'managed': managed, 'auto_created': klass, 'app_label': klass._meta.app_label, 'unique_together': (from_, to), 'verbose_name': '%(from)s-%(to)s relationship' % { 'from': from_, 'to': to }, 'verbose_name_plural': '%(from)s-%(to)s relationships' % { 'from': from_, 'to': to }, }) # Construct and return the new class. return type( name, (models.Model, ), { 'Meta': meta, '__module__': klass.__module__, from_: models.ForeignKey(klass, related_name='%s+' % name), to: models.ForeignKey( to_model, related_name='%s+' % name, unique=True) })
def create_many_to_many_intermediary_model(field, klass): """ Copied from django, but uses FKToVersion for the 'from' field. Fields are also always called 'from' and 'to' to avoid problems between version combined models. """ managed = True if isinstance(field.rel.to, basestring) and field.rel.to != \ related.RECURSIVE_RELATIONSHIP_CONSTANT: to_model = field.rel.to to = to_model.split('.')[-1] def set_managed(field, model, cls): managed = model._meta.managed or cls._meta.managed if issubclass(cls, VersionView): managed = False field.rel.through._meta.managed = managed related.add_lazy_relation(klass, field, to_model, set_managed) elif isinstance(field.rel.to, basestring): to = klass._meta.object_name to_model = klass managed = klass._meta.managed else: to = field.rel.to._meta.object_name to_model = field.rel.to managed = klass._meta.managed or to_model._meta.managed if issubclass(klass, VersionView): managed = False name = '%s_%s' % (klass._meta.object_name, field.name) if field.rel.to == related.RECURSIVE_RELATIONSHIP_CONSTANT or \ to == klass._meta.object_name: from_ = 'from_%s' % to.lower() to = 'to_%s' % to.lower() else: from_ = klass._meta.object_name.lower() to = to.lower() meta = type('Meta', (object,), { 'db_table': field._get_m2m_db_table(klass._meta), 'managed': managed, 'auto_created': klass, 'app_label': klass._meta.app_label, 'db_tablespace': klass._meta.db_tablespace, 'unique_together': ('from', 'to'), 'verbose_name': '%(from)s-%(to)s relationship' % {'from': from_, 'to': to}, 'verbose_name_plural': '%(from)s-%(to)s relationships' % { 'from': from_, 'to': to}, }) # Construct and return the new class. return type(name, (models.Model,), { 'Meta': meta, '__module__': klass.__module__, 'from': FKToVersion(klass, related_name='%s+' % name, db_tablespace=field.db_tablespace), 'to': models.ForeignKey(to_model, related_name='%s+' % name, db_tablespace=field.db_tablespace) })
def contribute_to_class(self, model, name): self.__m2m_name = name # Call Field, not super, to skip Django's ManyToManyField extra stuff # we don't need ListField.contribute_to_class(self, model, name) # Do the rest after resolving the 'to' relation add_lazy_relation(model, self, self._mm2m_to_or_name, self.contribute_after_resolving)
def contribute_to_class(self): if isinstance(self.model, six.string_types) or self.model._meta.pk is None: def resolve_related_class(rel, model, cls): rel.model = model rel.do_related_class() add_lazy_relation(self.field.model, self, self.model, resolve_related_class) else: self.do_related_class()
def contribute_to_class(self, cls, name): super(MaxCardinalityManyToManyField, self).contribute_to_class(cls, name) if self.max_cardinality or self.reverse_max_cardinality: through = self.rel.through if through: if isinstance(through, basestring): add_lazy_relation(cls, self, through, lambda self, through, cls: self.__connect_through_signals(through)) else: self.__connect_through_signals(through)
def create_many_to_many_intermediary_model(field, klass): """ This function is a large copy/paste from django in order to construct correct through tables for `ManyToManyField` relationships. """ from django.db import models from django_unsigned_fields.fields import UnsignedForeignKey from django.db.models.fields.related import ( add_lazy_relation, RECURSIVE_RELATIONSHIP_CONSTANT, ) managed = True if isinstance(field.rel.to, six.string_types) and field.rel.to != RECURSIVE_RELATIONSHIP_CONSTANT: to_model = field.rel.to to = to_model.split('.')[-1] def set_managed(field, model, cls): field.rel.through._meta.managed = model._meta.managed or cls._meta.managed add_lazy_relation(klass, field, to_model, set_managed) elif isinstance(field.rel.to, six.string_types): to = klass._meta.object_name to_model = klass managed = klass._meta.managed else: to = field.rel.to._meta.object_name to_model = field.rel.to managed = klass._meta.managed or to_model._meta.managed name = '%s_%s' % (klass._meta.object_name, field.name) if field.rel.to == RECURSIVE_RELATIONSHIP_CONSTANT or to == klass._meta.object_name: from_ = 'from_%s' % to.lower() to = 'to_%s' % to.lower() else: from_ = klass._meta.model_name to = to.lower() meta = type(str('Meta'), (object,), { 'db_table': field._get_m2m_db_table(klass._meta), 'managed': managed, 'auto_created': klass, 'app_label': klass._meta.app_label, 'db_tablespace': klass._meta.db_tablespace, 'unique_together': (from_, to), 'verbose_name': '%(from)s-%(to)s relationship' % {'from': from_, 'to': to}, 'verbose_name_plural': '%(from)s-%(to)s relationships' % {'from': from_, 'to': to}, 'apps': field.model._meta.apps, }) to_field_klass = UnsignedForeignKey if field.to_unsigned else models.ForeignKey from_field_klass = UnsignedForeignKey if field.from_unsigned else models.ForeignKey # Construct and return the new class. return type(str(name), (models.Model,), { 'Meta': meta, '__module__': klass.__module__, from_: from_field_klass(klass, related_name='%s+' % name, db_tablespace=field.db_tablespace, db_constraint=field.rel.db_constraint), to: to_field_klass(to_model, related_name='%s+' % name, db_tablespace=field.db_tablespace, db_constraint=field.rel.db_constraint) })
def __new__(cls, name, bases, attrs): new_cls = super().__new__(cls, name, bases, attrs) new_cls._meta.custom_fields_inheritance_by_model = {} new_cls._meta.custom_fields_inheritance_by_path = {} # for each field in custom field inheritance, call # `add_custom_field_inheritance` - it will be called lazy, only when # model will be loaded for field_path, model in new_cls.custom_fields_inheritance.items(): add_lazy_relation(new_cls, field_path, model, add_custom_field_inheritance) return new_cls
def contribute_to_class(self, cls, name): if VERSION < (1, 7): self.name = self.column = self.attname = name else: self.set_attributes_from_name(name) self.model = cls self.opts = cls._meta cls._meta.add_field(self) setattr(cls, name, self) if not cls._meta.abstract: # rel.to renamed to remote_field.model in Django 1.9 if VERSION >= (1, 9): if isinstance(self.remote_field.model, six.string_types): def resolve_related_class(cls, model, field): field.remote_field.model = model lazy_related_operation(resolve_related_class, cls, self.remote_field.model, field=self) else: if isinstance(self.rel.to, six.string_types): def resolve_related_class(field, model, cls): field.rel.to = model add_lazy_relation(cls, self, self.rel.to, resolve_related_class) if isinstance(self.through, six.string_types): if VERSION >= (1, 9): def resolve_related_class(cls, model, field): self.through = model self.remote_field.through = model self.post_through_setup(cls) lazy_related_operation(resolve_related_class, cls, self.through, field=self) else: def resolve_related_class(field, model, cls): self.through = model _remote_field(self).through = model self.post_through_setup(cls) add_lazy_relation(cls, self, self.through, resolve_related_class) else: self.post_through_setup(cls)
def create_sorted_many_to_many_intermediate_model(field, klass): from django.db import models managed = True if isinstance(field.rel.to, six.string_types) and field.rel.to != RECURSIVE_RELATIONSHIP_CONSTANT: to_model = field.rel.to to = to_model.split('.')[-1] def set_managed(field, model, cls): field.rel.through._meta.managed = model._meta.managed or cls._meta.managed add_lazy_relation(klass, field, to_model, set_managed) elif isinstance(field.rel.to, six.string_types): to = klass._meta.object_name to_model = klass managed = klass._meta.managed else: to = field.rel.to._meta.object_name to_model = field.rel.to managed = klass._meta.managed or to_model._meta.managed name = '%s_%s' % (klass._meta.object_name, field.name) if field.rel.to == RECURSIVE_RELATIONSHIP_CONSTANT or to == klass._meta.object_name: from_ = 'from_%s' % to.lower() to = 'to_%s' % to.lower() else: from_ = klass._meta.model_name to = to.lower() meta = type('Meta', (object,), { 'db_table': field._get_m2m_db_table(klass._meta), 'managed': managed, 'auto_created': klass, 'app_label': klass._meta.app_label, 'db_tablespace': klass._meta.db_tablespace, 'unique_together': (from_, to), 'ordering': (field.sort_value_field_name,), 'verbose_name': '%(from)s-%(to)s relationship' % {'from': from_, 'to': to}, 'verbose_name_plural': '%(from)s-%(to)s relationships' % {'from': from_, 'to': to}, 'apps': field.model._meta.apps, }) # Construct and return the new class. def default_sort_value(name): model = models.get_model(klass._meta.app_label, name) return model._default_manager.count() default_sort_value = curry(default_sort_value, name) return type(str(name), (models.Model,), { 'Meta': meta, '__module__': klass.__module__, from_: models.ForeignKey(klass, related_name='%s+' % name, db_tablespace=field.db_tablespace, db_constraint=field.rel.db_constraint), to: models.ForeignKey(to_model, related_name='%s+' % name, db_tablespace=field.db_tablespace, db_constraint=field.rel.db_constraint), field.sort_value_field_name: models.IntegerField(default=default_sort_value), '_sort_field_name': field.sort_value_field_name, '_from_field_name': from_, '_to_field_name': to, })
def contribute_to_class(self, cls, name): super(ImplicitRoleField, self).contribute_to_class(cls, name) setattr(cls, self.name, ImplicitRoleDescriptor(self)) if not hasattr(cls, '__implicit_role_fields'): setattr(cls, '__implicit_role_fields', []) getattr(cls, '__implicit_role_fields').append(self) post_save.connect(self._post_save, cls, True, dispatch_uid='implicit-role-post-save') post_delete.connect(self._post_delete, cls, True, dispatch_uid='implicit-role-post-delete') add_lazy_relation(cls, self, "self", self.bind_m2m_changed)
def setup(self, this_model): super(DependOnRelated,self).setup(this_model) # FIXME: this should not be necessary if self.other_model == related.RECURSIVE_RELATIONSHIP_CONSTANT: self.other_model = self.this_model if isinstance(self.other_model,(str,unicode)): # if ``other_model`` is a string, it certainly is a lazy relation. related.add_lazy_relation(self.this_model, None, self.other_model, self.resolved_model) else: # otherwise it can be resolved directly self.resolved_model(None,self.other_model,None)
def validate_not_to_tenant_model(field, to, model): """ Make sure the `to` relationship is not pointing to an instance of `TenantModelBase`. """ if isinstance(to, string_types): add_lazy_relation(model, field, to, validate_not_to_tenant_model) elif isinstance(to, TenantModelBase): remove_from_app_cache(model, quiet=True) raise ImproperlyConfigured( "`%s.%s`'s `to` option` can't point to an instance of " "`TenantModelBase` since it's not one itself." % (model.__name__, field.name))
def _prepare(cls): """ Creates some methods once self._meta has been populated. """ opts = cls._meta opts._prepare(cls) if opts.order_with_respect_to: cls.get_next_in_order = curry(cls._get_next_or_previous_in_order, is_next=True) cls.get_previous_in_order = curry( cls._get_next_or_previous_in_order, is_next=False) # defer creating accessors on the foreign class until we are # certain it has been created def make_foreign_order_accessors(field, model, cls): setattr(field.rel.to, 'get_%s_order' % cls.__name__.lower(), curry(method_get_order, cls)) setattr(field.rel.to, 'set_%s_order' % cls.__name__.lower(), curry(method_set_order, cls)) add_lazy_relation(cls, opts.order_with_respect_to, opts.order_with_respect_to.rel.to, make_foreign_order_accessors) # Give the class a docstring -- its definition. if cls.__doc__ is None: cls.__doc__ = "%s(%s)" % (cls.__name__, ", ".join( [f.attname for f in opts.fields])) if hasattr(cls, 'get_absolute_url'): cls.get_absolute_url = update_wrapper( curry(get_absolute_url, opts, cls.get_absolute_url), cls.get_absolute_url) if hasattr(cls, 'get_resource_url_list'): cls.get_resource_url_list = staticmethod( curry(get_resource_url_list, opts, cls.get_resource_url_list)) if hasattr(cls, 'get_resource_url_count'): cls.get_resource_url_count = update_wrapper( curry(get_resource_url_count, opts, cls.get_resource_url_count), cls.get_resource_url_count) if hasattr(cls, 'get_resource_url_detail'): cls.get_resource_url_detail = update_wrapper( curry(get_resource_url_detail, opts, cls.get_resource_url_detail), cls.get_resource_url_detail) signals.class_prepared.send(sender=cls)
def create_sorted_many_to_many_intermediate_model(field, klass): from django.db import models managed = True if isinstance(field.rel.to, basestring) and field.rel.to != RECURSIVE_RELATIONSHIP_CONSTANT: to_model = field.rel.to to = to_model.split('.')[-1] def set_managed(field, model, cls): field.rel.through._meta.managed = model._meta.managed or cls._meta.managed add_lazy_relation(klass, field, to_model, set_managed) elif isinstance(field.rel.to, basestring): to = klass._meta.object_name to_model = klass managed = klass._meta.managed else: to = field.rel.to._meta.object_name to_model = field.rel.to managed = klass._meta.managed or to_model._meta.managed name = '%s_%s' % (klass._meta.object_name, field.name) if field.rel.to == RECURSIVE_RELATIONSHIP_CONSTANT or to == klass._meta.object_name: from_ = 'from_%s' % to.lower() to = 'to_%s' % to.lower() else: from_ = klass._meta.object_name.lower() to = to.lower() meta = type('Meta', (object,), { 'db_table': field._get_m2m_db_table(klass._meta), 'managed': managed, 'auto_created': klass, 'app_label': klass._meta.app_label, 'unique_together': (from_, to), 'ordering': (SORT_VALUE_FIELD_NAME,), 'verbose_name': '%(from)s-%(to)s relationship' % {'from': from_, 'to': to}, 'verbose_name_plural': '%(from)s-%(to)s relationships' % {'from': from_, 'to': to}, }) # Construct and return the new class. def default_sort_value(name): model = models.get_model(klass._meta.app_label, name) return model._default_manager.count() default_sort_value = curry(default_sort_value, name) return type(name, (models.Model,), { 'Meta': meta, '__module__': klass.__module__, from_: models.ForeignKey(klass, related_name='%s+' % name), to: models.ForeignKey(to_model, related_name='%s+' % name), SORT_VALUE_FIELD_NAME: models.IntegerField(default=default_sort_value), '_sort_field_name': SORT_VALUE_FIELD_NAME, '_from_field_name': from_, '_to_field_name': to, })
def contribute_to_class(self, cls, name): super(PolymorphicTypeField, self).contribute_to_class(cls, name) polymorphic_type = self.rel.polymorphic_type if (isinstance(polymorphic_type, string_types) or polymorphic_type._meta.pk is None): def resolve_polymorphic_type(field, model, cls): field.validate_polymorphic_type(model) field.rel.polymorphic_type = model field.do_polymorphic_type(model) add_lazy_relation( cls, self, polymorphic_type, resolve_polymorphic_type ) else: self.do_polymorphic_type(polymorphic_type)
def contribute_to_class(self, cls, name, **kwargs): super(RelatedBitField, self).contribute_to_class(cls, name, **kwargs) other = self.fake_rel.to if isinstance(other, six.string_types) or other._meta.pk is None: def resolve_related_class(field, model, cls): rel = field.fake_rel rel.to = model rel.field_name = rel.field_name or model._meta.pk.name add_lazy_relation(cls, self, other, resolve_related_class) else: rel = self.fake_rel rel.field_name = rel.field_name or other._meta.pk.name
def contribute_to_class(self, cls, name): self.name = self.column = name self.model = cls cls._meta.add_field(self) setattr(cls, name, self) if not cls._meta.abstract: if isinstance(self.through, basestring): def resolve_related_class(field, model, cls): self.through = model self.post_through_setup(cls) add_lazy_relation( cls, self, self.through, resolve_related_class ) else: self.post_through_setup(cls)
def contribute_to_class(self, cls, name): super(GenericManyToManyField, self).contribute_to_class(cls, name) self.name = self.column = name self.model = cls cls._meta.add_field(self) setattr(cls, self.name, ReverseGenericManyRelatedObjectsDescriptor(self)) if not cls._meta.abstract: if isinstance(self.through, basestring): def resolve_related_class(field, model, cls): self.through = model add_lazy_relation( cls, self, self.through, resolve_related_class )
def validate_through(cls, field, rel_to, model): """ Make sure the related fields with a specified through points to an instance of `TenantModelBase`. """ through = field.rel.through if isinstance(through, string_types): add_lazy_relation(model, field, through, cls.validate_through) elif not isinstance(through, cls): del cls.references[model] remove_from_app_cache(model, quiet=True) raise ImproperlyConfigured( "Since `%s.%s` is originating from an instance of " "`TenantModelBase` its `through` option must also be pointing " "to one." % (model.__name__, field.name))
def contribute_to_class(self, cls, name): """ Replace the descriptor with our custom descriptor, so that the position field (which is saved in the formfield clean()) gets saved """ if self.sort_field_name is not None: def resolve_sort_field(field, model, cls): field.sort_field = model._meta.get_field(field.sort_field_name) if isinstance(self.rel.through, basestring): add_lazy_relation(cls, self, self.rel.through, resolve_sort_field) else: resolve_sort_field(self, self.rel.through, cls) super(ManyToManyField, self).contribute_to_class(cls, name) if self.sort_field_name is not None: setattr(cls, self.name, SortableReverseManyRelatedObjectsDescriptor(self))
def validate_not_to_tenant_model(field, to, model): """ Make sure the `to` relationship is not pointing to an instance of `TenantModelBase`. """ if isinstance(to, basestring): add_lazy_relation(model, field, to, validate_not_to_tenant_model) elif isinstance(to, TenantModelBase): remove_from_app_cache(model, quiet=True) raise ImproperlyConfigured( "`%s.%s`'s `to` option` can't point to an instance of " "`TenantModelBase` since it's not one itself." % ( model.__name__, field.name ) )
def validate_through(cls, field, rel_to, model): """ Make sure the related fields with a specified through points to an instance of `TenantModelBase`. """ through = field.rel.through if isinstance(through, basestring): add_lazy_relation(model, field, through, cls.validate_through) elif not isinstance(through, cls): del cls.references[model] remove_from_app_cache(model, quiet=True) raise ImproperlyConfigured( "Since `%s.%s` is originating from an instance of " "`TenantModelBase` its `through` option must also be pointing " "to one." % (model.__name__, field.name) )
def contribute_to_class(self, cls, name): if not self.sorted: return super(SortedManyToManyField, self).contribute_to_class(cls, name) # To support multiple relations to self, it's useful to have a non-None # related name on symmetrical relations for internal reasons. The # concept doesn't make a lot of sense externally ("you want me to # specify *what* on my non-reversible relation?!"), so we set it up # automatically. The funky name reduces the chance of an accidental # clash. if self.rel.symmetrical and (self.rel.to == "self" or self.rel.to == cls._meta.object_name): self.rel.related_name = "%s_rel_+" % name super(ManyToManyField, self).contribute_to_class(cls, name) # The intermediate m2m model is not auto created if: # 1) There is a manually specified intermediate, or # 2) The class owning the m2m field is abstract. if not self.rel.through and not cls._meta.abstract: self.rel.through = create_sorted_many_to_many_intermediate_model( self, cls) # Add the descriptor for the m2m relation setattr(cls, self.name, ReverseSortedManyRelatedObjectsDescriptor(self)) # Set up the accessor for the m2m table name for the relation self.m2m_db_table = curry(self._get_m2m_db_table, cls._meta) # Populate some necessary rel arguments so that cross-app relations # work correctly. if isinstance(self.rel.through, six.string_types): def resolve_through_model(field, model, cls): field.rel.through = model add_lazy_relation(cls, self, self.rel.through, resolve_through_model) if hasattr(cls._meta, 'duplicate_targets'): # Django<1.5 if isinstance(self.rel.to, six.string_types): target = self.rel.to else: target = self.rel.to._meta.db_table cls._meta.duplicate_targets[self.column] = (target, "m2m")
def _set_model(self, model): # We need to know the model to generate a valid key for the lookup but # EmbeddedModelFields are not contributed_to_class if used in ListFields # (and friends), so we can only know the model when the ListField sets # our 'model' attribute in its contribute_to_class method. if model is not None and isinstance(self.embedded_model, basestring): # The model argument passed to __init__ was a string, so we need # to make sure to resolve that string to the corresponding model # class, similar to relation fields. We abuse some of the relation # fields' code to do the lookup here: def _resolve_lookup(self_, resolved_model, model): self.embedded_model = resolved_model from django.db.models.fields.related import add_lazy_relation add_lazy_relation(model, self, self.embedded_model, _resolve_lookup) self._model = model
def create_one_to_many_intermediary_model(field, klass): """ Identical to the 'create_many_to_many_intermediary_model' implementation from django.db.models.fields.related, but with a 'unique' constraint added to the receiving relation. """ from django.db import models managed = True if isinstance(field.rel.to, basestring) and field.rel.to != RECURSIVE_RELATIONSHIP_CONSTANT: to_model = field.rel.to to = to_model.split('.')[-1] def set_managed(field, model, cls): field.rel.through._meta.managed = model._meta.managed or cls._meta.managed add_lazy_relation(klass, field, to_model, set_managed) elif isinstance(field.rel.to, basestring): to = klass._meta.object_name to_model = klass managed = klass._meta.managed else: to = field.rel.to._meta.object_name to_model = field.rel.to managed = klass._meta.managed or to_model._meta.managed name = '%s_%s' % (klass._meta.object_name, field.name) if field.rel.to == RECURSIVE_RELATIONSHIP_CONSTANT or to == klass._meta.object_name: from_ = 'from_%s' % to.lower() to = 'to_%s' % to.lower() else: from_ = klass._meta.object_name.lower() to = to.lower() meta = type('Meta', (object,), { 'db_table': field._get_m2m_db_table(klass._meta), 'managed': managed, 'auto_created': klass, 'app_label': klass._meta.app_label, 'unique_together': (from_, to), 'verbose_name': '%(from)s-%(to)s relationship' % {'from': from_, 'to': to}, 'verbose_name_plural': '%(from)s-%(to)s relationships' % {'from': from_, 'to': to}, }) # Construct and return the new class. return type(name, (models.Model,), { 'Meta': meta, '__module__': klass.__module__, from_: models.ForeignKey(klass, related_name='%s+' % name), to: models.ForeignKey(to_model, related_name='%s+' % name, unique=True) })
def _set_model(self, model): # EmbeddedModelFields are not contribute[d]_to_class if using within # ListFields (and friends), so we can only know the model field is # used in when the IterableField sets our 'model' attribute in its # contribute_to_class method. # We need to know the model to generate a valid key for the lookup. if model is not None and isinstance(self.embedded_model, basestring): # The model argument passed to __init__ was a string, so we need # to make sure to resolve that string to the corresponding model # class, similar to relation fields. We abuse some of the # relation fields' code to do the lookup here: def _resolve_lookup(self_, resolved_model, model): self.embedded_model = resolved_model from django.db.models.fields.related import add_lazy_relation add_lazy_relation(model, self, self.embedded_model, _resolve_lookup) self._model = model
def compat_add_lazy_relation(cls, field, relation, operation): if add_lazy_relation is not None: return add_lazy_relation(cls, field, relation, operation) # Rearrange args for new Apps.lazy_model_operation def function(local, related, field): return operation(field, related, local) lazy_related_operation(function, cls, relation, field=field)
def model_prepared(self, sender, **kwargs): ''' Wait for any field dependencies to be resolved, then call finalize(). ''' model = sender deps = self.get_field_dependencies(model) if deps: count = [len(deps)] def dependency_resolved(*args): count[0] = count[0] - 1 if count[0] == 0: self.finalize(model) for dep in deps: add_lazy_relation(model, None, dep.rel.to, dependency_resolved) else: self.finalize(model)
def contribute_to_class(self, cls, name): if VERSION < (1, 7): self.name = self.column = self.attname = name else: self.set_attributes_from_name(name) self.model = cls self.opts = cls._meta cls._meta.add_field(self) setattr(cls, name, self) if not cls._meta.abstract: # rel.to renamed to remote_field.model in Django 1.9 if VERSION >= (1, 9): if isinstance(self.remote_field.model, six.string_types): def resolve_related_class(cls, model, field): field.remote_field.model = model lazy_related_operation( resolve_related_class, cls, self.remote_field.model, field=self ) else: if isinstance(self.rel.to, six.string_types): def resolve_related_class(field, model, cls): field.rel.to = model add_lazy_relation(cls, self, self.rel.to, resolve_related_class) if isinstance(self.through, six.string_types): if VERSION >= (1, 9): def resolve_related_class(cls, model, field): self.through = model self.remote_field.through = model self.post_through_setup(cls) lazy_related_operation( resolve_related_class, cls, self.through, field=self ) else: def resolve_related_class(field, model, cls): self.through = model _remote_field(self).through = model self.post_through_setup(cls) add_lazy_relation( cls, self, self.through, resolve_related_class ) else: self.post_through_setup(cls)
def setup(self, this_model): super(DependOnRelated, self).setup(this_model) # FIXME: this should not be necessary if self.other_model == related.RECURSIVE_RELATIONSHIP_CONSTANT: self.other_model = self.this_model if isinstance(self.other_model, six.string_types): # if ``other_model`` is a string, it certainly is a lazy relation. try: def function(local, related, field): return self.resolved_model(field, related, local) related.lazy_related_operation(function, self.this_model, self.other_model, field=None) except AttributeError: # Django<2.0 related.add_lazy_relation(self.this_model, None, self.other_model, self.resolved_model) else: # otherwise it can be resolved directly self.resolved_model(None, self.other_model, None)
def contribute_to_class(self, cls, name): self.item_field.model = cls self.item_field.name = name super(AbstractIterableField, self).contribute_to_class(cls, name) # If items' field uses SubfieldBase we also need to. item_metaclass = getattr(self.item_field, '__metaclass__', None) if item_metaclass and issubclass(item_metaclass, models.SubfieldBase): setattr(cls, self.name, Creator(self)) if isinstance(self.item_field, models.ForeignKey) and isinstance(self.item_field.rel.to, six.string_types): """ If rel.to is a string because the actual class is not yet defined, look up the actual class later. Refer to django.models.fields.related.RelatedField.contribute_to_class. """ def _resolve_lookup(_, resolved_model, __): self.item_field.rel.to = resolved_model self.item_field.do_related_class(self, cls) add_lazy_relation(cls, self, self.item_field.rel.to, _resolve_lookup)
def validate_related_name(cls, field, rel_to, model): """ Make sure that related fields pointing to non-tenant models specify a related name containing a %(class)s format placeholder. """ if isinstance(rel_to, string_types): add_lazy_relation(model, field, rel_to, cls.validate_related_name) elif not isinstance(rel_to, TenantModelBase): related_name = cls.references[model].related_names[field.name] if (related_name is not None and not (field.rel.is_hidden() or '%(class)s' in related_name)): del cls.references[model] remove_from_app_cache(model, quiet=True) raise ImproperlyConfigured( "Since `%s.%s` is originating from an instance " "of `TenantModelBase` and not pointing to one " "its `related_name` option must ends with a " "'+' or contain the '%%(class)s' format " "placeholder." % (model.__name__, field.name))
def _set_model(self, model): """ Resolves embedded model class once the field knows the model it belongs to. If the model argument passed to __init__ was a string, we need to make sure to resolve that string to the corresponding model class, similar to relation fields. However, we need to know our own model to generate a valid key for the embedded model class lookup and EmbeddedModelFields are not contributed_to_class if used in iterable fields. Thus we rely on the collection field telling us its model (by setting our "model" attribute in its contribute_to_class method). """ self._model = model if model is not None and isinstance(self.embedded_model, basestring): def _resolve_lookup(self_, resolved_model, model): self.embedded_model = resolved_model add_lazy_relation(model, self, self.embedded_model, _resolve_lookup)
def validate_related_name(cls, field, rel_to, model): """ Make sure that related fields pointing to non-tenant models specify a related name containing a %(class)s format placeholder. """ if isinstance(rel_to, basestring): add_lazy_relation(model, field, rel_to, cls.validate_related_name) elif not isinstance(rel_to, cls): related_name = cls.references[model].related_names[field.name] if (related_name is not None and not (field.rel.is_hidden() or '%(class)s' in related_name)): del cls.references[model] remove_from_app_cache(model, quiet=True) raise ImproperlyConfigured( "Since `%s.%s` is originating from an instance " "of `TenantModelBase` and not pointing to one " "its `related_name` option must ends with a " "'+' or contain the '%%(class)s' format " "placeholder." % (model.__name__, field.name) )
def contribute_to_class(self, cls, name): self.item_field.model = cls self.item_field.name = name super(AbstractIterableField, self).contribute_to_class(cls, name) # If items' field uses SubfieldBase we also need to. item_metaclass = getattr(self.item_field, '__metaclass__', None) if item_metaclass and issubclass(item_metaclass, models.SubfieldBase): setattr(cls, self.name, Creator(self)) if isinstance(self.item_field, models.ForeignKey) and isinstance(self.item_field.rel.to, basestring): """ If rel.to is a string because the actual class is not yet defined, look up the actual class later. Refer to django.models.fields.related.RelatedField.contribute_to_class. """ def _resolve_lookup(_, resolved_model, __): self.item_field.rel.to = resolved_model self.item_field.do_related_class(self, cls) add_lazy_relation(cls, self, self.item_field.rel.to, _resolve_lookup)
def contribute_to_class(self, cls, name, **kwargs): if not self.sorted: return super(SortedManyToManyField, self).contribute_to_class(cls, name, **kwargs) # To support multiple relations to self, it's useful to have a non-None # related name on symmetrical relations for internal reasons. The # concept doesn't make a lot of sense externally ("you want me to # specify *what* on my non-reversible relation?!"), so we set it up # automatically. The funky name reduces the chance of an accidental # clash. if self.rel.symmetrical and (self.rel.to == "self" or self.rel.to == cls._meta.object_name): self.rel.related_name = "%s_rel_+" % name super(_ManyToManyField, self).contribute_to_class(cls, name, **kwargs) # The intermediate m2m model is not auto created if: # 1) There is a manually specified intermediate, or # 2) The class owning the m2m field is abstract. if not self.rel.through and not cls._meta.abstract: self.rel.through = self.create_intermediate_model(cls) # Add the descriptor for the m2m relation setattr(cls, self.name, SortedManyToManyDescriptor(self)) # Set up the accessor for the m2m table name for the relation self.m2m_db_table = curry(self._get_m2m_db_table, cls._meta) # Populate some necessary rel arguments so that cross-app relations # work correctly. if isinstance(self.rel.through, six.string_types): def resolve_through_model(field, model, cls): field.rel.through = model add_lazy_relation(cls, self, self.rel.through, resolve_through_model) if hasattr(cls._meta, 'duplicate_targets'): # Django<1.5 if isinstance(self.rel.to, six.string_types): target = self.rel.to else: target = self.rel.to._meta.db_table cls._meta.duplicate_targets[self.column] = (target, "m2m")
def __init__(self, cls, field, name, setup_reversed): add_lazy_relation(cls, field, name, self.__setup) self.__setup_reversed = setup_reversed
def create_many_to_many_intermediary_model(field, klass): """ Copied from django, but uses FKToVersion for the 'from' field. Fields are also always called 'from' and 'to' to avoid problems between version combined models. """ managed = True if (isinstance(field.remote_field.to, basestring) and field.remote_field.to != related.RECURSIVE_RELATIONSHIP_CONSTANT): to_model = field.remote_field.to to = to_model.split('.')[-1] def set_managed(field, model, cls): managed = model._meta.managed or cls._meta.managed if issubclass(cls, VersionView): managed = False field.remote_field.through._meta.managed = managed related.add_lazy_relation(klass, field, to_model, set_managed) elif isinstance(field.remote_field.to, basestring): to = klass._meta.object_name to_model = klass managed = klass._meta.managed else: to = field.remote_field.to._meta.object_name to_model = field.remote_field.to managed = klass._meta.managed or to_model._meta.managed if issubclass(klass, VersionView): managed = False name = '%s_%s' % (klass._meta.object_name, field.name) if (field.remote_field.to == related.RECURSIVE_RELATIONSHIP_CONSTANT or to == klass._meta.object_name): from_ = 'from_%s' % to.lower() to = 'to_%s' % to.lower() else: from_ = klass._meta.object_name.lower() to = to.lower() meta = type('Meta', (object,), { 'db_table': field._get_m2m_db_table(klass._meta), 'managed': managed, 'auto_created': klass, 'app_label': klass._meta.app_label, 'db_tablespace': klass._meta.db_tablespace, 'unique_together': ('from', 'to'), 'verbose_name': '%(from)s-%(to)s relationship' % { 'from': from_, 'to': to}, 'verbose_name_plural': '%(from)s-%(to)s relationships' % { 'from': from_, 'to': to}, 'apps': field.model._meta.apps, }) # Construct and return the new class. return type(str(name), (models.Model,), { 'Meta': meta, '__module__': klass.__module__, 'from': FKToVersion(klass, related_name='%s+' % name, db_tablespace=field.db_tablespace, db_constraint=field.remote_field.db_constraint), 'to': models.ForeignKey(to_model, related_name='%s+' % name, db_tablespace=field.db_tablespace, db_constraint=field.remote_field.db_constraint) })
def contribute_to_class(self, cls, virtual_only=False): # Connect the descriptor for this field setattr(cls, self.field.attname, SourceGM2MDescriptor(self.field)) if cls._meta.abstract or cls._meta.swapped: # do not do anything for abstract or swapped model classes return if not self.through: self.set_init('through', create_gm2m_intermediary_model(self.field, cls)) # we set through_fields to the default intermediary model's # THROUGH_FIELDS as it carries fields assignments for # ModelState instances self.set_init('through_fields', THROUGH_FIELDS) # set related name if not self.field.model._meta.abstract and self.related_name: self.set_init('related_name', self.related_name % { 'class': self.field.model.__name__.lower(), 'app_label': self.field.model._meta.app_label.lower() }) def calc_field_names(rel): # Extract field names from through model and stores them in # rel.through_field (so that they are sent on deconstruct and # passed to ModelState instances) tf_dict = {} if is_fake_model(rel.through): # we populate the through field dict using rel.through_fields # that was either provided or computed beforehand with the # actual model for f, k in zip(rel.through_fields, ('src', 'tgt', 'tgt_ct', 'tgt_fk')): tf_dict[k] = f rel.through._meta._field_names = tf_dict return if rel.through_fields: tf_dict['src'], tf_dict['tgt'] = \ rel.through_fields[:2] for gfk in rel.through._meta.private_fields: if gfk.name == tf_dict['tgt']: break else: raise FieldDoesNotExist( 'Generic foreign key "%s" does not exist in through ' 'model "%s"' % (tf_dict['tgt'], rel.through._meta.model_name) ) tf_dict['tgt_ct'] = gfk.ct_field tf_dict['tgt_fk'] = gfk.fk_field else: for f in rel.through._meta.fields: # ETJ DEBUG if (hasattr(f, 'rel') and f.remote_field \ and (f.remote_field.model == rel.field.model or f.remote_field.model == '%s.%s' % (rel.field.model._meta.app_label,rel.field.model._meta.object_name) or f.remote_field.model == rel.field.model._meta.object_name)): # NOTE: Original code failed on Django 1.10 # if hasattr(f, 'rel') and f.remote_field \ # and (f.remote_field.model == rel.field.model # or f.remote_field.model == '%s.%s' % ( # rel.field.model._meta.app_label, # rel.field.model._meta.object_name)): # END DEBUG tf_dict['src'] = f.name break for f in rel.through._meta.private_fields: if isinstance(f, ct.GenericForeignKey): tf_dict['tgt'] = f.name tf_dict['tgt_ct'] = f.ct_field tf_dict['tgt_fk'] = f.fk_field break if not set(tf_dict.keys()).issuperset(('src', 'tgt')): raise ValueError('Bad through model for GM2M relationship.') rel.through._meta._field_names = tf_dict # save the result in rel.through_fields so that it appears # in the deconstruction. Without that there would be no way for # a ModelState constructed from a migration to know which fields # have which function, as all virtual fields are stripped tf = [] for f in ('src', 'tgt', 'tgt_ct', 'tgt_fk'): tf.append(tf_dict[f]) rel.set_init('through_fields', tf) # resolve through model if it's provided as a string if isinstance(self.through, six.string_types): def resolve_through_model(r, model, c): r.set_init('through', model) calc_field_names(r) add_lazy_relation(cls, self, self.through, resolve_through_model) else: calc_field_names(self) self.related_model = cls for rel in self.rels: # we need to make sure the GM2MUnitRel's field instance is the # right one. Indeed, if cls is derived from an abstract model # where the GM2MField is defined, rel.field is the field linked # to the abstract model rel.field = self.field rel.contribute_to_class()