def contribute_to_class(self, 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.remote_field.symmetrical and ( self.remote_field.model == "self" or self.remote_field.model == cls._meta.object_name): self.remote_field.related_name = "%s_rel_+" % name elif self.remote_field.is_hidden(): # If the backwards relation is disabled, replace the original # related_name with one generated from the m2m field name. Django # still uses backwards relations internally and we need to avoid # clashes between multiple m2m fields with related_name == '+'. self.remote_field.related_name = "_%s_%s_+" % (cls.__name__.lower(), name) super(ArrayManyToManyField, self).contribute_to_class(cls, name) if not cls._meta.abstract: setattr(cls, self.name, MultiReferenceDescriptor(self.remote_field, reverse=False)) self.opts = cls._meta if self.remote_field.related_name: related_name = force_text(self.remote_field.related_name) % { 'class': cls.__name__.lower(), 'app_label': cls._meta.app_label.lower() } self.remote_field.related_name = related_name def resolve_related_class(model, related, field): field.remote_field.model = related field.do_related_class(related, model) lazy_related_operation(resolve_related_class, cls, self.remote_field.model, field=self)
def make_model_sealable(model): """ Replace forward fields descriptors by sealable ones and reverse fields descriptors attached to SealableModel subclasses as well. This function should be called on a third-party model once all apps are done loading models such as from an AppConfig.ready(). """ opts = model._meta for field in (opts.local_fields + opts.local_many_to_many + opts.private_fields): make_descriptor_sealable(model, field.name) remote_field = field.remote_field if remote_field: # Use lazy_related_operation because lazy relationships might not # be resolved yet. lazy_related_operation(make_remote_field_descriptor_sealable, model, remote_field.model, remote_field=remote_field) # Non SealableModel subclasses won't have remote fields descriptors # attached to them made sealable so make sure to make locally defined # related objects sealable. if not issubclass(model, SealableModel): for related_object in opts.related_objects: make_descriptor_sealable(model, related_object.get_accessor_name())
def contribute_to_class(self, cls: Type[models.Model], name: str, private_only: bool = False) -> None: # pylint: disable=arguments-differ,unused-argument self.name = name self.attname = name self.model = cls cls._meta.add_field(self, private=False) setattr( cls, self.name, ReverseGenericManyToOneDescriptor( self.remote_field), # type: ignore ) self.opts = cls._meta if not cls._meta.abstract: def resolve_related_class(model, related, field): # type: ignore field.remote_field.model = related field.do_related_class(related, model) lazy_related_operation(resolve_related_class, cls, self.remote_field.model, field=self)
def contribute_to_class(self, cls, name): 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: if isinstance(self.remote_field.model, str): 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) if isinstance(self.through, str): 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: self.post_through_setup(cls)
def contribute_to_class(self, cls, name, **kwargs): kwargs['private_only'] = True super().contribute_to_class(cls, name, **kwargs) self.model = cls # Disable the reverse relation for fields inherited by subclasses of a # model in multi-table inheritance. The reverse relation points to the # field of the base model. if self.mti_inherited: self.remote_field.related_name = '+' self.remote_field.related_query_name = None setattr(cls, self.name, ReverseGenericManyToOneDescriptor(self.remote_field)) # Add get_RELATED_order() and set_RELATED_order() to the model this # field belongs to, if the model on the other end of this relation # is ordered with respect to its corresponding GenericForeignKey. if not cls._meta.abstract: def make_generic_foreign_order_accessors(related_model, model): if self._is_matching_generic_foreign_key( model._meta.order_with_respect_to): make_foreign_order_accessors(model, related_model) lazy_related_operation(make_generic_foreign_order_accessors, self.model, self.remote_field.model)
def contribute_to_class(self, cls, name, *args, **kwargs): super(DBToGitManyToManyField, self).contribute_to_class(cls, name, *args, **kwargs) setattr(cls, name, DBToGitManyToManyDescriptor(self)) def resolve_related_class(model, related, field): field.target = related # TODO: Support backward relation lazy_related_operation(resolve_related_class, cls, self.to, field=self)
def create_many_to_many_intermediary_model(field, klass): from django.db import models def set_managed(model, related, through): through._meta.managed = model._meta.managed or related._meta.managed to_model = resolve_relation(klass, field.remote_field.model) name = '%s_%s' % (klass._meta.object_name, field.name) lazy_related_operation(set_managed, klass, to_model, name) to = make_model_tuple(to_model)[1] from_ = klass._meta.model_name if to == from_: to = 'to_%s' % to from_ = 'from_%s' % from_ meta = type( str('Meta'), (object, ), { 'db_table': field._get_m2m_db_table(klass._meta), '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_: models.ForeignKey( klass, related_name='%s+' % name, db_tablespace=field.db_tablespace, db_constraint=field.remote_field.db_constraint, on_delete=DATABASE_CASCADE, ), to: models.ForeignKey( to_model, related_name='%s+' % name, db_tablespace=field.db_tablespace, db_constraint=field.remote_field.db_constraint, on_delete=DATABASE_CASCADE, ) })
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 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 contribute_to_class(self, cls, name): super(PolymorphicTypeField, self).contribute_to_class(cls, name) polymorphic_type = self.polymorphic_type if (isinstance(polymorphic_type, string_types) or polymorphic_type._meta.pk is None): def resolve_polymorphic_type(model, related_model, field): field.do_polymorphic_type(related_model) lazy_related_operation(resolve_polymorphic_type, cls, polymorphic_type, field=self) else: self.do_polymorphic_type(polymorphic_type)
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 contribute_to_class(self, cls, name, private_only=False, **kwargs): self.set_attributes_from_name(name) self.model = cls self.opts = cls._meta if not cls._meta.abstract: def resolve_related_class(model, related, field): field.remote_field.model = related field.do_related_class(related, model) lazy_related_operation(resolve_related_class, cls, self.remote_field.model, field=self) setattr(cls, self.name, self.forward_related_accessor_class(self))
def contribute_to_class(self, cls, name, *args, **kwargs): super(GitToGitForeignKey, self).contribute_to_class(cls, name) setattr(cls, self.name, self.forward_descriptor(self)) def resolve_related_class(model, related, field): field.target = related field.do_related_class(related, model) lazy_related_operation(resolve_related_class, cls, self.target, field=self)
def create_sortable_many_to_many_intermediary_model(field, klass, sort_field_name, base_classes=None): def set_managed(model, related, through): through._meta.managed = model._meta.managed or related._meta.managed to_model = resolve_relation(klass, field.remote_field.model) name = '%s_%s' % (klass._meta.object_name, field.name) lazy_related_operation(set_managed, klass, to_model, name) base_classes = base_classes if base_classes else (models.Model,) # TODO : use autoincrement here ? sort_field = models.IntegerField(default=0) to = make_model_tuple(to_model)[1] from_ = klass._meta.model_name if to == from_: to = 'to_%s' % to from_ = 'from_%s' % from_ meta = type('Meta', (), { 'db_table': field._get_m2m_db_table(klass._meta), # pylint: disable=protected-access 'auto_created': klass, 'app_label': klass._meta.app_label, 'db_tablespace': klass._meta.db_tablespace, 'unique_together': (from_, to), 'ordering': (sort_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. return type(force_str(name), base_classes, { 'Meta': meta, '__module__': klass.__module__, from_: models.ForeignKey( klass, related_name='%s+' % name, db_tablespace=field.db_tablespace, db_constraint=field.remote_field.db_constraint, on_delete=models.CASCADE, ), to: models.ForeignKey( to_model, related_name='%s+' % name, db_tablespace=field.db_tablespace, db_constraint=field.remote_field.db_constraint, on_delete=models.CASCADE, ), # Sort fields sort_field_name: sort_field, '_sort_field_name': sort_field_name, })
def contribute_to_class(self): if isinstance(self.model, str) or self.model._meta.pk is None: def resolve_related_class(cls, model, rel): rel.model = model rel.do_related_class() lazy_related_operation(resolve_related_class, self.field.model, self.model, rel=self) else: self.do_related_class()
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') function = lambda local, related, field: self.bind_m2m_changed(field, related, local) lazy_related_operation(function, cls, "self", field=self)
def contribute_to_class(self, 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.remote_field.symmetrical and (self.remote_field.model == "self" or self.remote_field.model == cls._meta.object_name): self.remote_field.related_name = "%s_rel_+" % name elif self.remote_field.is_hidden(): # If the backwards relation is disabled, replace the original # related_name with one generated from the m2m field name. Django # still uses backwards relations internally and we need to avoid # clashes between multiple m2m fields with related_name == '+'. self.remote_field.related_name = "_%s_%s_+" % ( cls.__name__.lower(), name) super(models.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. # 3) The class owning the m2m field has been swapped out. if not cls._meta.abstract: if self.remote_field.through: def resolve_through_model(_, model, field): field.remote_field.through = model lazy_related_operation(resolve_through_model, cls, self.remote_field.through, field=self) elif not cls._meta.swapped: using_db = self.db_list_for_read.first( ).get_name if self.db_list_for_read else None self.remote_field.through = create_Sharded_many_to_many_intermediary_model( self, cls, db=using_db) # Add the descriptor for the m2m relation. #print(CustomManyToManyDescriptor(self.remote_field, reverse=False)) setattr(cls, self.name, ManyToManyDescriptor(self.remote_field, reverse=False)) # Set up the accessor for the m2m table name for the relation. self.m2m_db_table = partial(self._get_m2m_db_table, cls._meta)
def contribute_to_class(self, cls, name): 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: if self.remote_field.related_name: related_name = self.remote_field.related_name else: related_name = self.opts.default_related_name if related_name: related_name = related_name % { "class": cls.__name__.lower(), "model_name": cls._meta.model_name.lower(), "app_label": cls._meta.app_label.lower(), } self.remote_field.related_name = related_name if self.remote_field.related_query_name: related_query_name = self.remote_field.related_query_name % { "class": cls.__name__.lower(), "app_label": cls._meta.app_label.lower(), } self.remote_field.related_query_name = related_query_name if isinstance(self.remote_field.model, str): 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) if isinstance(self.through, str): 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: self.post_through_setup(cls)
def contribute_to_class(self, cls, name, **kwargs): kwargs['private_only'] = True super(GenericRelation, self).contribute_to_class(cls, name, **kwargs) self.model = cls setattr(cls, self.name, ReverseGenericManyToOneDescriptor(self.remote_field)) # Add get_RELATED_order() and set_RELATED_order() to the model this # field belongs to, if the model on the other end of this relation # is ordered with respect to its corresponding GenericForeignKey. if not cls._meta.abstract: def make_generic_foreign_order_accessors(related_model, model): if self._is_matching_generic_foreign_key(model._meta.order_with_respect_to): make_foreign_order_accessors(model, related_model) lazy_related_operation(make_generic_foreign_order_accessors, self.model, self.remote_field.model)
def contribute_to_class(self, cls, name, **kwargs): kwargs['private_only'] = True super().contribute_to_class(cls, name, **kwargs) self.model = cls setattr(cls, self.name, ReverseGenericManyToOneDescriptor(self.remote_field)) # Add get_RELATED_order() and set_RELATED_order() to the model this # field belongs to, if the model on the other end of this relation # is ordered with respect to its corresponding GenericForeignKey. if not cls._meta.abstract: def make_generic_foreign_order_accessors(related_model, model): if self._is_matching_generic_foreign_key(model._meta.order_with_respect_to): make_foreign_order_accessors(model, related_model) lazy_related_operation(make_generic_foreign_order_accessors, self.model, self.remote_field.model)
def contribute_to_class(self, cls, name, virtual_only=True): self.set_attributes_from_name(name) self.model = cls if virtual_only: cls._meta.add_field(self, virtual=True) else: cls._meta.add_field(self) if self.choices: setattr(cls, 'get_%s_display' % self.name, curry(cls._get_FIELD_display, field=self)) setattr(cls, self.name, self.forward_descriptor(self)) def resolve_related_class(model, related, field): field.target = related field.do_related_class(related, model) lazy_related_operation(resolve_related_class, cls, self.target, field=self)
def create_many_to_many_intermediary_model(field, klass): from django.db import models def set_managed(model, related, through): through._meta.managed = model._meta.managed or related._meta.managed to_model = resolve_relation(klass, field.remote_field.model) name = '%s_%s' % (klass._meta.object_name, field.name) lazy_related_operation(set_managed, klass, to_model, name) to = make_model_tuple(to_model)[1] from_ = klass._meta.model_name if to == from_: to = 'to_%s' % to from_ = 'from_%s' % from_ meta = type(str('Meta'), (object,), { 'db_table': field._get_m2m_db_table(klass._meta), '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_: models.ForeignKey( klass, related_name='%s+' % name, db_tablespace=field.db_tablespace, db_constraint=field.remote_field.db_constraint, on_delete=DATABASE_CASCADE, ), to: models.ForeignKey( to_model, related_name='%s+' % name, db_tablespace=field.db_tablespace, db_constraint=field.remote_field.db_constraint, on_delete=DATABASE_CASCADE, ) })
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. rel = get_rel(self) if rel.symmetrical and (rel.model == "self" or rel.model == cls._meta.object_name): rel.related_name = "%s_rel_+" % name elif rel.is_hidden(): # If the backwards relation is disabled, replace the original # related_name with one generated from the m2m field name. Django # still uses backwards relations internally and we need to avoid # clashes between multiple m2m fields with related_name == '+'. rel.related_name = "_%s_%s_+" % (cls.__name__.lower(), name) # pylint: disable=bad-super-call 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. # 3) The class owning the m2m field has been swapped out. if not cls._meta.abstract: if rel.through: def resolve_through_model(_, model): rel.through = model lazy_related_operation(resolve_through_model, cls, rel.through) elif not cls._meta.swapped: 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 = partial(self._get_m2m_db_table, cls._meta) # pylint: disable=attribute-defined-outside-init
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, **kwargs): kwargs['private_only'] = True super(GenericRelation, self).contribute_to_class(cls, name, **kwargs) self.model = cls setattr(cls, self.name, ReverseGenericManyToOneDescriptor(self.remote_field)) # Add get_RELATED_order() and set_RELATED_order() methods if the model # on the other end of this relation is ordered with respect to this. def matching_gfk(field): return (isinstance(field, GenericForeignKey) and self.content_type_field_name == field.ct_field and self.object_id_field_name == field.fk_field) def make_generic_foreign_order_accessors(related_model, model): if matching_gfk(model._meta.order_with_respect_to): make_foreign_order_accessors(model, related_model) lazy_related_operation(make_generic_foreign_order_accessors, self.model, self.remote_field.model)
def contribute_to_class(self, cls, name, **kwargs): kwargs['virtual_only'] = True super(GenericRelation, self).contribute_to_class(cls, name, **kwargs) self.model = cls setattr(cls, self.name, ReverseGenericManyToOneDescriptor(self.remote_field)) # Add get_RELATED_order() and set_RELATED_order() methods if the model # on the other end of this relation is ordered with respect to this. def matching_gfk(field): return ( isinstance(field, GenericForeignKey) and self.content_type_field_name == field.ct_field and self.object_id_field_name == field.fk_field ) def make_generic_foreign_order_accessors(related_model, model): if matching_gfk(model._meta.order_with_respect_to): make_foreign_order_accessors(model, related_model) lazy_related_operation(make_generic_foreign_order_accessors, self.model, self.remote_field.model)
def _prepare(cls): """Create 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 = partialmethod( cls._get_next_or_previous_in_order, is_next=True) cls.get_previous_in_order = partialmethod( cls._get_next_or_previous_in_order, is_next=False) # Defer creating accessors on the foreign class until it has been # created and registered. If remote_field is None, we're ordering # with respect to a GenericForeignKey and don't know what the # foreign class is - we'll add those accessors later in # contribute_to_class(). if opts.order_with_respect_to.remote_field: wrt = opts.order_with_respect_to remote = wrt.remote_field.model lazy_related_operation(make_foreign_order_accessors, cls, remote) # Give the class a docstring -- its definition. if cls.__doc__ is None: cls.__doc__ = "%s(%s)" % (cls.__name__, ", ".join( f.name for f in opts.fields)) get_absolute_url_override = settings.ABSOLUTE_URL_OVERRIDES.get( opts.label_lower) if get_absolute_url_override: setattr(cls, 'get_absolute_url', get_absolute_url_override) # Set the name of _meta.indexes. This can't be done in # Options.contribute_to_class() because fields haven't been added to # the model at that point. for index in cls._meta.indexes: if not index.name: index.set_name_with_model(cls) class_prepared.send(sender=cls)
def contribute_to_class(self, cls, name, **kwargs): kwargs['private_only'] = True super().contribute_to_class(cls, name, **kwargs) self.model = cls # Disable the reverse relation for fields inherited by subclasses of a # model in multi-table inheritance. The reverse relation points to the # field of the base model. if self.mti_inherited: self.remote_field.related_name = '+' self.remote_field.related_query_name = None setattr(cls, self.name, ReverseGenericManyToOneDescriptor(self.remote_field)) # Add get_RELATED_order() and set_RELATED_order() to the model this # field belongs to, if the model on the other end of this relation # is ordered with respect to its corresponding GenericForeignKey. if not cls._meta.abstract: def make_generic_foreign_order_accessors(related_model, model): if self._is_matching_generic_foreign_key(model._meta.order_with_respect_to): make_foreign_order_accessors(model, related_model) lazy_related_operation(make_generic_foreign_order_accessors, self.model, self.remote_field.model)
def contribute_to_class(self, 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.remote_field.symmetrical and ( self.remote_field.model == "self" or self.remote_field.model == cls._meta.object_name): self.remote_field.related_name = "%s_rel_+" % name elif self.remote_field.is_hidden(): # If the backwards relation is disabled, replace the original # related_name with one generated from the m2m field name. Django # still uses backwards relations internally and we need to avoid # clashes between multiple m2m fields with related_name == '+'. self.remote_field.related_name = "_%s_%s_+" % (cls.__name__.lower(), name) super(models.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. # 3) The class owning the m2m field has been swapped out. if not cls._meta.abstract: if self.remote_field.through: def resolve_through_model(_, model, field): field.remote_field.through = model lazy_related_operation(resolve_through_model, cls, self.remote_field.through, field=self) elif not cls._meta.swapped: self.remote_field.through = create_many_to_many_intermediary_model(self, cls) # Add the descriptor for the m2m relation. setattr(cls, self.name, ManyToManyDescriptor(self.remote_field, reverse=False)) # Set up the accessor for the m2m table name for the relation. self.m2m_db_table = curry(self._get_m2m_db_table, cls._meta)
def create_versioned_many_to_many_intermediary_model( field, cls, field_name): # TODO: Verify functionality against # django.db.models.fields.related:1048 # Let's not care too much on what flags could potentially be set on # that intermediary class (e.g. managed, etc) # Let's play the game, as if the programmer had specified a class # within his models... Here's how. # FIXME: VersionedManyToManyModels do not get registered in the # apps models. # FIXME: This is usually done at django/db/models/base.py:284, # invoked by create_many_to_many_intermediary_model at # django.db.models.fields.related:1048 def set_managed(model, related, through): through._meta.managed = model._meta.managed or \ related._meta.managed to_model = resolve_relation(cls, field.remote_field.model) name = '%s_%s' % (cls._meta.object_name, field_name) lazy_related_operation(set_managed, cls, to_model, name) # Force 'to' to be a string (and leave the hard work to Django) to = make_model_tuple(to_model)[1] from_ = cls._meta.model_name if to == from_: from_ = 'from_%s' % from_ to = 'to_%s' % to meta = type( 'Meta', (object, ), { 'db_table': field._get_m2m_db_table(cls._meta), 'auto_created': cls, 'app_label': cls._meta.app_label, 'db_tablespace': cls._meta.db_tablespace, # 'unique_together' is not applicable as is, due to multiple # versions to be allowed to exist. # '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, }) return type( str(name), (Versionable, ), { 'Meta': meta, '__module__': cls.__module__, from_: VersionedForeignKey( cls, related_name='%s+' % name, db_tablespace=field.db_tablespace, db_constraint=field.remote_field.db_constraint, auto_created=name, on_delete=DO_NOTHING, ), to: VersionedForeignKey( to_model, related_name='%s+' % name, db_tablespace=field.db_tablespace, db_constraint=field.remote_field.db_constraint, auto_created=name, on_delete=DO_NOTHING, ), })
def 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 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: try: remote_field = f.remote_field except AttributeError: continue if remote_field and (remote_field.model == rel.field.model or remote_field.model == '%s.%s' % (rel.field.model._meta.app_label, rel.field.model._meta.object_name)): 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, str): def resolve_through_model(c, model, r): r.set_init('through', model) calc_field_names(r) lazy_related_operation(resolve_through_model, cls, self.through, r=self) 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()
def create_versioned_many_to_many_intermediary_model(field, cls, field_name): # TODO: Verify functionality against # django.db.models.fields.related:1048 # Let's not care too much on what flags could potentially be set on # that intermediary class (e.g. managed, etc) # Let's play the game, as if the programmer had specified a class # within his models... Here's how. # FIXME: VersionedManyToManyModels do not get registered in the # apps models. # FIXME: This is usually done at django/db/models/base.py:284, # invoked by create_many_to_many_intermediary_model at # django.db.models.fields.related:1048 def set_managed(model, related, through): through._meta.managed = model._meta.managed or \ related._meta.managed to_model = resolve_relation(cls, field.remote_field.model) name = '%s_%s' % (cls._meta.object_name, field_name) lazy_related_operation(set_managed, cls, to_model, name) # Force 'to' to be a string (and leave the hard work to Django) to = make_model_tuple(to_model)[1] from_ = cls._meta.model_name if to == from_: from_ = 'from_%s' % from_ to = 'to_%s' % to meta = type('Meta', (object,), { 'db_table': field._get_m2m_db_table(cls._meta), 'auto_created': cls, 'app_label': cls._meta.app_label, 'db_tablespace': cls._meta.db_tablespace, # 'unique_together' is not applicable as is, due to multiple # versions to be allowed to exist. # '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, }) return type(str(name), (Versionable,), { 'Meta': meta, '__module__': cls.__module__, from_: VersionedForeignKey( cls, related_name='%s+' % name, db_tablespace=field.db_tablespace, db_constraint=field.remote_field.db_constraint, auto_created=name, on_delete=DO_NOTHING, ), to: VersionedForeignKey( to_model, related_name='%s+' % name, db_tablespace=field.db_tablespace, db_constraint=field.remote_field.db_constraint, auto_created=name, on_delete=DO_NOTHING, ), })
from __future__ import unicode_literals
def create_sortable_many_to_many_intermediary_model( field, klass, sort_field_name, base_classes=None ): def set_managed(model, related, through): through._meta.managed = model._meta.managed or related._meta.managed to_model = resolve_relation(klass, field.remote_field.model) name = "%s_%s" % (klass._meta.object_name, field.name) lazy_related_operation(set_managed, klass, to_model, name) base_classes = base_classes if base_classes else (models.Model,) # TODO : use autoincrement here ? sort_field = models.IntegerField(default=0) to = make_model_tuple(to_model)[1] from_ = klass._meta.model_name if to == from_: to = "to_%s" % to from_ = "from_%s" % from_ meta = type( "Meta", (), { "db_table": field._get_m2m_db_table(klass._meta), "auto_created": klass, "app_label": klass._meta.app_label, "db_tablespace": klass._meta.db_tablespace, "unique_together": (from_, to), "ordering": (sort_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. return type( force_str(name), base_classes, { "Meta": meta, "__module__": klass.__module__, from_: models.ForeignKey( klass, related_name="%s+" % name, db_tablespace=field.db_tablespace, db_constraint=field.remote_field.db_constraint, on_delete=models.CASCADE, ), to: models.ForeignKey( to_model, related_name="%s+" % name, db_tablespace=field.db_tablespace, db_constraint=field.remote_field.db_constraint, on_delete=models.CASCADE, ), # Sort fields sort_field_name: sort_field, "_sort_field_name": sort_field_name, }, )