Reworked docstrings and comments in related.py.
Thanks Tim Graham for the review.
This commit is contained in:
parent
c5a77721e2
commit
5efd472178
|
@ -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):
|
||||||
|
|
|
@ -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):
|
||||||
|
|
Loading…
Reference in New Issue