mirror of
https://github.com/django/django.git
synced 2025-01-22 00:02:15 +00:00
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.
This commit is contained in:
parent
2409a4241a
commit
e542e81b39
@ -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 import DO_NOTHING, signals
|
||||||
from django.db.models.base import ModelBase, make_foreign_order_accessors
|
from django.db.models.base import ModelBase, make_foreign_order_accessors
|
||||||
from django.db.models.fields.related import (
|
from django.db.models.fields.related import (
|
||||||
ForeignObject, ForeignObjectRel, ForeignRelatedObjectsDescriptor,
|
ForeignObject, ForeignObjectRel, ReverseManyToOneDescriptor,
|
||||||
lazy_related_operation,
|
lazy_related_operation,
|
||||||
)
|
)
|
||||||
from django.db.models.query_utils import PathInfo
|
from django.db.models.query_utils import PathInfo
|
||||||
@ -24,8 +24,7 @@ class GenericForeignKey(object):
|
|||||||
``object_id`` fields.
|
``object_id`` fields.
|
||||||
|
|
||||||
This class also doubles as an accessor to the related object (similar to
|
This class also doubles as an accessor to the related object (similar to
|
||||||
ReverseSingleRelatedObjectDescriptor) by adding itself as a model
|
ForwardManyToOneDescriptor) by adding itself as a model attribute.
|
||||||
attribute.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Field flags
|
# Field flags
|
||||||
@ -381,7 +380,7 @@ class GenericRelation(ForeignObject):
|
|||||||
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)
|
||||||
self.model = cls
|
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
|
# 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.
|
# 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
|
Accessor to the related objects manager on the one-to-many relation created
|
||||||
by GenericRelation.
|
by GenericRelation.
|
||||||
@ -440,7 +439,7 @@ class ReverseGenericRelatedObjectsDescriptor(ForeignRelatedObjectsDescriptor):
|
|||||||
class Post(Model):
|
class Post(Model):
|
||||||
comments = GenericRelation(Comment)
|
comments = GenericRelation(Comment)
|
||||||
|
|
||||||
``post.comments`` is a ReverseGenericRelatedObjectsDescriptor instance.
|
``post.comments`` is a ReverseGenericManyToOneDescriptor instance.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
|
@ -25,8 +25,8 @@ from . import (
|
|||||||
PositiveSmallIntegerField,
|
PositiveSmallIntegerField,
|
||||||
)
|
)
|
||||||
from .related_descriptors import (
|
from .related_descriptors import (
|
||||||
ForeignRelatedObjectsDescriptor, ManyRelatedObjectsDescriptor,
|
ForwardManyToOneDescriptor, ManyToManyDescriptor,
|
||||||
ReverseSingleRelatedObjectDescriptor, SingleRelatedObjectDescriptor,
|
ReverseManyToOneDescriptor, ReverseOneToOneDescriptor,
|
||||||
)
|
)
|
||||||
from .related_lookups import (
|
from .related_lookups import (
|
||||||
RelatedExact, RelatedGreaterThan, RelatedGreaterThanOrEqual, RelatedIn,
|
RelatedExact, RelatedGreaterThan, RelatedGreaterThanOrEqual, RelatedIn,
|
||||||
@ -432,7 +432,7 @@ class ForeignObject(RelatedField):
|
|||||||
one_to_one = False
|
one_to_one = False
|
||||||
|
|
||||||
requires_unique_target = True
|
requires_unique_target = True
|
||||||
related_accessor_class = ForeignRelatedObjectsDescriptor
|
related_accessor_class = ReverseManyToOneDescriptor
|
||||||
rel_class = ForeignObjectRel
|
rel_class = ForeignObjectRel
|
||||||
|
|
||||||
def __init__(self, to, on_delete, from_fields, to_fields, rel=None, related_name=None,
|
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):
|
def contribute_to_class(self, cls, name, virtual_only=False):
|
||||||
super(ForeignObject, self).contribute_to_class(cls, name, virtual_only=virtual_only)
|
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):
|
def contribute_to_related_class(self, cls, related):
|
||||||
# Internal FK's - i.e., those with a related name ending with '+' -
|
# 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_many = False
|
||||||
one_to_one = True
|
one_to_one = True
|
||||||
|
|
||||||
related_accessor_class = SingleRelatedObjectDescriptor
|
related_accessor_class = ReverseOneToOneDescriptor
|
||||||
rel_class = OneToOneRel
|
rel_class = OneToOneRel
|
||||||
|
|
||||||
description = _("One-to-one relationship")
|
description = _("One-to-one relationship")
|
||||||
@ -1560,7 +1560,7 @@ class ManyToManyField(RelatedField):
|
|||||||
self.remote_field.through = create_many_to_many_intermediary_model(self, cls)
|
self.remote_field.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.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.
|
# 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)
|
||||||
@ -1569,7 +1569,7 @@ class ManyToManyField(RelatedField):
|
|||||||
# Internal M2Ms (i.e., those with a related name ending with '+')
|
# Internal M2Ms (i.e., those with a related name ending with '+')
|
||||||
# and swapped models don't get a related descriptor.
|
# and swapped models don't get a related descriptor.
|
||||||
if not self.remote_field.is_hidden() and not related.related_model._meta.swapped:
|
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.
|
# 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')
|
||||||
|
@ -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)
|
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.
|
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
|
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
|
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
|
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
|
the descriptor is concerned. The constraint is checked upstream (unicity
|
||||||
validation in forms) or downstream (unique indexes in the database).
|
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:
|
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
|
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
|
name, because they're implemented in the database with a foreign key from
|
||||||
one table to another. As a consequence ``SingleRelatedObjectDescriptor`` is
|
one table to another. As a consequence ``ReverseOneToOneDescriptor`` is
|
||||||
slightly different from ``ReverseSingleRelatedObjectDescriptor``.
|
slightly different from ``ForwardManyToOneDescriptor``.
|
||||||
|
|
||||||
3. Related objects manager for related instances on the reverse side of a
|
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
|
Unlike the previous two classes, this one provides access to a collection
|
||||||
of objects. It returns a manager rather than an instance.
|
of objects. It returns a manager rather than an instance.
|
||||||
|
|
||||||
4. Related objects manager for related instances on the forward or reverse
|
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
|
Many-to-many relations are symmetrical. The syntax of Django models
|
||||||
requires declaring them on one side but that's an implementation detail.
|
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.
|
They could be declared on the other side without any change in behavior.
|
||||||
Therefore the forward and reverse descriptors can be the same.
|
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
|
from __future__ import unicode_literals
|
||||||
@ -65,7 +70,7 @@ from django.db.models.query import QuerySet
|
|||||||
from django.utils.functional import cached_property
|
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
|
Accessor to the related object on the forward side of a many-to-one or
|
||||||
one-to-one relation.
|
one-to-one relation.
|
||||||
@ -75,7 +80,7 @@ class ReverseSingleRelatedObjectDescriptor(object):
|
|||||||
class Child(Model):
|
class Child(Model):
|
||||||
parent = ForeignKey(Parent, related_name='children')
|
parent = ForeignKey(Parent, related_name='children')
|
||||||
|
|
||||||
``child.parent`` is a ``ReverseSingleRelatedObjectDescriptor`` instance.
|
``child.parent`` is a ``ForwardManyToOneDescriptor`` instance.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, field_with_rel):
|
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 related instance is loaded from the database and then cached in
|
||||||
# the attribute defined in self.cache_name. It can also be pre-cached
|
# the attribute defined in self.cache_name. It can also be pre-cached
|
||||||
# by the reverse accessor (SingleRelatedObjectDescriptor).
|
# by the reverse accessor (ReverseOneToOneDescriptor).
|
||||||
try:
|
try:
|
||||||
rel_obj = getattr(instance, self.cache_name)
|
rel_obj = getattr(instance, self.cache_name)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
@ -249,7 +254,7 @@ class ReverseSingleRelatedObjectDescriptor(object):
|
|||||||
setattr(value, self.field.remote_field.get_cache_name(), instance)
|
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
|
Accessor to the related object on the reverse side of a one-to-one
|
||||||
relation.
|
relation.
|
||||||
@ -259,7 +264,7 @@ class SingleRelatedObjectDescriptor(object):
|
|||||||
class Restaurant(Model):
|
class Restaurant(Model):
|
||||||
place = OneToOneField(Place, related_name='restaurant')
|
place = OneToOneField(Place, related_name='restaurant')
|
||||||
|
|
||||||
``place.restaurant`` is a ``SingleRelatedObjectDescriptor`` instance.
|
``place.restaurant`` is a ``ReverseOneToOneDescriptor`` instance.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, related):
|
def __init__(self, related):
|
||||||
@ -269,7 +274,7 @@ class SingleRelatedObjectDescriptor(object):
|
|||||||
@cached_property
|
@cached_property
|
||||||
def RelatedObjectDoesNotExist(self):
|
def RelatedObjectDoesNotExist(self):
|
||||||
# The exception isn't created at initialization time for the sake of
|
# The exception isn't created at initialization time for the sake of
|
||||||
# consistency with `ReverseSingleRelatedObjectDescriptor`.
|
# consistency with `ForwardManyToOneDescriptor`.
|
||||||
return type(
|
return type(
|
||||||
str('RelatedObjectDoesNotExist'),
|
str('RelatedObjectDoesNotExist'),
|
||||||
(self.related.related_model.DoesNotExist, AttributeError),
|
(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 related instance is loaded from the database and then cached in
|
||||||
# the attribute defined in self.cache_name. It can also be pre-cached
|
# the attribute defined in self.cache_name. It can also be pre-cached
|
||||||
# by the forward accessor (ReverseSingleRelatedObjectDescriptor).
|
# by the forward accessor (ForwardManyToOneDescriptor).
|
||||||
try:
|
try:
|
||||||
rel_obj = getattr(instance, self.cache_name)
|
rel_obj = getattr(instance, self.cache_name)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
@ -366,7 +371,7 @@ class SingleRelatedObjectDescriptor(object):
|
|||||||
Keep in mind that ``Restaurant`` holds the foreign key to ``Place``.
|
Keep in mind that ``Restaurant`` holds the foreign key to ``Place``.
|
||||||
"""
|
"""
|
||||||
# The similarity of the code below to the code in
|
# 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.
|
# of small differences that would make a common base class convoluted.
|
||||||
|
|
||||||
# If null=True, we can assign null here, but otherwise the value needs
|
# 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)
|
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
|
Accessor to the related objects manager on the reverse side of a
|
||||||
many-to-one relation.
|
many-to-one relation.
|
||||||
@ -420,10 +425,10 @@ class ForeignRelatedObjectsDescriptor(object):
|
|||||||
class Child(Model):
|
class Child(Model):
|
||||||
parent = ForeignKey(Parent, related_name='children')
|
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
|
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):
|
def __init__(self, rel):
|
||||||
@ -432,7 +437,7 @@ class ForeignRelatedObjectsDescriptor(object):
|
|||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def related_manager_cls(self):
|
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.related_model._default_manager.__class__,
|
||||||
self.rel,
|
self.rel,
|
||||||
)
|
)
|
||||||
@ -466,7 +471,7 @@ class ForeignRelatedObjectsDescriptor(object):
|
|||||||
manager.set(value)
|
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.
|
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
|
# We use **kwargs rather than a kwarg argument to enforce the
|
||||||
# `manager='manager_name'` syntax.
|
# `manager='manager_name'` syntax.
|
||||||
manager = getattr(self.model, kwargs.pop('manager'))
|
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)
|
return manager_class(self.instance)
|
||||||
do_not_call_in_templates = True
|
do_not_call_in_templates = True
|
||||||
|
|
||||||
@ -650,7 +655,7 @@ def create_foreign_related_manager(superclass, rel):
|
|||||||
return RelatedManager
|
return RelatedManager
|
||||||
|
|
||||||
|
|
||||||
class ManyRelatedObjectsDescriptor(ForeignRelatedObjectsDescriptor):
|
class ManyToManyDescriptor(ReverseManyToOneDescriptor):
|
||||||
"""
|
"""
|
||||||
Accessor to the related objects manager on the forward and reverse sides of
|
Accessor to the related objects manager on the forward and reverse sides of
|
||||||
a many-to-many relation.
|
a many-to-many relation.
|
||||||
@ -660,15 +665,15 @@ class ManyRelatedObjectsDescriptor(ForeignRelatedObjectsDescriptor):
|
|||||||
class Pizza(Model):
|
class Pizza(Model):
|
||||||
toppings = ManyToManyField(Topping, related_name='pizzas')
|
toppings = ManyToManyField(Topping, related_name='pizzas')
|
||||||
|
|
||||||
``pizza.toppings`` and ``topping.pizzas`` are
|
``pizza.toppings`` and ``topping.pizzas`` are ``ManyToManyDescriptor``
|
||||||
``ManyRelatedObjectsDescriptor`` instances.
|
instances.
|
||||||
|
|
||||||
Most of the implementation is delegated to a dynamically defined manager
|
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):
|
def __init__(self, rel, reverse=False):
|
||||||
super(ManyRelatedObjectsDescriptor, self).__init__(rel)
|
super(ManyToManyDescriptor, self).__init__(rel)
|
||||||
|
|
||||||
self.reverse = reverse
|
self.reverse = reverse
|
||||||
|
|
||||||
@ -682,14 +687,14 @@ 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.model
|
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__,
|
model._default_manager.__class__,
|
||||||
self.rel,
|
self.rel,
|
||||||
reverse=self.reverse,
|
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.
|
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
|
# We use **kwargs rather than a kwarg argument to enforce the
|
||||||
# `manager='manager_name'` syntax.
|
# `manager='manager_name'` syntax.
|
||||||
manager = getattr(self.model, kwargs.pop('manager'))
|
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)
|
return manager_class(instance=self.instance)
|
||||||
do_not_call_in_templates = True
|
do_not_call_in_templates = True
|
||||||
|
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models.fields.related import \
|
from django.db.models.fields.related import ForwardManyToOneDescriptor
|
||||||
ReverseSingleRelatedObjectDescriptor
|
|
||||||
from django.utils.encoding import python_2_unicode_compatible
|
from django.utils.encoding import python_2_unicode_compatible
|
||||||
from django.utils.translation import get_language
|
from django.utils.translation import get_language
|
||||||
|
|
||||||
|
|
||||||
class ArticleTranslationDescriptor(ReverseSingleRelatedObjectDescriptor):
|
class ArticleTranslationDescriptor(ForwardManyToOneDescriptor):
|
||||||
"""
|
"""
|
||||||
The set of articletranslation should not set any local fields.
|
The set of articletranslation should not set any local fields.
|
||||||
"""
|
"""
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models.fields.related import (
|
from django.db.models.fields.related import (
|
||||||
ForeignObjectRel, ForeignRelatedObjectsDescriptor,
|
ForeignObjectRel, ReverseManyToOneDescriptor,
|
||||||
)
|
)
|
||||||
from django.db.models.lookups import StartsWith
|
from django.db.models.lookups import StartsWith
|
||||||
from django.db.models.query_utils import PathInfo
|
from django.db.models.query_utils import PathInfo
|
||||||
@ -10,7 +10,7 @@ from django.utils.encoding import python_2_unicode_compatible
|
|||||||
class CustomForeignObjectRel(ForeignObjectRel):
|
class CustomForeignObjectRel(ForeignObjectRel):
|
||||||
"""
|
"""
|
||||||
Define some extra Field methods so this Rel acts more like a Field, which
|
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
|
@property
|
||||||
def foreign_related_fields(self):
|
def foreign_related_fields(self):
|
||||||
@ -24,7 +24,7 @@ class StartsWithRelation(models.ForeignObject):
|
|||||||
"""
|
"""
|
||||||
A ForeignObject that uses StartsWith operator in its joins instead of
|
A ForeignObject that uses StartsWith operator in its joins instead of
|
||||||
the default equality operator. This is logically a many-to-many relation
|
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
|
auto_created = False
|
||||||
|
|
||||||
@ -42,7 +42,7 @@ class StartsWithRelation(models.ForeignObject):
|
|||||||
@property
|
@property
|
||||||
def field(self):
|
def field(self):
|
||||||
"""
|
"""
|
||||||
Makes ForeignRelatedObjectsDescriptor work in both directions.
|
Makes ReverseManyToOneDescriptor work in both directions.
|
||||||
"""
|
"""
|
||||||
return self.remote_field
|
return self.remote_field
|
||||||
|
|
||||||
@ -66,7 +66,7 @@ class StartsWithRelation(models.ForeignObject):
|
|||||||
|
|
||||||
def contribute_to_class(self, cls, name, virtual_only=False):
|
def contribute_to_class(self, cls, name, virtual_only=False):
|
||||||
super(StartsWithRelation, self).contribute_to_class(cls, name, virtual_only)
|
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):
|
class BrokenContainsRelation(StartsWithRelation):
|
||||||
|
@ -329,7 +329,7 @@ class GenericRelationsTests(TestCase):
|
|||||||
def test_assign_with_queryset(self):
|
def test_assign_with_queryset(self):
|
||||||
# Ensure that querysets used in reverse GFK assignments are pre-evaluated
|
# Ensure that querysets used in reverse GFK assignments are pre-evaluated
|
||||||
# so their value isn't affected by the clearing operation in
|
# 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 = Vegetable.objects.create(name="Bacon", is_yucky=False)
|
||||||
bacon.tags.create(tag="fatty")
|
bacon.tags.create(tag="fatty")
|
||||||
bacon.tags.create(tag="salty")
|
bacon.tags.create(tag="salty")
|
||||||
|
@ -412,7 +412,7 @@ class ManyToManyTests(TestCase):
|
|||||||
def test_forward_assign_with_queryset(self):
|
def test_forward_assign_with_queryset(self):
|
||||||
# Ensure that querysets used in m2m assignments are pre-evaluated
|
# Ensure that querysets used in m2m assignments are pre-evaluated
|
||||||
# so their value isn't affected by the clearing operation in
|
# 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]
|
self.a1.publications = [self.p1, self.p2]
|
||||||
|
|
||||||
qs = self.a1.publications.filter(title='The Python Journal')
|
qs = self.a1.publications.filter(title='The Python Journal')
|
||||||
@ -424,7 +424,7 @@ class ManyToManyTests(TestCase):
|
|||||||
def test_reverse_assign_with_queryset(self):
|
def test_reverse_assign_with_queryset(self):
|
||||||
# Ensure that querysets used in M2M assignments are pre-evaluated
|
# Ensure that querysets used in M2M assignments are pre-evaluated
|
||||||
# so their value isn't affected by the clearing operation in
|
# 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]
|
self.p1.article_set = [self.a1, self.a2]
|
||||||
|
|
||||||
qs = self.p1.article_set.filter(headline='Django lets you build Web apps easily')
|
qs = self.p1.article_set.filter(headline='Django lets you build Web apps easily')
|
||||||
|
@ -106,7 +106,7 @@ class ManyToOneNullTests(TestCase):
|
|||||||
def test_assign_with_queryset(self):
|
def test_assign_with_queryset(self):
|
||||||
# Ensure that querysets used in reverse FK assignments are pre-evaluated
|
# Ensure that querysets used in reverse FK assignments are pre-evaluated
|
||||||
# so their value isn't affected by the clearing operation in
|
# 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]
|
self.r2.article_set = [self.a2, self.a3]
|
||||||
|
|
||||||
qs = self.r2.article_set.filter(headline="Second")
|
qs = self.r2.article_set.filter(headline="Second")
|
||||||
|
@ -595,7 +595,7 @@ class CustomPrefetchTests(TestCase):
|
|||||||
self.assertEqual(lst2[0].houses_lst[0].rooms_lst[0].main_room_of, self.house1)
|
self.assertEqual(lst2[0].houses_lst[0].rooms_lst[0].main_room_of, self.house1)
|
||||||
self.assertEqual(len(lst2[1].houses_lst), 0)
|
self.assertEqual(len(lst2[1].houses_lst), 0)
|
||||||
|
|
||||||
# Test ReverseSingleRelatedObjectDescriptor.
|
# Test ForwardManyToOneDescriptor.
|
||||||
houses = House.objects.select_related('owner')
|
houses = House.objects.select_related('owner')
|
||||||
with self.assertNumQueries(6):
|
with self.assertNumQueries(6):
|
||||||
rooms = Room.objects.all().prefetch_related('house')
|
rooms = Room.objects.all().prefetch_related('house')
|
||||||
@ -624,7 +624,7 @@ class CustomPrefetchTests(TestCase):
|
|||||||
with self.assertNumQueries(3):
|
with self.assertNumQueries(3):
|
||||||
getattr(rooms.first().house, 'address')
|
getattr(rooms.first().house, 'address')
|
||||||
|
|
||||||
# Test SingleRelatedObjectDescriptor.
|
# Test ReverseOneToOneDescriptor.
|
||||||
houses = House.objects.select_related('owner')
|
houses = House.objects.select_related('owner')
|
||||||
with self.assertNumQueries(6):
|
with self.assertNumQueries(6):
|
||||||
rooms = Room.objects.all().prefetch_related('main_room_of')
|
rooms = Room.objects.all().prefetch_related('main_room_of')
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models.fields.related import (
|
from django.db.models.fields.related import (
|
||||||
RECURSIVE_RELATIONSHIP_CONSTANT, ManyRelatedObjectsDescriptor,
|
RECURSIVE_RELATIONSHIP_CONSTANT, ManyToManyDescriptor,
|
||||||
ManyToManyField, ManyToManyRel, RelatedField,
|
ManyToManyField, ManyToManyRel, RelatedField,
|
||||||
create_many_to_many_intermediary_model,
|
create_many_to_many_intermediary_model,
|
||||||
)
|
)
|
||||||
@ -42,7 +42,7 @@ class CustomManyToManyField(RelatedField):
|
|||||||
super(CustomManyToManyField, self).contribute_to_class(cls, name, **kwargs)
|
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:
|
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)
|
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)
|
self.m2m_db_table = curry(self._get_m2m_db_table, cls._meta)
|
||||||
|
|
||||||
def get_internal_type(self):
|
def get_internal_type(self):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user