Reworked docstrings and comments in related.py.

Thanks Tim Graham for the review.
This commit is contained in:
Loic Bistuer 2015-02-16 16:06:42 +07:00
parent c5a77721e2
commit 5efd472178
2 changed files with 201 additions and 87 deletions

View File

@ -19,9 +19,14 @@ from django.utils.functional import cached_property
@python_2_unicode_compatible @python_2_unicode_compatible
class GenericForeignKey(object): class GenericForeignKey(object):
""" """
Provides a generic relation to any object through content-type/object-id Provide a generic many-to-one relation through the ``content_type`` and
fields. ``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 # Field flags
auto_created = False auto_created = False
concrete = False concrete = False
@ -96,9 +101,10 @@ class GenericForeignKey(object):
return [] return []
def _check_content_type_field(self): 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: try:
field = self.model._meta.get_field(self.ct_field) field = self.model._meta.get_field(self.ct_field)
except FieldDoesNotExist: except FieldDoesNotExist:
@ -146,8 +152,8 @@ class GenericForeignKey(object):
def instance_pre_init(self, signal, sender, args, kwargs, **_kwargs): def instance_pre_init(self, signal, sender, args, kwargs, **_kwargs):
""" """
Handles initializing an object with the generic FK instead of Handle initializing an object with the generic FK instead of
content-type/object-id fields. content_type and object_id fields.
""" """
if self.name in kwargs: if self.name in kwargs:
value = kwargs.pop(self.name) value = kwargs.pop(self.name)
@ -256,6 +262,10 @@ class GenericForeignKey(object):
class GenericRel(ForeignObjectRel): 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): def __init__(self, field, to, related_name=None, related_query_name=None, limit_choices_to=None):
super(GenericRel, self).__init__( super(GenericRel, self).__init__(
field, to, field, to,
@ -267,7 +277,10 @@ class GenericRel(ForeignObjectRel):
class GenericRelation(ForeignObject): 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 # Field flags
auto_created = False auto_created = False
@ -310,9 +323,6 @@ class GenericRelation(ForeignObject):
def _check_generic_foreign_key_existence(self): def _check_generic_foreign_key_existence(self):
target = self.rel.to target = self.rel.to
if isinstance(target, ModelBase): 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 fields = target._meta.virtual_fields
if any(isinstance(field, GenericForeignKey) and if any(isinstance(field, GenericForeignKey) and
field.ct_field == self.content_type_field_name 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): def contribute_to_class(self, cls, name, **kwargs):
kwargs['virtual_only'] = True kwargs['virtual_only'] = True
super(GenericRelation, self).contribute_to_class(cls, name, **kwargs) 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 self.model = cls
# Add the descriptor for the relation
setattr(cls, self.name, ReverseGenericRelatedObjectsDescriptor(self.rel)) setattr(cls, self.name, ReverseGenericRelatedObjectsDescriptor(self.rel))
def set_attributes_from_rel(self): def set_attributes_from_rel(self):
@ -371,7 +379,7 @@ class GenericRelation(ForeignObject):
def get_content_type(self): 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, return ContentType.objects.get_for_model(self.model,
for_concrete_model=self.for_concrete_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): def bulk_related_objects(self, objs, using=DEFAULT_DB_ALIAS):
""" """
Return all objects related to ``objs`` via this ``GenericRelation``. Return all objects related to ``objs`` via this ``GenericRelation``.
""" """
return self.rel.to._base_manager.db_manager(using).filter(**{ 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( "%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): class ReverseGenericRelatedObjectsDescriptor(ForeignRelatedObjectsDescriptor):
""" """
This class provides the functionality that makes the related-object Accessor to the related objects manager on the one-to-many relation created
managers available as attributes on a model class, for fields that have by GenericRelation.
multiple "remote" values and have a GenericRelation defined in their model
(rather than having another model pointed *at* them). In the example In the example::
"article.publications", the publications attribute is a
ReverseGenericRelatedObjectsDescriptor instance. class Post(Model):
comments = GenericRelation(Comment)
``post.comments`` is a ReverseGenericRelatedObjectsDescriptor instance.
""" """
@cached_property @cached_property
def related_manager_cls(self): def related_manager_cls(self):
return create_generic_related_manager( return create_generic_related_manager(
@ -419,8 +426,9 @@ class ReverseGenericRelatedObjectsDescriptor(ForeignRelatedObjectsDescriptor):
def create_generic_related_manager(superclass, rel): def create_generic_related_manager(superclass, rel):
""" """
Factory function for a manager that subclasses 'superclass' (which is a Factory function to create a manager that subclasses another manager
Manager) and adds behavior for generic related objects. (generally the default manager of a given model) and adds behaviors
specific to generic relations.
""" """
class GenericRelatedObjectManager(superclass): class GenericRelatedObjectManager(superclass):

View File

@ -50,7 +50,8 @@ def add_lazy_relation(cls, field, relation, operation):
lazy relationships -- then the relation won't be set up until the lazy relationships -- then the relation won't be set up until the
class_prepared signal fires at the end of model initialization. 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 # Check for recursive relations
if relation == RECURSIVE_RELATIONSHIP_CONSTANT: if relation == RECURSIVE_RELATIONSHIP_CONSTANT:
@ -58,17 +59,17 @@ def add_lazy_relation(cls, field, relation, operation):
model_name = cls.__name__ model_name = cls.__name__
else: else:
# Look for an "app.Model" relation # Look for an "app.Model" relation.
if isinstance(relation, six.string_types): if isinstance(relation, six.string_types):
try: try:
app_label, model_name = relation.split(".") app_label, model_name = relation.split(".")
except ValueError: 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 app_label = cls._meta.app_label
model_name = relation model_name = relation
else: else:
# it's actually a model class # It's actually a model class.
app_label = relation._meta.app_label app_label = relation._meta.app_label
model_name = relation._meta.object_name model_name = relation._meta.object_name
@ -88,7 +89,7 @@ def add_lazy_relation(cls, field, relation, operation):
def do_pending_lookups(sender, **kwargs): 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__) key = (sender._meta.app_label, sender.__name__)
for cls, field, operation in sender._meta.apps._pending_lookups.pop(key, []): 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): class RelatedField(Field):
"""
Base class that all relational fields inherit from.
"""
# Field flags # Field flags
one_to_many = False one_to_many = False
one_to_one = False one_to_one = False
@ -174,8 +179,9 @@ class RelatedField(Field):
return [] return []
def _check_clashes(self): 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 from django.db.models.base import ModelBase
errors = [] errors = []
@ -278,14 +284,13 @@ class RelatedField(Field):
return errors return errors
def db_type(self, connection): def db_type(self, connection):
'''By default related field will not have a column # By default related field will not have a column as it relates to
as it relates columns to another table''' # columns from another table.
return None return None
def contribute_to_class(self, cls, name, virtual_only=False): def contribute_to_class(self, cls, name, virtual_only=False):
sup = super(RelatedField, self) sup = super(RelatedField, self)
# Store the opts for related_query_name()
self.opts = cls._meta self.opts = cls._meta
if hasattr(sup, 'contribute_to_class'): if hasattr(sup, 'contribute_to_class'):
@ -310,7 +315,7 @@ class RelatedField(Field):
@property @property
def swappable_setting(self): 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 it's not swapped in / marked with swappable=False.
""" """
if self.swappable: if self.swappable:
@ -350,7 +355,8 @@ class RelatedField(Field):
self.contribute_to_related_class(other, self.rel) self.contribute_to_related_class(other, self.rel)
def get_limit_choices_to(self): 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 If it is a callable, it will be invoked and the result will be
returned. returned.
@ -360,7 +366,8 @@ class RelatedField(Field):
return self.rel.limit_choices_to return self.rel.limit_choices_to
def formfield(self, **kwargs): 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. 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 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) return super(RelatedField, self).formfield(**defaults)
def related_query_name(self): 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 Define the name that can be used to identify this related object in a
# object_name by default, but this can be overridden with the table-spanning query.
# "related_name" option. """
return self.rel.related_query_name or self.rel.related_name or self.opts.model_name return self.rel.related_query_name or self.rel.related_name or self.opts.model_name
class SingleRelatedObjectDescriptor(object): 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 Accessor to the related object on the reverse side of a one-to-one
# a single "remote" value, on the class pointed to by a related field. relation.
# In the example "place.restaurant", the restaurant attribute is a
# SingleRelatedObjectDescriptor instance. In the example::
class Restaurant(Model):
place = OneToOneField(Place, related_name='restaurant')
``place.restaurant`` is a ``SingleRelatedObjectDescriptor`` instance.
"""
def __init__(self, related): def __init__(self, related):
self.related = related self.related = related
self.cache_name = related.get_cache_name() self.cache_name = related.get_cache_name()
@ -517,11 +531,18 @@ class SingleRelatedObjectDescriptor(object):
class ReverseSingleRelatedObjectDescriptor(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 Accessor to the related object on the forward side of a many-to-one or
# a single "remote" value, on the class that defines the related field. one-to-one relation.
# In the example "choice.poll", the poll attribute is a
# ReverseSingleRelatedObjectDescriptor instance. In the example::
class Choice(Model):
poll = ForeignKey(Place, related_name='choices')
`choice.poll` is a ReverseSingleRelatedObjectDescriptor instance.
"""
def __init__(self, field_with_rel): def __init__(self, field_with_rel):
self.field = field_with_rel self.field = field_with_rel
self.cache_name = self.field.get_cache_name() self.cache_name = self.field.get_cache_name()
@ -679,6 +700,12 @@ class ReverseSingleRelatedObjectDescriptor(object):
def create_foreign_related_manager(superclass, rel): 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): class RelatedManager(superclass):
def __init__(self, instance): def __init__(self, instance):
super(RelatedManager, self).__init__() super(RelatedManager, self).__init__()
@ -833,11 +860,18 @@ def create_foreign_related_manager(superclass, rel):
class ForeignRelatedObjectsDescriptor(object): 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 Accessor to the related objects manager on the reverse side of a
# multiple "remote" values and have a ForeignKey pointed at them by many-to-one relation.
# some other model. In the example "poll.choice_set", the choice_set
# attribute is a ForeignRelatedObjectsDescriptor instance. In the example::
class Choice(Model):
poll = ForeignKey(Place, related_name='choices')
``poll.choices`` is a ``ForeignRelatedObjectsDescriptor`` instance.
"""
def __init__(self, rel): def __init__(self, rel):
self.rel = rel self.rel = rel
self.field = rel.field self.field = rel.field
@ -861,9 +895,12 @@ class ForeignRelatedObjectsDescriptor(object):
def create_many_related_manager(superclass, rel, reverse): 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): class ManyRelatedManager(superclass):
def __init__(self, instance=None): def __init__(self, instance=None):
super(ManyRelatedManager, self).__init__() super(ManyRelatedManager, self).__init__()
@ -1198,9 +1235,18 @@ def create_many_related_manager(superclass, rel, reverse):
class ManyRelatedObjectsDescriptor(ForeignRelatedObjectsDescriptor): 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): def __init__(self, rel, reverse=False):
super(ManyRelatedObjectsDescriptor, self).__init__(rel) super(ManyRelatedObjectsDescriptor, self).__init__(rel)
@ -1217,8 +1263,6 @@ class ManyRelatedObjectsDescriptor(ForeignRelatedObjectsDescriptor):
@cached_property @cached_property
def related_manager_cls(self): def related_manager_cls(self):
model = self.rel.related_model if self.reverse else self.rel.to 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( return create_many_related_manager(
model._default_manager.__class__, model._default_manager.__class__,
self.rel, self.rel,
@ -1227,7 +1271,12 @@ class ManyRelatedObjectsDescriptor(ForeignRelatedObjectsDescriptor):
class ForeignObjectRel(object): 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 # Field flags
auto_created = True auto_created = True
@ -1301,7 +1350,7 @@ class ForeignObjectRel(object):
def get_choices(self, include_blank=True, blank_choice=BLANK_CHOICE_DASH, def get_choices(self, include_blank=True, blank_choice=BLANK_CHOICE_DASH,
limit_to_currently_related=False): 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. SelectField choices for this field.
Analog of django.db.models.fields.Field.get_choices(), provided Analog of django.db.models.fields.Field.get_choices(), provided
@ -1370,6 +1419,20 @@ class ForeignObjectRel(object):
class ManyToOneRel(ForeignObjectRel): 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, def __init__(self, field, to, field_name, related_name=None, related_query_name=None,
limit_choices_to=None, parent_link=False, on_delete=None): limit_choices_to=None, parent_link=False, on_delete=None):
super(ManyToOneRel, self).__init__( super(ManyToOneRel, self).__init__(
@ -1385,8 +1448,7 @@ class ManyToOneRel(ForeignObjectRel):
def get_related_field(self): def get_related_field(self):
""" """
Returns the Field in the 'to' object to which this relationship is Return the Field in the 'to' object to which this relationship is tied.
tied.
""" """
field = self.to._meta.get_field(self.field_name) field = self.to._meta.get_field(self.field_name)
if not field.concrete: if not field.concrete:
@ -1399,6 +1461,13 @@ class ManyToOneRel(ForeignObjectRel):
class OneToOneRel(ManyToOneRel): 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, def __init__(self, field, to, field_name, related_name=None, related_query_name=None,
limit_choices_to=None, parent_link=False, on_delete=None): limit_choices_to=None, parent_link=False, on_delete=None):
super(OneToOneRel, self).__init__( super(OneToOneRel, self).__init__(
@ -1414,6 +1483,13 @@ class OneToOneRel(ManyToOneRel):
class ManyToManyRel(ForeignObjectRel): 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, def __init__(self, field, to, related_name=None, related_query_name=None,
limit_choices_to=None, symmetrical=True, through=None, through_fields=None, limit_choices_to=None, symmetrical=True, through=None, through_fields=None,
db_constraint=True): db_constraint=True):
@ -1437,7 +1513,7 @@ class ManyToManyRel(ForeignObjectRel):
def get_related_field(self): 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. Provided for symmetry with ManyToOneRel.
""" """
opts = self.through._meta opts = self.through._meta
@ -1452,6 +1528,10 @@ class ManyToManyRel(ForeignObjectRel):
class ForeignObject(RelatedField): class ForeignObject(RelatedField):
"""
Abstraction of the ForeignKey relation, supports multi-column relations.
"""
# Field flags # Field flags
many_to_many = False many_to_many = False
many_to_one = True many_to_one = True
@ -1491,7 +1571,6 @@ class ForeignObject(RelatedField):
if rel_is_string or not self.requires_unique_target: if rel_is_string or not self.requires_unique_target:
return [] return []
# Skip if the
try: try:
self.foreign_related_fields self.foreign_related_fields
except FieldDoesNotExist: except FieldDoesNotExist:
@ -1640,7 +1719,7 @@ class ForeignObject(RelatedField):
def get_extra_descriptor_filter(self, instance): 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 user does 'instance.fieldname', that is the extra filter is used in
the descriptor of the field. the descriptor of the field.
@ -1655,7 +1734,7 @@ class ForeignObject(RelatedField):
def get_extra_restriction(self, where_class, alias, related_alias): 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) condition is something that responds to as_sql(compiler, connection)
method. method.
@ -1765,6 +1844,14 @@ class ForeignObject(RelatedField):
class ForeignKey(ForeignObject): 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 # Field flags
many_to_many = False many_to_many = False
many_to_one = True many_to_one = True
@ -1981,10 +2068,11 @@ class ForeignKey(ForeignObject):
class OneToOneField(ForeignKey): class OneToOneField(ForeignKey):
""" """
A OneToOneField is essentially the same as a ForeignKey, with the exception A OneToOneField is essentially the same as a ForeignKey, with the exception
that always carries a "unique" constraint with it and the reverse relation that it always carries a "unique" constraint with it and the reverse
always returns the object pointed to (since there will only ever be one), relation always returns the object pointed to (since there will only ever
rather than returning a list. be one), rather than returning a list.
""" """
# Field flags # Field flags
many_to_many = False many_to_many = False
many_to_one = False many_to_one = False
@ -2018,7 +2106,7 @@ class OneToOneField(ForeignKey):
setattr(instance, self.attname, data) setattr(instance, self.attname, data)
def _check_unique(self, **kwargs): def _check_unique(self, **kwargs):
# override ForeignKey since check isn't applicable here # Override ForeignKey since check isn't applicable here.
return [] return []
@ -2078,6 +2166,15 @@ def create_many_to_many_intermediary_model(field, klass):
class ManyToManyField(RelatedField): 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 # Field flags
many_to_many = True many_to_many = True
many_to_one = False 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: if self.rel.through_fields is not None:
# Validate that we're given an iterable of at least two items # 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 if not (len(self.rel.through_fields) >= 2 and
self.rel.through_fields[0] and self.rel.through_fields[1]): self.rel.through_fields[0] and self.rel.through_fields[1]):
errors.append( errors.append(
@ -2317,7 +2414,7 @@ class ManyToManyField(RelatedField):
# Validate the given through fields -- they should be actual # Validate the given through fields -- they should be actual
# fields on the through model, and also be foreign keys to the # fields on the through model, and also be foreign keys to the
# expected models # expected models.
else: else:
assert from_model is not None, ( assert from_model is not None, (
"ManyToManyField with intermediate " "ManyToManyField with intermediate "
@ -2372,7 +2469,7 @@ class ManyToManyField(RelatedField):
def deconstruct(self): def deconstruct(self):
name, path, args, kwargs = super(ManyToManyField, self).deconstruct() name, path, args, kwargs = super(ManyToManyField, self).deconstruct()
# Handle the simpler arguments # Handle the simpler arguments.
if self.db_table is not None: if self.db_table is not None:
kwargs['db_table'] = self.db_table kwargs['db_table'] = self.db_table
if self.rel.db_constraint is not True: if self.rel.db_constraint is not True:
@ -2395,7 +2492,7 @@ class ManyToManyField(RelatedField):
# of a swap. # of a swap.
swappable_setting = self.swappable_setting swappable_setting = self.swappable_setting
if swappable_setting is not None: 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 hasattr(kwargs['to'], "setting_name"):
if kwargs['to'].setting_name != swappable_setting: if kwargs['to'].setting_name != swappable_setting:
raise ValueError( raise ValueError(
@ -2403,7 +2500,7 @@ class ManyToManyField(RelatedField):
"model that is swapped in place of more than one model " "model that is swapped in place of more than one model "
"(%s and %s)" % (kwargs['to'].setting_name, swappable_setting) "(%s and %s)" % (kwargs['to'].setting_name, swappable_setting)
) )
# Set it
from django.db.migrations.writer import SettingsReference from django.db.migrations.writer import SettingsReference
kwargs['to'] = SettingsReference( kwargs['to'] = SettingsReference(
kwargs['to'], kwargs['to'],
@ -2439,7 +2536,10 @@ class ManyToManyField(RelatedField):
return Field.get_choices(self, include_blank=False) return Field.get_choices(self, include_blank=False)
def _get_m2m_db_table(self, opts): 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: if self.rel.through is not None:
return self.rel.through._meta.db_table return self.rel.through._meta.db_table
elif self.db_table: elif self.db_table:
@ -2449,7 +2549,10 @@ class ManyToManyField(RelatedField):
connection.ops.max_name_length()) connection.ops.max_name_length())
def _get_m2m_attr(self, related, attr): 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 cache_attr = '_m2m_%s_cache' % attr
if hasattr(self, cache_attr): if hasattr(self, cache_attr):
return getattr(self, cache_attr) return getattr(self, cache_attr)
@ -2464,7 +2567,10 @@ class ManyToManyField(RelatedField):
return getattr(self, cache_attr) return getattr(self, cache_attr)
def _get_m2m_reverse_attr(self, related, 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 cache_attr = '_m2m_reverse_%s_cache' % attr
if hasattr(self, cache_attr): if hasattr(self, cache_attr):
return getattr(self, cache_attr) return getattr(self, cache_attr)
@ -2474,7 +2580,6 @@ class ManyToManyField(RelatedField):
else: else:
link_field_name = None link_field_name = None
for f in self.rel.through._meta.fields: 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 f.is_relation and f.rel.to == related.model:
if link_field_name is None and related.related_model == related.model: if link_field_name is None and related.related_model == related.model:
# If this is an m2m-intermediate to self, # 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: 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) 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. # Add the descriptor for the m2m relation.
setattr(cls, self.name, ManyRelatedObjectsDescriptor(self.rel, reverse=False)) 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) self.m2m_db_table = curry(self._get_m2m_db_table, cls._meta)
# Populate some necessary rel arguments so that cross-app relations # 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: if not self.rel.is_hidden() and not related.related_model._meta.swapped:
setattr(cls, related.get_accessor_name(), ManyRelatedObjectsDescriptor(self.rel, reverse=True)) 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_column_name = curry(self._get_m2m_attr, related, 'column')
self.m2m_reverse_name = curry(self._get_m2m_reverse_attr, related, 'column') self.m2m_reverse_name = curry(self._get_m2m_reverse_attr, related, 'column')
@ -2560,7 +2664,9 @@ class ManyToManyField(RelatedField):
pass pass
def value_from_object(self, obj): 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() return getattr(obj, self.attname).all()
def save_form_data(self, instance, data): def save_form_data(self, instance, data):