Commit e542e81b authored by Aymeric Augustin's avatar Aymeric Augustin
Browse files

Renamed descriptor classes for related objects.

The old names were downright confusing. Some seemed to mean the opposite
of what the class actually did.

The new names follow a consistent nomenclature:

    (Forward|Reverse)(ManyToOne|OneToOne|ManyToMany)Descriptor.

I mentioned combinations that do not exist in the docstring in order to
help people who would search for them in the code base.
parent 2409a424
Loading
Loading
Loading
Loading
+5 −6
Original line number Diff line number Diff line
@@ -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
+7 −7
Original line number Diff line number Diff line
@@ -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')
+34 −29
Original line number Diff line number Diff line
@@ -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

+2 −3
Original line number Diff line number Diff line
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.
    """
+5 −5
Original line number Diff line number Diff line
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):
Loading