From 5efd472178f6fd4ee6f3c823c4ec68b15dcf4834 Mon Sep 17 00:00:00 2001 From: Loic Bistuer Date: Mon, 16 Feb 2015 16:06:42 +0700 Subject: [PATCH] Reworked docstrings and comments in related.py. Thanks Tim Graham for the review. --- django/contrib/contenttypes/fields.py | 60 ++++--- django/db/models/fields/related.py | 228 +++++++++++++++++++------- 2 files changed, 201 insertions(+), 87 deletions(-) diff --git a/django/contrib/contenttypes/fields.py b/django/contrib/contenttypes/fields.py index 4bd62225c2..1dddbeb41c 100644 --- a/django/contrib/contenttypes/fields.py +++ b/django/contrib/contenttypes/fields.py @@ -19,9 +19,14 @@ from django.utils.functional import cached_property @python_2_unicode_compatible class GenericForeignKey(object): """ - Provides a generic relation to any object through content-type/object-id - fields. + Provide a generic many-to-one relation through the ``content_type`` and + ``object_id`` fields. + + This class also doubles as an accessor to the related object (similar to + ReverseSingleRelatedObjectDescriptor) by adding itself as a model + attribute. """ + # Field flags auto_created = False concrete = False @@ -96,9 +101,10 @@ class GenericForeignKey(object): return [] def _check_content_type_field(self): - """ Check if field named `field_name` in model `model` exists and is - valid content_type field (is a ForeignKey to ContentType). """ - + """ + Check if field named `field_name` in model `model` exists and is a + valid content_type field (is a ForeignKey to ContentType). + """ try: field = self.model._meta.get_field(self.ct_field) except FieldDoesNotExist: @@ -146,8 +152,8 @@ class GenericForeignKey(object): def instance_pre_init(self, signal, sender, args, kwargs, **_kwargs): """ - Handles initializing an object with the generic FK instead of - content-type/object-id fields. + Handle initializing an object with the generic FK instead of + content_type and object_id fields. """ if self.name in kwargs: value = kwargs.pop(self.name) @@ -256,6 +262,10 @@ class GenericForeignKey(object): class GenericRel(ForeignObjectRel): + """ + Used by GenericRelation to store information about the relation. + """ + def __init__(self, field, to, related_name=None, related_query_name=None, limit_choices_to=None): super(GenericRel, self).__init__( field, to, @@ -267,7 +277,10 @@ class GenericRel(ForeignObjectRel): class GenericRelation(ForeignObject): - """Provides an accessor to generic related objects (e.g. comments)""" + """ + Provide a reverse to a relation created by a GenericForeignKey. + """ + # Field flags auto_created = False @@ -310,9 +323,6 @@ class GenericRelation(ForeignObject): def _check_generic_foreign_key_existence(self): target = self.rel.to if isinstance(target, ModelBase): - # Using `vars` is very ugly approach, but there is no better one, - # because GenericForeignKeys are not considered as fields and, - # therefore, are not included in `target._meta.local_fields`. fields = target._meta.virtual_fields if any(isinstance(field, GenericForeignKey) and field.ct_field == self.content_type_field_name and @@ -358,9 +368,7 @@ class GenericRelation(ForeignObject): def contribute_to_class(self, cls, name, **kwargs): kwargs['virtual_only'] = True super(GenericRelation, self).contribute_to_class(cls, name, **kwargs) - # Save a reference to which model this class is on for future use self.model = cls - # Add the descriptor for the relation setattr(cls, self.name, ReverseGenericRelatedObjectsDescriptor(self.rel)) def set_attributes_from_rel(self): @@ -371,7 +379,7 @@ class GenericRelation(ForeignObject): def get_content_type(self): """ - Returns the content type associated with this field's model. + Return the content type associated with this field's model. """ return ContentType.objects.get_for_model(self.model, for_concrete_model=self.for_concrete_model) @@ -387,7 +395,6 @@ class GenericRelation(ForeignObject): def bulk_related_objects(self, objs, using=DEFAULT_DB_ALIAS): """ Return all objects related to ``objs`` via this ``GenericRelation``. - """ return self.rel.to._base_manager.db_manager(using).filter(**{ "%s__pk" % self.content_type_field_name: ContentType.objects.db_manager(using).get_for_model( @@ -398,17 +405,17 @@ class GenericRelation(ForeignObject): class ReverseGenericRelatedObjectsDescriptor(ForeignRelatedObjectsDescriptor): """ - This class provides the functionality that makes the related-object - managers available as attributes on a model class, for fields that have - multiple "remote" values and have a GenericRelation defined in their model - (rather than having another model pointed *at* them). In the example - "article.publications", the publications attribute is a - ReverseGenericRelatedObjectsDescriptor instance. + Accessor to the related objects manager on the one-to-many relation created + by GenericRelation. + + In the example:: + + class Post(Model): + comments = GenericRelation(Comment) + + ``post.comments`` is a ReverseGenericRelatedObjectsDescriptor instance. """ - - - @cached_property def related_manager_cls(self): return create_generic_related_manager( @@ -419,8 +426,9 @@ class ReverseGenericRelatedObjectsDescriptor(ForeignRelatedObjectsDescriptor): def create_generic_related_manager(superclass, rel): """ - Factory function for a manager that subclasses 'superclass' (which is a - Manager) and adds behavior for generic related objects. + Factory function to create a manager that subclasses another manager + (generally the default manager of a given model) and adds behaviors + specific to generic relations. """ class GenericRelatedObjectManager(superclass): diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py index 8d11f87794..80157f0748 100644 --- a/django/db/models/fields/related.py +++ b/django/db/models/fields/related.py @@ -50,7 +50,8 @@ def add_lazy_relation(cls, field, relation, operation): lazy relationships -- then the relation won't be set up until the class_prepared signal fires at the end of model initialization. - operation is the work that must be performed once the relation can be resolved. + ``operation`` is the work that must be performed once the relation can be + resolved. """ # Check for recursive relations if relation == RECURSIVE_RELATIONSHIP_CONSTANT: @@ -58,17 +59,17 @@ def add_lazy_relation(cls, field, relation, operation): model_name = cls.__name__ else: - # Look for an "app.Model" relation + # Look for an "app.Model" relation. if isinstance(relation, six.string_types): try: app_label, model_name = relation.split(".") except ValueError: - # If we can't split, assume a model in current app + # If we can't split, assume a model in current app. app_label = cls._meta.app_label model_name = relation else: - # it's actually a model class + # It's actually a model class. app_label = relation._meta.app_label model_name = relation._meta.object_name @@ -88,7 +89,7 @@ def add_lazy_relation(cls, field, relation, operation): def do_pending_lookups(sender, **kwargs): """ - Handle any pending relations to the sending model. Sent from class_prepared. + Sent from class_prepared to handle pending relations to the sending model. """ key = (sender._meta.app_label, sender.__name__) for cls, field, operation in sender._meta.apps._pending_lookups.pop(key, []): @@ -98,6 +99,10 @@ signals.class_prepared.connect(do_pending_lookups) class RelatedField(Field): + """ + Base class that all relational fields inherit from. + """ + # Field flags one_to_many = False one_to_one = False @@ -174,8 +179,9 @@ class RelatedField(Field): return [] def _check_clashes(self): - """ Check accessor and reverse query name clashes. """ - + """ + Check accessor and reverse query name clashes. + """ from django.db.models.base import ModelBase errors = [] @@ -278,14 +284,13 @@ class RelatedField(Field): return errors def db_type(self, connection): - '''By default related field will not have a column - as it relates columns to another table''' + # By default related field will not have a column as it relates to + # columns from another table. return None def contribute_to_class(self, cls, name, virtual_only=False): sup = super(RelatedField, self) - # Store the opts for related_query_name() self.opts = cls._meta if hasattr(sup, 'contribute_to_class'): @@ -310,7 +315,7 @@ class RelatedField(Field): @property def swappable_setting(self): """ - Gets the setting that this is powered from for swapping, or None + Get the setting that this is powered from for swapping, or None if it's not swapped in / marked with swappable=False. """ if self.swappable: @@ -350,7 +355,8 @@ class RelatedField(Field): self.contribute_to_related_class(other, self.rel) def get_limit_choices_to(self): - """Returns 'limit_choices_to' for this model field. + """ + Return ``limit_choices_to`` for this model field. If it is a callable, it will be invoked and the result will be returned. @@ -360,7 +366,8 @@ class RelatedField(Field): return self.rel.limit_choices_to def formfield(self, **kwargs): - """Passes ``limit_choices_to`` to field being constructed. + """ + Pass ``limit_choices_to`` to the field being constructed. Only passes it if there is a type that supports related fields. This is a similar strategy used to pass the ``queryset`` to the field @@ -379,19 +386,26 @@ class RelatedField(Field): return super(RelatedField, self).formfield(**defaults) def related_query_name(self): - # This method defines the name that can be used to identify this - # related object in a table-spanning query. It uses the lower-cased - # object_name by default, but this can be overridden with the - # "related_name" option. + """ + Define the name that can be used to identify this related object in a + table-spanning query. + """ return self.rel.related_query_name or self.rel.related_name or self.opts.model_name class SingleRelatedObjectDescriptor(object): - # This class provides the functionality that makes the related-object - # managers available as attributes on a model class, for fields that have - # a single "remote" value, on the class pointed to by a related field. - # In the example "place.restaurant", the restaurant attribute is a - # SingleRelatedObjectDescriptor instance. + """ + Accessor to the related object on the reverse side of a one-to-one + relation. + + In the example:: + + class Restaurant(Model): + place = OneToOneField(Place, related_name='restaurant') + + ``place.restaurant`` is a ``SingleRelatedObjectDescriptor`` instance. + """ + def __init__(self, related): self.related = related self.cache_name = related.get_cache_name() @@ -517,11 +531,18 @@ class SingleRelatedObjectDescriptor(object): class ReverseSingleRelatedObjectDescriptor(object): - # This class provides the functionality that makes the related-object - # managers available as attributes on a model class, for fields that have - # a single "remote" value, on the class that defines the related field. - # In the example "choice.poll", the poll attribute is a - # ReverseSingleRelatedObjectDescriptor instance. + """ + Accessor to the related object on the forward side of a many-to-one or + one-to-one relation. + + In the example:: + + class Choice(Model): + poll = ForeignKey(Place, related_name='choices') + + `choice.poll` is a ReverseSingleRelatedObjectDescriptor instance. + """ + def __init__(self, field_with_rel): self.field = field_with_rel self.cache_name = self.field.get_cache_name() @@ -679,6 +700,12 @@ class ReverseSingleRelatedObjectDescriptor(object): def create_foreign_related_manager(superclass, rel): + """ + Factory function to create a manager that subclasses another manager + (generally the default manager of a given model) and adds behaviors + specific to many-to-one relations. + """ + class RelatedManager(superclass): def __init__(self, instance): super(RelatedManager, self).__init__() @@ -833,11 +860,18 @@ def create_foreign_related_manager(superclass, rel): class ForeignRelatedObjectsDescriptor(object): - # This class provides the functionality that makes the related-object - # managers available as attributes on a model class, for fields that have - # multiple "remote" values and have a ForeignKey pointed at them by - # some other model. In the example "poll.choice_set", the choice_set - # attribute is a ForeignRelatedObjectsDescriptor instance. + """ + Accessor to the related objects manager on the reverse side of a + many-to-one relation. + + In the example:: + + class Choice(Model): + poll = ForeignKey(Place, related_name='choices') + + ``poll.choices`` is a ``ForeignRelatedObjectsDescriptor`` instance. + """ + def __init__(self, rel): self.rel = rel self.field = rel.field @@ -861,9 +895,12 @@ class ForeignRelatedObjectsDescriptor(object): def create_many_related_manager(superclass, rel, reverse): + """ + Factory function to create a manager that subclasses another manager + (generally the default manager of a given model) and adds behaviors + specific to many-to-many relations. + """ - """Creates a manager that subclasses 'superclass' (which is a Manager) - and adds behavior for many-to-many related objects.""" class ManyRelatedManager(superclass): def __init__(self, instance=None): super(ManyRelatedManager, self).__init__() @@ -1198,9 +1235,18 @@ def create_many_related_manager(superclass, rel, reverse): class ManyRelatedObjectsDescriptor(ForeignRelatedObjectsDescriptor): + """ + Accessor to the related objects manager on the forward and reverse sides of + a many-to-many relation. + In the example:: + class Pizza(Model): + toppings = ManyToManyField(Topping, related_name='pizzas') + ``pizza.toppings`` and ``topping.pizzas`` are ManyRelatedObjectsDescriptor + instances. + """ def __init__(self, rel, reverse=False): super(ManyRelatedObjectsDescriptor, self).__init__(rel) @@ -1217,8 +1263,6 @@ class ManyRelatedObjectsDescriptor(ForeignRelatedObjectsDescriptor): @cached_property def related_manager_cls(self): model = self.rel.related_model if self.reverse else self.rel.to - # Dynamically create a class that subclasses the related model's - # default manager. return create_many_related_manager( model._default_manager.__class__, self.rel, @@ -1227,7 +1271,12 @@ class ManyRelatedObjectsDescriptor(ForeignRelatedObjectsDescriptor): class ForeignObjectRel(object): + """ + Used by ForeignObject to store information about the relation. + ``_meta.get_fields()`` returns this class to provide access to the field + flags for the reverse relation. + """ # Field flags auto_created = True @@ -1301,7 +1350,7 @@ class ForeignObjectRel(object): def get_choices(self, include_blank=True, blank_choice=BLANK_CHOICE_DASH, limit_to_currently_related=False): """ - Returns choices with a default blank choices included, for use as + Return choices with a default blank choices included, for use as SelectField choices for this field. Analog of django.db.models.fields.Field.get_choices(), provided @@ -1370,6 +1419,20 @@ class ForeignObjectRel(object): class ManyToOneRel(ForeignObjectRel): + """ + Used by the ForeignKey field to store information about the relation. + + ``_meta.get_fields()`` returns this class to provide access to the field + flags for the reverse relation. + + Note: Because we somewhat abuse the Rel objects by using them as reverse + fields we get the funny situation where + ``ManyToOneRel.many_to_one == False`` and + ``ManyToOneRel.one_to_many == True``. This is unfortunate but the actual + ManyToOneRel class is a private API and there is work underway to turn + reverse relations into actual fields. + """ + def __init__(self, field, to, field_name, related_name=None, related_query_name=None, limit_choices_to=None, parent_link=False, on_delete=None): super(ManyToOneRel, self).__init__( @@ -1385,8 +1448,7 @@ class ManyToOneRel(ForeignObjectRel): def get_related_field(self): """ - Returns the Field in the 'to' object to which this relationship is - tied. + Return the Field in the 'to' object to which this relationship is tied. """ field = self.to._meta.get_field(self.field_name) if not field.concrete: @@ -1399,6 +1461,13 @@ class ManyToOneRel(ForeignObjectRel): class OneToOneRel(ManyToOneRel): + """ + Used by OneToOneField to store information about the relation. + + ``_meta.get_fields()`` returns this class to provide access to the field + flags for the reverse relation. + """ + def __init__(self, field, to, field_name, related_name=None, related_query_name=None, limit_choices_to=None, parent_link=False, on_delete=None): super(OneToOneRel, self).__init__( @@ -1414,6 +1483,13 @@ class OneToOneRel(ManyToOneRel): class ManyToManyRel(ForeignObjectRel): + """ + Used by ManyToManyField to store information about the relation. + + ``_meta.get_fields()`` returns this class to provide access to the field + flags for the reverse relation. + """ + def __init__(self, field, to, related_name=None, related_query_name=None, limit_choices_to=None, symmetrical=True, through=None, through_fields=None, db_constraint=True): @@ -1437,7 +1513,7 @@ class ManyToManyRel(ForeignObjectRel): def get_related_field(self): """ - Returns the field in the 'to' object to which this relationship is tied. + Return the field in the 'to' object to which this relationship is tied. Provided for symmetry with ManyToOneRel. """ opts = self.through._meta @@ -1452,6 +1528,10 @@ class ManyToManyRel(ForeignObjectRel): class ForeignObject(RelatedField): + """ + Abstraction of the ForeignKey relation, supports multi-column relations. + """ + # Field flags many_to_many = False many_to_one = True @@ -1491,7 +1571,6 @@ class ForeignObject(RelatedField): if rel_is_string or not self.requires_unique_target: return [] - # Skip if the try: self.foreign_related_fields except FieldDoesNotExist: @@ -1640,7 +1719,7 @@ class ForeignObject(RelatedField): def get_extra_descriptor_filter(self, instance): """ - Returns an extra filter condition for related object fetching when + Return an extra filter condition for related object fetching when user does 'instance.fieldname', that is the extra filter is used in the descriptor of the field. @@ -1655,7 +1734,7 @@ class ForeignObject(RelatedField): def get_extra_restriction(self, where_class, alias, related_alias): """ - Returns a pair condition used for joining and subquery pushdown. The + Return a pair condition used for joining and subquery pushdown. The condition is something that responds to as_sql(compiler, connection) method. @@ -1765,6 +1844,14 @@ class ForeignObject(RelatedField): class ForeignKey(ForeignObject): + """ + Provide a many-to-one relation by adding a column to the local model + to hold the remote value. + + By default ForeignKey will target the pk of the remote model but this + behavior can be changed by using the ``to_field`` argument. + """ + # Field flags many_to_many = False many_to_one = True @@ -1981,10 +2068,11 @@ class ForeignKey(ForeignObject): class OneToOneField(ForeignKey): """ A OneToOneField is essentially the same as a ForeignKey, with the exception - that always carries a "unique" constraint with it and the reverse relation - always returns the object pointed to (since there will only ever be one), - rather than returning a list. + that it always carries a "unique" constraint with it and the reverse + relation always returns the object pointed to (since there will only ever + be one), rather than returning a list. """ + # Field flags many_to_many = False many_to_one = False @@ -2018,7 +2106,7 @@ class OneToOneField(ForeignKey): setattr(instance, self.attname, data) def _check_unique(self, **kwargs): - # override ForeignKey since check isn't applicable here + # Override ForeignKey since check isn't applicable here. return [] @@ -2078,6 +2166,15 @@ def create_many_to_many_intermediary_model(field, klass): class ManyToManyField(RelatedField): + """ + Provide a many-to-many relation by using an intermediary model that + holds two ForeignKey fields pointed at the two sides of the relation. + + Unless a ``through`` model was provided, ManyToManyField will use the + create_many_to_many_intermediary_model factory to automatically generate + the intermediary model. + """ + # Field flags many_to_many = True many_to_one = False @@ -2296,10 +2393,10 @@ class ManyToManyField(RelatedField): ) ) - # Validate `through_fields` + # Validate `through_fields`. if self.rel.through_fields is not None: # Validate that we're given an iterable of at least two items - # and that none of them is "falsy" + # and that none of them is "falsy". if not (len(self.rel.through_fields) >= 2 and self.rel.through_fields[0] and self.rel.through_fields[1]): errors.append( @@ -2317,7 +2414,7 @@ class ManyToManyField(RelatedField): # Validate the given through fields -- they should be actual # fields on the through model, and also be foreign keys to the - # expected models + # expected models. else: assert from_model is not None, ( "ManyToManyField with intermediate " @@ -2372,7 +2469,7 @@ class ManyToManyField(RelatedField): def deconstruct(self): name, path, args, kwargs = super(ManyToManyField, self).deconstruct() - # Handle the simpler arguments + # Handle the simpler arguments. if self.db_table is not None: kwargs['db_table'] = self.db_table if self.rel.db_constraint is not True: @@ -2395,7 +2492,7 @@ class ManyToManyField(RelatedField): # of a swap. swappable_setting = self.swappable_setting if swappable_setting is not None: - # If it's already a settings reference, error + # If it's already a settings reference, error. if hasattr(kwargs['to'], "setting_name"): if kwargs['to'].setting_name != swappable_setting: raise ValueError( @@ -2403,7 +2500,7 @@ class ManyToManyField(RelatedField): "model that is swapped in place of more than one model " "(%s and %s)" % (kwargs['to'].setting_name, swappable_setting) ) - # Set it + from django.db.migrations.writer import SettingsReference kwargs['to'] = SettingsReference( kwargs['to'], @@ -2439,7 +2536,10 @@ class ManyToManyField(RelatedField): return Field.get_choices(self, include_blank=False) def _get_m2m_db_table(self, opts): - "Function that can be curried to provide the m2m table name for this relation" + """ + Function that can be curried to provide the m2m table name for this + relation. + """ if self.rel.through is not None: return self.rel.through._meta.db_table elif self.db_table: @@ -2449,7 +2549,10 @@ class ManyToManyField(RelatedField): connection.ops.max_name_length()) def _get_m2m_attr(self, related, attr): - "Function that can be curried to provide the source accessor or DB column name for the m2m table" + """ + Function that can be curried to provide the source accessor or DB + column name for the m2m table. + """ cache_attr = '_m2m_%s_cache' % attr if hasattr(self, cache_attr): return getattr(self, cache_attr) @@ -2464,7 +2567,10 @@ class ManyToManyField(RelatedField): return getattr(self, cache_attr) def _get_m2m_reverse_attr(self, related, attr): - "Function that can be curried to provide the related accessor or DB column name for the m2m table" + """ + Function that can be curried to provide the related accessor or DB + column name for the m2m table. + """ cache_attr = '_m2m_reverse_%s_cache' % attr if hasattr(self, cache_attr): return getattr(self, cache_attr) @@ -2474,7 +2580,6 @@ class ManyToManyField(RelatedField): else: link_field_name = None for f in self.rel.through._meta.fields: - # NOTE f.rel.to != f.related_model if f.is_relation and f.rel.to == related.model: if link_field_name is None and related.related_model == related.model: # If this is an m2m-intermediate to self, @@ -2524,11 +2629,10 @@ class ManyToManyField(RelatedField): if not self.rel.through and not cls._meta.abstract and not cls._meta.swapped: self.rel.through = create_many_to_many_intermediary_model(self, cls) - # Add the descriptor for the m2m relation # Add the descriptor for the m2m relation. setattr(cls, self.name, ManyRelatedObjectsDescriptor(self.rel, reverse=False)) - # Set up the accessor for the m2m table name for the relation + # 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 @@ -2544,7 +2648,7 @@ class ManyToManyField(RelatedField): if not self.rel.is_hidden() and not related.related_model._meta.swapped: setattr(cls, related.get_accessor_name(), ManyRelatedObjectsDescriptor(self.rel, reverse=True)) - # Set up the accessors for the column names on the m2m table + # Set up the accessors for the column names on the m2m table. self.m2m_column_name = curry(self._get_m2m_attr, related, 'column') self.m2m_reverse_name = curry(self._get_m2m_reverse_attr, related, 'column') @@ -2560,7 +2664,9 @@ class ManyToManyField(RelatedField): pass def value_from_object(self, obj): - "Returns the value of this field in the given model instance." + """ + Return the value of this field in the given model instance. + """ return getattr(obj, self.attname).all() def save_form_data(self, instance, data):