diff --git a/django/contrib/contenttypes/fields.py b/django/contrib/contenttypes/fields.py index 1cd2fc0fa6..9d5c47da9a 100644 --- a/django/contrib/contenttypes/fields.py +++ b/django/contrib/contenttypes/fields.py @@ -9,7 +9,7 @@ from django.db import DEFAULT_DB_ALIAS, connection, models, router, transaction from django.db.models import DO_NOTHING, signals from django.db.models.base import ModelBase, make_foreign_order_accessors from django.db.models.fields.related import ( - ForeignObject, ForeignObjectRel, ForeignRelatedObjectsDescriptor, + ForeignObject, ForeignObjectRel, ReverseManyToOneDescriptor, lazy_related_operation, ) from django.db.models.query_utils import PathInfo @@ -24,8 +24,7 @@ class GenericForeignKey(object): ``object_id`` fields. This class also doubles as an accessor to the related object (similar to - ReverseSingleRelatedObjectDescriptor) by adding itself as a model - attribute. + ForwardManyToOneDescriptor) by adding itself as a model attribute. """ # Field flags @@ -381,7 +380,7 @@ class GenericRelation(ForeignObject): kwargs['virtual_only'] = True super(GenericRelation, self).contribute_to_class(cls, name, **kwargs) self.model = cls - setattr(cls, self.name, ReverseGenericRelatedObjectsDescriptor(self.remote_field)) + 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. @@ -430,7 +429,7 @@ class GenericRelation(ForeignObject): }) -class ReverseGenericRelatedObjectsDescriptor(ForeignRelatedObjectsDescriptor): +class ReverseGenericManyToOneDescriptor(ReverseManyToOneDescriptor): """ Accessor to the related objects manager on the one-to-many relation created by GenericRelation. @@ -440,7 +439,7 @@ class ReverseGenericRelatedObjectsDescriptor(ForeignRelatedObjectsDescriptor): class Post(Model): comments = GenericRelation(Comment) - ``post.comments`` is a ReverseGenericRelatedObjectsDescriptor instance. + ``post.comments`` is a ReverseGenericManyToOneDescriptor instance. """ @cached_property diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py index b5cfbb1541..6b563360f5 100644 --- a/django/db/models/fields/related.py +++ b/django/db/models/fields/related.py @@ -25,8 +25,8 @@ from . import ( PositiveSmallIntegerField, ) from .related_descriptors import ( - ForeignRelatedObjectsDescriptor, ManyRelatedObjectsDescriptor, - ReverseSingleRelatedObjectDescriptor, SingleRelatedObjectDescriptor, + ForwardManyToOneDescriptor, ManyToManyDescriptor, + ReverseManyToOneDescriptor, ReverseOneToOneDescriptor, ) from .related_lookups import ( RelatedExact, RelatedGreaterThan, RelatedGreaterThanOrEqual, RelatedIn, @@ -432,7 +432,7 @@ class ForeignObject(RelatedField): one_to_one = False requires_unique_target = True - related_accessor_class = ForeignRelatedObjectsDescriptor + related_accessor_class = ReverseManyToOneDescriptor rel_class = ForeignObjectRel def __init__(self, to, on_delete, from_fields, to_fields, rel=None, related_name=None, @@ -684,7 +684,7 @@ class ForeignObject(RelatedField): def contribute_to_class(self, cls, name, virtual_only=False): super(ForeignObject, self).contribute_to_class(cls, name, virtual_only=virtual_only) - setattr(cls, self.name, ReverseSingleRelatedObjectDescriptor(self)) + setattr(cls, self.name, ForwardManyToOneDescriptor(self)) def contribute_to_related_class(self, cls, related): # Internal FK's - i.e., those with a related name ending with '+' - @@ -974,7 +974,7 @@ class OneToOneField(ForeignKey): one_to_many = False one_to_one = True - related_accessor_class = SingleRelatedObjectDescriptor + related_accessor_class = ReverseOneToOneDescriptor rel_class = OneToOneRel description = _("One-to-one relationship") @@ -1560,7 +1560,7 @@ class ManyToManyField(RelatedField): self.remote_field.through = create_many_to_many_intermediary_model(self, cls) # Add the descriptor for the m2m relation. - setattr(cls, self.name, ManyRelatedObjectsDescriptor(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 = curry(self._get_m2m_db_table, cls._meta) @@ -1569,7 +1569,7 @@ class ManyToManyField(RelatedField): # Internal M2Ms (i.e., those with a related name ending with '+') # and swapped models don't get a related descriptor. if not self.remote_field.is_hidden() and not related.related_model._meta.swapped: - setattr(cls, related.get_accessor_name(), ManyRelatedObjectsDescriptor(self.remote_field, reverse=True)) + setattr(cls, related.get_accessor_name(), ManyToManyDescriptor(self.remote_field, reverse=True)) # Set up the accessors for the column names on the m2m table. self.m2m_column_name = curry(self._get_m2m_attr, related, 'column') diff --git a/django/db/models/fields/related_descriptors.py b/django/db/models/fields/related_descriptors.py index 926704ad7d..2398b166bb 100644 --- a/django/db/models/fields/related_descriptors.py +++ b/django/db/models/fields/related_descriptors.py @@ -22,37 +22,42 @@ reverse many-to-one relation. There are three types of relations (many-to-one, one-to-one, and many-to-many) and two directions (forward and reverse) for a total of six combinations. -However only four accessor classes are required. 1. Related instance on the forward side of a many-to-one or one-to-one - relation: ``ReverseSingleRelatedObjectDescriptor``. + relation: ``ForwardManyToOneDescriptor``. Uniqueness of foreign key values is irrelevant to accessing the related instance, making the many-to-one and one-to-one cases identical as far as the descriptor is concerned. The constraint is checked upstream (unicity validation in forms) or downstream (unique indexes in the database). + If you're looking for ``ForwardOneToOneDescriptor``, use + ``ForwardManyToOneDescriptor`` instead. + 2. Related instance on the reverse side of a one-to-one relation: - ``SingleRelatedObjectDescriptor``. + ``ReverseOneToOneDescriptor``. One-to-one relations are asymmetrical, despite the apparent symmetry of the name, because they're implemented in the database with a foreign key from - one table to another. As a consequence ``SingleRelatedObjectDescriptor`` is - slightly different from ``ReverseSingleRelatedObjectDescriptor``. + one table to another. As a consequence ``ReverseOneToOneDescriptor`` is + slightly different from ``ForwardManyToOneDescriptor``. 3. Related objects manager for related instances on the reverse side of a - many-to-one relation: ``ForeignRelatedObjectsDescriptor``. + many-to-one relation: ``ReverseManyToOneDescriptor``. Unlike the previous two classes, this one provides access to a collection of objects. It returns a manager rather than an instance. 4. Related objects manager for related instances on the forward or reverse - sides of a many-to-many relation: ``ManyRelatedObjectsDescriptor``. + sides of a many-to-many relation: ``ManyToManyDescriptor``. Many-to-many relations are symmetrical. The syntax of Django models requires declaring them on one side but that's an implementation detail. They could be declared on the other side without any change in behavior. Therefore the forward and reverse descriptors can be the same. + + If you're looking for ``ForwardManyToManyDescriptor`` or + ``ReverseManyToManyDescriptor``, use ``ManyToManyDescriptor`` instead. """ from __future__ import unicode_literals @@ -65,7 +70,7 @@ from django.db.models.query import QuerySet from django.utils.functional import cached_property -class ReverseSingleRelatedObjectDescriptor(object): +class ForwardManyToOneDescriptor(object): """ Accessor to the related object on the forward side of a many-to-one or one-to-one relation. @@ -75,7 +80,7 @@ class ReverseSingleRelatedObjectDescriptor(object): class Child(Model): parent = ForeignKey(Parent, related_name='children') - ``child.parent`` is a ``ReverseSingleRelatedObjectDescriptor`` instance. + ``child.parent`` is a ``ForwardManyToOneDescriptor`` instance. """ def __init__(self, field_with_rel): @@ -150,7 +155,7 @@ class ReverseSingleRelatedObjectDescriptor(object): # The related instance is loaded from the database and then cached in # the attribute defined in self.cache_name. It can also be pre-cached - # by the reverse accessor (SingleRelatedObjectDescriptor). + # by the reverse accessor (ReverseOneToOneDescriptor). try: rel_obj = getattr(instance, self.cache_name) except AttributeError: @@ -249,7 +254,7 @@ class ReverseSingleRelatedObjectDescriptor(object): setattr(value, self.field.remote_field.get_cache_name(), instance) -class SingleRelatedObjectDescriptor(object): +class ReverseOneToOneDescriptor(object): """ Accessor to the related object on the reverse side of a one-to-one relation. @@ -259,7 +264,7 @@ class SingleRelatedObjectDescriptor(object): class Restaurant(Model): place = OneToOneField(Place, related_name='restaurant') - ``place.restaurant`` is a ``SingleRelatedObjectDescriptor`` instance. + ``place.restaurant`` is a ``ReverseOneToOneDescriptor`` instance. """ def __init__(self, related): @@ -269,7 +274,7 @@ class SingleRelatedObjectDescriptor(object): @cached_property def RelatedObjectDoesNotExist(self): # The exception isn't created at initialization time for the sake of - # consistency with `ReverseSingleRelatedObjectDescriptor`. + # consistency with `ForwardManyToOneDescriptor`. return type( str('RelatedObjectDoesNotExist'), (self.related.related_model.DoesNotExist, AttributeError), @@ -323,7 +328,7 @@ class SingleRelatedObjectDescriptor(object): # The related instance is loaded from the database and then cached in # the attribute defined in self.cache_name. It can also be pre-cached - # by the forward accessor (ReverseSingleRelatedObjectDescriptor). + # by the forward accessor (ForwardManyToOneDescriptor). try: rel_obj = getattr(instance, self.cache_name) except AttributeError: @@ -366,7 +371,7 @@ class SingleRelatedObjectDescriptor(object): Keep in mind that ``Restaurant`` holds the foreign key to ``Place``. """ # The similarity of the code below to the code in - # ReverseSingleRelatedObjectDescriptor is annoying, but there's a bunch + # ForwardManyToOneDescriptor is annoying, but there's a bunch # of small differences that would make a common base class convoluted. # If null=True, we can assign null here, but otherwise the value needs @@ -410,7 +415,7 @@ class SingleRelatedObjectDescriptor(object): setattr(value, self.related.field.get_cache_name(), instance) -class ForeignRelatedObjectsDescriptor(object): +class ReverseManyToOneDescriptor(object): """ Accessor to the related objects manager on the reverse side of a many-to-one relation. @@ -420,10 +425,10 @@ class ForeignRelatedObjectsDescriptor(object): class Child(Model): parent = ForeignKey(Parent, related_name='children') - ``parent.children`` is a ``ForeignRelatedObjectsDescriptor`` instance. + ``parent.children`` is a ``ReverseManyToOneDescriptor`` instance. Most of the implementation is delegated to a dynamically defined manager - class built by ``create_many_related_manager()`` which is defined below. + class built by ``create_forward_many_to_many_manager()`` defined below. """ def __init__(self, rel): @@ -432,7 +437,7 @@ class ForeignRelatedObjectsDescriptor(object): @cached_property def related_manager_cls(self): - return create_foreign_related_manager( + return create_reverse_many_to_one_manager( self.rel.related_model._default_manager.__class__, self.rel, ) @@ -466,7 +471,7 @@ class ForeignRelatedObjectsDescriptor(object): manager.set(value) -def create_foreign_related_manager(superclass, rel): +def create_reverse_many_to_one_manager(superclass, rel): """ Create a manager for the reverse side of a many-to-one relation. @@ -488,7 +493,7 @@ def create_foreign_related_manager(superclass, rel): # We use **kwargs rather than a kwarg argument to enforce the # `manager='manager_name'` syntax. manager = getattr(self.model, kwargs.pop('manager')) - manager_class = create_foreign_related_manager(manager.__class__, rel) + manager_class = create_reverse_many_to_one_manager(manager.__class__, rel) return manager_class(self.instance) do_not_call_in_templates = True @@ -650,7 +655,7 @@ def create_foreign_related_manager(superclass, rel): return RelatedManager -class ManyRelatedObjectsDescriptor(ForeignRelatedObjectsDescriptor): +class ManyToManyDescriptor(ReverseManyToOneDescriptor): """ Accessor to the related objects manager on the forward and reverse sides of a many-to-many relation. @@ -660,15 +665,15 @@ class ManyRelatedObjectsDescriptor(ForeignRelatedObjectsDescriptor): class Pizza(Model): toppings = ManyToManyField(Topping, related_name='pizzas') - ``pizza.toppings`` and ``topping.pizzas`` are - ``ManyRelatedObjectsDescriptor`` instances. + ``pizza.toppings`` and ``topping.pizzas`` are ``ManyToManyDescriptor`` + instances. Most of the implementation is delegated to a dynamically defined manager - class built by ``create_many_related_manager()`` which is defined below. + class built by ``create_forward_many_to_many_manager()`` defined below. """ def __init__(self, rel, reverse=False): - super(ManyRelatedObjectsDescriptor, self).__init__(rel) + super(ManyToManyDescriptor, self).__init__(rel) self.reverse = reverse @@ -682,14 +687,14 @@ class ManyRelatedObjectsDescriptor(ForeignRelatedObjectsDescriptor): @cached_property def related_manager_cls(self): model = self.rel.related_model if self.reverse else self.rel.model - return create_many_related_manager( + return create_forward_many_to_many_manager( model._default_manager.__class__, self.rel, reverse=self.reverse, ) -def create_many_related_manager(superclass, rel, reverse): +def create_forward_many_to_many_manager(superclass, rel, reverse): """ Create a manager for the either side of a many-to-many relation. @@ -746,7 +751,7 @@ def create_many_related_manager(superclass, rel, reverse): # We use **kwargs rather than a kwarg argument to enforce the # `manager='manager_name'` syntax. manager = getattr(self.model, kwargs.pop('manager')) - manager_class = create_many_related_manager(manager.__class__, rel, reverse) + manager_class = create_forward_many_to_many_manager(manager.__class__, rel, reverse) return manager_class(instance=self.instance) do_not_call_in_templates = True diff --git a/tests/foreign_object/models/article.py b/tests/foreign_object/models/article.py index f9e6f1fca4..f0c2f3fbac 100644 --- a/tests/foreign_object/models/article.py +++ b/tests/foreign_object/models/article.py @@ -1,11 +1,10 @@ from django.db import models -from django.db.models.fields.related import \ - ReverseSingleRelatedObjectDescriptor +from django.db.models.fields.related import ForwardManyToOneDescriptor from django.utils.encoding import python_2_unicode_compatible from django.utils.translation import get_language -class ArticleTranslationDescriptor(ReverseSingleRelatedObjectDescriptor): +class ArticleTranslationDescriptor(ForwardManyToOneDescriptor): """ The set of articletranslation should not set any local fields. """ diff --git a/tests/foreign_object/models/empty_join.py b/tests/foreign_object/models/empty_join.py index 3bf121bf39..b8b05ea1c2 100644 --- a/tests/foreign_object/models/empty_join.py +++ b/tests/foreign_object/models/empty_join.py @@ -1,6 +1,6 @@ from django.db import models from django.db.models.fields.related import ( - ForeignObjectRel, ForeignRelatedObjectsDescriptor, + ForeignObjectRel, ReverseManyToOneDescriptor, ) from django.db.models.lookups import StartsWith from django.db.models.query_utils import PathInfo @@ -10,7 +10,7 @@ from django.utils.encoding import python_2_unicode_compatible class CustomForeignObjectRel(ForeignObjectRel): """ Define some extra Field methods so this Rel acts more like a Field, which - lets us use ForeignRelatedObjectsDescriptor in both directions. + lets us use ReverseManyToOneDescriptor in both directions. """ @property def foreign_related_fields(self): @@ -24,7 +24,7 @@ class StartsWithRelation(models.ForeignObject): """ A ForeignObject that uses StartsWith operator in its joins instead of the default equality operator. This is logically a many-to-many relation - and creates a ForeignRelatedObjectsDescriptor in both directions. + and creates a ReverseManyToOneDescriptor in both directions. """ auto_created = False @@ -42,7 +42,7 @@ class StartsWithRelation(models.ForeignObject): @property def field(self): """ - Makes ForeignRelatedObjectsDescriptor work in both directions. + Makes ReverseManyToOneDescriptor work in both directions. """ return self.remote_field @@ -66,7 +66,7 @@ class StartsWithRelation(models.ForeignObject): def contribute_to_class(self, cls, name, virtual_only=False): super(StartsWithRelation, self).contribute_to_class(cls, name, virtual_only) - setattr(cls, self.name, ForeignRelatedObjectsDescriptor(self)) + setattr(cls, self.name, ReverseManyToOneDescriptor(self)) class BrokenContainsRelation(StartsWithRelation): diff --git a/tests/generic_relations/tests.py b/tests/generic_relations/tests.py index e9bd748329..72d4d492c6 100644 --- a/tests/generic_relations/tests.py +++ b/tests/generic_relations/tests.py @@ -329,7 +329,7 @@ class GenericRelationsTests(TestCase): def test_assign_with_queryset(self): # Ensure that querysets used in reverse GFK assignments are pre-evaluated # so their value isn't affected by the clearing operation in - # ManyRelatedObjectsDescriptor.__set__. Refs #19816. + # ManyToManyDescriptor.__set__. Refs #19816. bacon = Vegetable.objects.create(name="Bacon", is_yucky=False) bacon.tags.create(tag="fatty") bacon.tags.create(tag="salty") diff --git a/tests/many_to_many/tests.py b/tests/many_to_many/tests.py index ffe5e879ca..be18dcedb0 100644 --- a/tests/many_to_many/tests.py +++ b/tests/many_to_many/tests.py @@ -412,7 +412,7 @@ class ManyToManyTests(TestCase): def test_forward_assign_with_queryset(self): # Ensure that querysets used in m2m assignments are pre-evaluated # so their value isn't affected by the clearing operation in - # ManyRelatedObjectsDescriptor.__set__. Refs #19816. + # ManyToManyDescriptor.__set__. Refs #19816. self.a1.publications = [self.p1, self.p2] qs = self.a1.publications.filter(title='The Python Journal') @@ -424,7 +424,7 @@ class ManyToManyTests(TestCase): def test_reverse_assign_with_queryset(self): # Ensure that querysets used in M2M assignments are pre-evaluated # so their value isn't affected by the clearing operation in - # ManyRelatedObjectsDescriptor.__set__. Refs #19816. + # ManyToManyDescriptor.__set__. Refs #19816. self.p1.article_set = [self.a1, self.a2] qs = self.p1.article_set.filter(headline='Django lets you build Web apps easily') diff --git a/tests/many_to_one_null/tests.py b/tests/many_to_one_null/tests.py index 06f756e35b..ae5e21e1cf 100644 --- a/tests/many_to_one_null/tests.py +++ b/tests/many_to_one_null/tests.py @@ -106,7 +106,7 @@ class ManyToOneNullTests(TestCase): def test_assign_with_queryset(self): # Ensure that querysets used in reverse FK assignments are pre-evaluated # so their value isn't affected by the clearing operation in - # ForeignRelatedObjectsDescriptor.__set__. Refs #19816. + # ReverseManyToOneDescriptor.__set__. Refs #19816. self.r2.article_set = [self.a2, self.a3] qs = self.r2.article_set.filter(headline="Second") diff --git a/tests/prefetch_related/tests.py b/tests/prefetch_related/tests.py index 0a864e93ae..7aed238bc0 100644 --- a/tests/prefetch_related/tests.py +++ b/tests/prefetch_related/tests.py @@ -595,7 +595,7 @@ class CustomPrefetchTests(TestCase): self.assertEqual(lst2[0].houses_lst[0].rooms_lst[0].main_room_of, self.house1) self.assertEqual(len(lst2[1].houses_lst), 0) - # Test ReverseSingleRelatedObjectDescriptor. + # Test ForwardManyToOneDescriptor. houses = House.objects.select_related('owner') with self.assertNumQueries(6): rooms = Room.objects.all().prefetch_related('house') @@ -624,7 +624,7 @@ class CustomPrefetchTests(TestCase): with self.assertNumQueries(3): getattr(rooms.first().house, 'address') - # Test SingleRelatedObjectDescriptor. + # Test ReverseOneToOneDescriptor. houses = House.objects.select_related('owner') with self.assertNumQueries(6): rooms = Room.objects.all().prefetch_related('main_room_of') diff --git a/tests/schema/fields.py b/tests/schema/fields.py index 8499477d65..30357b1061 100644 --- a/tests/schema/fields.py +++ b/tests/schema/fields.py @@ -1,6 +1,6 @@ from django.db import models from django.db.models.fields.related import ( - RECURSIVE_RELATIONSHIP_CONSTANT, ManyRelatedObjectsDescriptor, + RECURSIVE_RELATIONSHIP_CONSTANT, ManyToManyDescriptor, ManyToManyField, ManyToManyRel, RelatedField, create_many_to_many_intermediary_model, ) @@ -42,7 +42,7 @@ class CustomManyToManyField(RelatedField): super(CustomManyToManyField, self).contribute_to_class(cls, name, **kwargs) if not self.remote_field.through and not cls._meta.abstract and not cls._meta.swapped: self.remote_field.through = create_many_to_many_intermediary_model(self, cls) - setattr(cls, self.name, ManyRelatedObjectsDescriptor(self.remote_field)) + setattr(cls, self.name, ManyToManyDescriptor(self.remote_field)) self.m2m_db_table = curry(self._get_m2m_db_table, cls._meta) def get_internal_type(self):