summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--django/contrib/contenttypes/fields.py56
-rw-r--r--django/db/models/fields/related.py228
2 files changed, 199 insertions, 85 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,16 +405,16 @@ 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):
@@ -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):