Fixed #21414 -- Removed RelatedObject and deprecated Field.related.

This commit is contained in:
Anssi Kääriäinen 2013-11-09 14:25:15 +02:00 committed by Tim Graham
parent 6e08bde8c4
commit f233bf47dd
17 changed files with 318 additions and 285 deletions

View File

@ -8,7 +8,7 @@ certain test -- e.g. being a DateField or ForeignKey.
import datetime
from django.db import models
from django.db.models.fields.related import ManyToManyField
from django.db.models.fields.related import ForeignObjectRel, ManyToManyField
from django.core.exceptions import ImproperlyConfigured, ValidationError
from django.utils.encoding import smart_text, force_text
from django.utils.translation import ugettext_lazy as _
@ -180,7 +180,7 @@ class RelatedFieldListFilter(FieldListFilter):
self.title = self.lookup_title
def has_output(self):
if (isinstance(self.field, models.related.RelatedObject) and
if (isinstance(self.field, ForeignObjectRel) and
self.field.field.null or hasattr(self.field, 'rel') and
self.field.null):
extra = 1
@ -210,7 +210,7 @@ class RelatedFieldListFilter(FieldListFilter):
}, [self.lookup_kwarg_isnull]),
'display': val,
}
if (isinstance(self.field, models.related.RelatedObject) and
if (isinstance(self.field, ForeignObjectRel) and
(self.field.field.null or isinstance(self.field.field, ManyToManyField)) or
hasattr(self.field, 'rel') and (self.field.null or isinstance(self.field, ManyToManyField))):
yield {
@ -223,7 +223,7 @@ class RelatedFieldListFilter(FieldListFilter):
FieldListFilter.register(lambda f: (
bool(f.rel) if hasattr(f, 'rel') else
isinstance(f, models.related.RelatedObject)), RelatedFieldListFilter)
isinstance(f, ForeignObjectRel)), RelatedFieldListFilter)
class BooleanFieldListFilter(FieldListFilter):

View File

@ -25,8 +25,8 @@ from django.core.paginator import Paginator
from django.core.urlresolvers import reverse
from django.db import models, transaction, router
from django.db.models.constants import LOOKUP_SEP
from django.db.models.related import RelatedObject
from django.db.models.fields import BLANK_CHOICE_DASH, FieldDoesNotExist
from django.db.models.fields.related import ForeignObjectRel
from django.db.models.sql.constants import QUERY_TERMS
from django.forms.formsets import all_valid, DELETION_FIELD_NAME
from django.forms.models import (modelform_factory, modelformset_factory,
@ -421,7 +421,7 @@ class BaseModelAdmin(six.with_metaclass(forms.MediaDefiningClass)):
rel_name = field.rel.get_related_field().name
else:
rel_name = None
elif isinstance(field, RelatedObject):
elif isinstance(field, ForeignObjectRel):
model = field.model
rel_name = model._meta.pk.name
else:

View File

@ -304,7 +304,7 @@ def label_for_field(name, model, model_admin=None, return_attr=False):
try:
label = field.verbose_name
except AttributeError:
# field is likely a RelatedObject
# field is likely a ForeignObjectRel
label = field.opts.verbose_name
except models.FieldDoesNotExist:
if name == "__unicode__":

View File

@ -9,7 +9,7 @@ from django.db import models, router, transaction, DEFAULT_DB_ALIAS
from django.db.models import signals, FieldDoesNotExist, DO_NOTHING
from django.db.models.base import ModelBase
from django.db.models.fields.related import ForeignObject, ForeignObjectRel
from django.db.models.related import PathInfo
from django.db.models.query_utils import PathInfo
from django.db.models.expressions import Col
from django.contrib.contenttypes.models import ContentType
from django.utils.encoding import smart_text, python_2_unicode_compatible
@ -27,6 +27,7 @@ class GenericForeignKey(object):
self.fk_field = fk_field
self.for_concrete_model = for_concrete_model
self.editable = False
self.rel = None
def contribute_to_class(self, cls, name, **kwargs):
self.name = name

View File

@ -1,6 +1,7 @@
from __future__ import unicode_literals
from operator import attrgetter
import warnings
from django.apps import apps
from django.core import checks
@ -9,13 +10,15 @@ from django.db.backends import utils
from django.db.models import signals, Q
from django.db.models.deletion import SET_NULL, SET_DEFAULT, CASCADE
from django.db.models.fields import (AutoField, Field, IntegerField,
PositiveIntegerField, PositiveSmallIntegerField, FieldDoesNotExist)
PositiveIntegerField, PositiveSmallIntegerField, FieldDoesNotExist,
BLANK_CHOICE_DASH)
from django.db.models.lookups import IsNull
from django.db.models.related import RelatedObject, PathInfo
from django.db.models.query import QuerySet
from django.db.models.query_utils import PathInfo
from django.db.models.expressions import Col
from django.utils.encoding import force_text, smart_text
from django.utils import six
from django.utils.deprecation import RemovedInDjango20Warning
from django.utils.translation import ugettext_lazy as _
from django.utils.functional import curry, cached_property
from django.core import exceptions
@ -178,7 +181,7 @@ class RelatedField(Field):
return []
try:
self.related
self.rel
except AttributeError:
return []
@ -195,13 +198,13 @@ class RelatedField(Field):
rel_opts = self.rel.to._meta
# rel_opts.object_name == "Target"
rel_name = self.related.get_accessor_name() # i. e. "model_set"
rel_name = self.rel.get_accessor_name() # i. e. "model_set"
rel_query_name = self.related_query_name() # i. e. "model"
field_name = "%s.%s" % (opts.object_name,
self.name) # i. e. "Model.field"
# Check clashes between accessor or reverse query name of `field`
# and any other field name -- i. e. accessor for Model.foreign is
# and any other field name -- i.e. accessor for Model.foreign is
# model_set and it clashes with Target.model_set.
potential_clashes = rel_opts.fields + rel_opts.many_to_many
for clash_field in potential_clashes:
@ -324,11 +327,17 @@ class RelatedField(Field):
self.verbose_name = self.rel.to._meta.verbose_name
self.rel.set_field_name()
@property
def related(self):
warnings.warn(
"Usage of field.related has been deprecated. Use field.rel instead.",
RemovedInDjango20Warning, 2)
return self.rel
def do_related_class(self, other, cls):
self.set_attributes_from_rel()
self.related = RelatedObject(other, cls, self)
if not cls._meta.abstract:
self.contribute_to_related_class(other, self.related)
self.contribute_to_related_class(other, self.rel)
def get_limit_choices_to(self):
"""Returns 'limit_choices_to' for this model field.
@ -554,7 +563,7 @@ class ReverseSingleRelatedObjectDescriptor(object):
# Since we're going to assign directly in the cache,
# we must manage the reverse relation cache manually.
if not self.field.rel.multiple:
rel_obj_cache_name = self.field.related.get_cache_name()
rel_obj_cache_name = self.field.rel.get_cache_name()
for rel_obj in queryset:
instance = instances_dict[rel_obj_attr(rel_obj)]
setattr(rel_obj, rel_obj_cache_name, instance)
@ -583,7 +592,7 @@ class ReverseSingleRelatedObjectDescriptor(object):
# Assuming the database enforces foreign keys, this won't fail.
rel_obj = qs.get()
if not self.field.rel.multiple:
setattr(rel_obj, self.field.related.get_cache_name(), instance)
setattr(rel_obj, self.field.rel.get_cache_name(), instance)
setattr(instance, self.cache_name, rel_obj)
if rel_obj is None and not self.field.null:
raise self.RelatedObjectDoesNotExist(
@ -635,7 +644,7 @@ class ReverseSingleRelatedObjectDescriptor(object):
# cache. This cache also might not exist if the related object
# hasn't been accessed yet.
if related is not None:
setattr(related, self.field.related.get_cache_name(), None)
setattr(related, self.field.rel.get_cache_name(), None)
for lh_field, rh_field in self.field.related_fields:
setattr(instance, lh_field.attname, None)
@ -656,7 +665,7 @@ class ReverseSingleRelatedObjectDescriptor(object):
# object you just set.
setattr(instance, self.cache_name, value)
if value is not None and not self.field.rel.multiple:
setattr(value, self.field.related.get_cache_name(), instance)
setattr(value, self.field.rel.get_cache_name(), instance)
def create_foreign_related_manager(superclass, rel_field, rel_model):
@ -1248,13 +1257,6 @@ class ReverseManyRelatedObjectsDescriptor(object):
class ForeignObjectRel(object):
def __init__(self, field, to, related_name=None, limit_choices_to=None,
parent_link=False, on_delete=None, related_query_name=None):
try:
to._meta
except AttributeError: # to._meta doesn't exist, so it must be RECURSIVE_RELATIONSHIP_CONSTANT
assert isinstance(to, six.string_types), (
"'to' must be either a model, a model name or the string %r" % RECURSIVE_RELATIONSHIP_CONSTANT
)
self.field = field
self.to = to
self.related_name = related_name
@ -1263,6 +1265,56 @@ class ForeignObjectRel(object):
self.multiple = True
self.parent_link = parent_link
self.on_delete = on_delete
self.symmetrical = False
# This and the following cached_properties can't be initialized in
# __init__ as the field doesn't have its model yet. Calling these methods
# before field.contribute_to_class() has been called will result in
# AttributeError
@cached_property
def model(self):
if not self.field.model:
raise AttributeError(
"This property can't be accessed before self.field.contribute_to_class has been called.")
return self.field.model
@cached_property
def opts(self):
return self.model._meta
@cached_property
def to_opts(self):
return self.to._meta
@cached_property
def parent_model(self):
return self.to
@cached_property
def name(self):
return '%s.%s' % (self.opts.app_label, self.opts.model_name)
def get_choices(self, include_blank=True, blank_choice=BLANK_CHOICE_DASH,
limit_to_currently_related=False):
"""
Returns choices with a default blank choices included, for use as
SelectField choices for this field.
Analog of django.db.models.fields.Field.get_choices(), provided
initially for utilization by RelatedFieldListFilter.
"""
first_choice = blank_choice if include_blank else []
queryset = self.model._default_manager.all()
if limit_to_currently_related:
queryset = queryset.complex_filter(
{'%s__isnull' % self.parent_model._meta.model_name: False}
)
lst = [(x._get_pk_val(), smart_text(x)) for x in queryset]
return first_choice + lst
def get_db_prep_lookup(self, lookup_type, value, connection, prepared=False):
# Defer to the actual field definition for db prep
return self.field.get_db_prep_lookup(lookup_type, value, connection=connection, prepared=prepared)
def is_hidden(self):
"Should the related object be hidden?"
@ -1289,6 +1341,34 @@ class ForeignObjectRel(object):
return self.field.get_lookup_constraint(constraint_class, alias, targets, sources,
lookup_type, raw_value)
def get_accessor_name(self, model=None):
# This method encapsulates the logic that decides what name to give an
# accessor descriptor that retrieves related many-to-one or
# many-to-many objects. It uses the lower-cased object_name + "_set",
# but this can be overridden with the "related_name" option.
# Due to backwards compatibility ModelForms need to be able to provide
# an alternate model. See BaseInlineFormSet.get_default_prefix().
opts = model._meta if model else self.opts
model = model or self.model
if self.multiple:
# If this is a symmetrical m2m relation on self, there is no reverse accessor.
if self.symmetrical and model == self.to:
return None
if self.related_name:
return self.related_name
if opts.default_related_name:
return self.opts.default_related_name % {
'model_name': self.opts.model_name.lower(),
'app_label': self.opts.app_label.lower(),
}
return opts.model_name + ('_set' if self.multiple else '')
def get_cache_name(self):
return "_%s_cache" % self.get_accessor_name()
def get_path_info(self):
return self.field.get_reverse_path_info()
class ManyToOneRel(ForeignObjectRel):
def __init__(self, field, to, field_name, related_name=None, limit_choices_to=None,
@ -1322,30 +1402,23 @@ class OneToOneRel(ManyToOneRel):
self.multiple = False
class ManyToManyRel(object):
def __init__(self, to, related_name=None, limit_choices_to=None,
class ManyToManyRel(ForeignObjectRel):
def __init__(self, field, to, related_name=None, limit_choices_to=None,
symmetrical=True, through=None, through_fields=None,
db_constraint=True, related_query_name=None):
if through and not db_constraint:
raise ValueError("Can't supply a through model and db_constraint=False")
if through_fields and not through:
raise ValueError("Cannot specify through_fields without a through model")
self.to = to
self.related_name = related_name
self.related_query_name = related_query_name
if limit_choices_to is None:
limit_choices_to = {}
self.limit_choices_to = limit_choices_to
super(ManyToManyRel, self).__init__(
field, to, related_name=related_name,
limit_choices_to=limit_choices_to, related_query_name=related_query_name)
self.symmetrical = symmetrical
self.multiple = True
self.through = through
self.through_fields = through_fields
self.db_constraint = db_constraint
def is_hidden(self):
"Should the related object be hidden?"
return self.related_name and self.related_name[-1] == '+'
def get_related_field(self):
"""
Returns the field in the 'to' object to which this relationship is tied.
@ -1402,7 +1475,7 @@ class ForeignObject(RelatedField):
return []
try:
self.related
self.rel
except AttributeError:
return []
@ -1979,7 +2052,8 @@ class ManyToManyField(RelatedField):
to = str(to)
kwargs['verbose_name'] = kwargs.get('verbose_name', None)
kwargs['rel'] = ManyToManyRel(to,
kwargs['rel'] = ManyToManyRel(
self, to,
related_name=kwargs.pop('related_name', None),
related_query_name=kwargs.pop('related_query_name', None),
limit_choices_to=kwargs.pop('limit_choices_to', None),

View File

@ -408,7 +408,7 @@ class Options(object):
the Field instance for the given name, model is the model containing
this field (None for local fields), direct is True if the field exists
on this model, and m2m is True for many-to-many relations. When
'direct' is False, 'field_object' is the corresponding RelatedObject
'direct' is False, 'field_object' is the corresponding ForeignObjectRel
for this field (since the field doesn't have an instance associated
with it).
@ -456,7 +456,7 @@ class Options(object):
for f, model in self.get_fields_with_model():
cache[f.name] = cache[f.attname] = (f, model, True, False)
for f in self.virtual_fields:
if hasattr(f, 'related'):
if f.rel:
cache[f.name] = cache[f.attname] = (
f, None if f.model == self.model else f.model, True, False)
if apps.ready:
@ -508,10 +508,10 @@ class Options(object):
if (hasattr(f, 'rel') and f.rel and not isinstance(f.rel.to, six.string_types)
and f.generate_reverse_relation):
if self == f.rel.to._meta:
cache[f.related] = None
proxy_cache[f.related] = None
cache[f.rel] = None
proxy_cache[f.rel] = None
elif self.concrete_model == f.rel.to._meta.concrete_model:
proxy_cache[f.related] = None
proxy_cache[f.rel] = None
self._related_objects_cache = cache
self._related_objects_proxy_cache = proxy_cache
@ -552,7 +552,7 @@ class Options(object):
if (f.rel
and not isinstance(f.rel.to, six.string_types)
and self == f.rel.to._meta):
cache[f.related] = None
cache[f.rel] = None
if apps.ready:
self._related_many_to_many_cache = cache
return cache

View File

@ -1477,7 +1477,7 @@ def get_cached_row(row, index_start, using, klass_info, offset=0,
if f.unique and rel_obj is not None:
# If the field is unique, populate the
# reverse descriptor cache on the related object
setattr(rel_obj, f.related.get_cache_name(), obj)
setattr(rel_obj, f.rel.get_cache_name(), obj)
# Now do the same, but for reverse related objects.
# Only handle the restricted case - i.e., don't do a depth
@ -1497,7 +1497,7 @@ def get_cached_row(row, index_start, using, klass_info, offset=0,
rel_obj, index_end = cached_row
if obj is not None:
# populate the reverse descriptor cache
setattr(obj, f.related.get_cache_name(), rel_obj)
setattr(obj, f.rel.get_cache_name(), rel_obj)
if rel_obj is not None:
# If the related object exists, populate
# the descriptor cache.

View File

@ -7,6 +7,8 @@ circular import difficulties.
"""
from __future__ import unicode_literals
from collections import namedtuple
from django.apps import apps
from django.db.backends import utils
from django.db.models.constants import LOOKUP_SEP
@ -14,6 +16,12 @@ from django.utils import six
from django.utils import tree
# PathInfo is used when converting lookups (fk__somecol). The contents
# describe the relation in Model terms (model Options and Fields for both
# sides of the relation. The join_field is the field backing the relation.
PathInfo = namedtuple('PathInfo', 'from_opts to_opts target_fields join_field m2m direct')
class InvalidQuery(Exception):
"""
The query passed to raw isn't a safe query to use with raw.

View File

@ -1,75 +0,0 @@
from collections import namedtuple
from django.utils.encoding import smart_text
from django.db.models.fields import BLANK_CHOICE_DASH
# PathInfo is used when converting lookups (fk__somecol). The contents
# describe the relation in Model terms (model Options and Fields for both
# sides of the relation. The join_field is the field backing the relation.
PathInfo = namedtuple('PathInfo',
'from_opts to_opts target_fields join_field '
'm2m direct')
class RelatedObject(object):
def __init__(self, parent_model, model, field):
self.parent_model = parent_model
self.model = model
self.opts = model._meta
self.field = field
self.name = '%s:%s' % (self.opts.app_label, self.opts.model_name)
self.var_name = self.opts.model_name
def get_choices(self, include_blank=True, blank_choice=BLANK_CHOICE_DASH,
limit_to_currently_related=False):
"""Returns choices with a default blank choices included, for use
as SelectField choices for this field.
Analogue of django.db.models.fields.Field.get_choices, provided
initially for utilization by RelatedFieldListFilter.
"""
first_choice = blank_choice if include_blank else []
queryset = self.model._default_manager.all()
if limit_to_currently_related:
queryset = queryset.complex_filter(
{'%s__isnull' % self.parent_model._meta.model_name: False})
lst = [(x._get_pk_val(), smart_text(x)) for x in queryset]
return first_choice + lst
def get_db_prep_lookup(self, lookup_type, value, connection, prepared=False):
# Defer to the actual field definition for db prep
return self.field.get_db_prep_lookup(lookup_type, value,
connection=connection, prepared=prepared)
def editable_fields(self):
"Get the fields in this class that should be edited inline."
return [f for f in self.opts.fields + self.opts.many_to_many if f.editable and f != self.field]
def __repr__(self):
return "<RelatedObject: %s related to %s>" % (self.name, self.field.name)
def get_accessor_name(self):
# This method encapsulates the logic that decides what name to give an
# accessor descriptor that retrieves related many-to-one or
# many-to-many objects. It uses the lower-cased object_name + "_set",
# but this can be overridden with the "related_name" option.
if self.field.rel.multiple:
# If this is a symmetrical m2m relation on self, there is no reverse accessor.
if getattr(self.field.rel, 'symmetrical', False) and self.model == self.parent_model:
return None
if self.field.rel.related_name:
return self.field.rel.related_name
if self.opts.default_related_name:
return self.opts.default_related_name % {
'model_name': self.opts.model_name.lower(),
'app_label': self.opts.app_label.lower(),
}
return self.opts.model_name + '_set'
else:
return self.field.rel.related_name or (self.opts.model_name)
def get_cache_name(self):
return "_%s_cache" % self.get_accessor_name()
def get_path_info(self):
return self.field.get_reverse_path_info()

View File

@ -55,7 +55,7 @@ class Join(object):
# A list of 2-tuples to use in the ON clause of the JOIN.
# Each 2-tuple will create one join condition in the ON clause.
self.join_cols = join_field.get_joining_columns()
# Along which field (or RelatedObject in the reverse join case)
# Along which field (or ForeignObjectRel in the reverse join case)
self.join_field = join_field
# Is this join nullabled?
self.nullable = nullable

View File

@ -13,12 +13,11 @@ import warnings
from django.core.exceptions import FieldError
from django.db import connections, DEFAULT_DB_ALIAS
from django.db.models.aggregates import Count
from django.db.models.constants import LOOKUP_SEP
from django.db.models.expressions import Col, Ref
from django.db.models.fields import FieldDoesNotExist
from django.db.models.query_utils import Q, refs_aggregate
from django.db.models.related import PathInfo
from django.db.models.aggregates import Count
from django.db.models.query_utils import PathInfo, Q, refs_aggregate
from django.db.models.sql.constants import (QUERY_TERMS, ORDER_DIR, SINGLE,
ORDER_PATTERN, SelectInfo, INNER, LOUTER)
from django.db.models.sql.datastructures import (

View File

@ -883,8 +883,7 @@ class BaseInlineFormSet(BaseModelFormSet):
@classmethod
def get_default_prefix(cls):
from django.db.models.fields.related import RelatedObject
return RelatedObject(cls.fk.rel.to, cls.model, cls.fk).get_accessor_name().replace('+', '')
return cls.fk.rel.get_accessor_name(model=cls.model).replace('+', '')
def save_new(self, form, commit=True):
# Use commit=False so we can assign the parent key afterwards, then

View File

@ -99,6 +99,8 @@ details on these changes.
``'django.contrib.auth.middleware.SessionAuthenticationMiddleware'`` is in
``MIDDLEWARE_CLASSES``.
* Private attribute ``django.db.models.Field.related`` will be removed.
.. _deprecation-removed-in-1.9:
1.9

View File

@ -1226,6 +1226,16 @@ to your ``MIDDLEWARE_CLASSES`` sometime before then to opt-in. Please read the
``django.contrib.flatpages.sitemaps.FlatPageSitemap``. The old import location
is deprecated and will be removed in Django 1.9.
Model ``Field.related``
~~~~~~~~~~~~~~~~~~~~~~~
Private attribute ``django.db.models.Field.related`` is deprecated in favor
of ``Field.rel``. The latter is an instance of
``django.db.models.fields.related.ForeignObjectRel`` which replaces
``django.db.models.related.RelatedObject``. The ``django.db.models.related``
module has been removed and the ``Field.related`` attribute will be removed in
Django 2.0.
.. removed-features-1.8:
Features removed in 1.8

View File

@ -2,11 +2,13 @@ from __future__ import unicode_literals
from datetime import datetime, timedelta
import threading
import warnings
from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
from django.db import connections, DEFAULT_DB_ALIAS
from django.db import DatabaseError
from django.db.models.fields import Field
from django.db.models.fields.related import ForeignObjectRel
from django.db.models.manager import BaseManager
from django.db.models.query import QuerySet, EmptyQuerySet, ValuesListQuerySet, MAX_GET_RESULTS
from django.test import TestCase, TransactionTestCase, skipIfDBFeature, skipUnlessDBFeature
@ -770,3 +772,16 @@ class ModelRefreshTests(TestCase):
a = Article.objects.create(pub_date=self._truncate_ms(datetime.now()))
with self.assertNumQueries(0):
a.refresh_from_db(fields=[])
class TestRelatedObjectDeprecation(TestCase):
def test_field_related_deprecation(self):
field = SelfRef._meta.get_field_by_name('selfref')[0]
with warnings.catch_warnings(record=True) as warns:
warnings.simplefilter('always')
self.assertIsInstance(field.related, ForeignObjectRel)
self.assertEqual(len(warns), 1)
self.assertEqual(
str(warns.pop().message),
'Usage of field.related has been deprecated. Use field.rel instead.'
)

View File

@ -233,114 +233,114 @@ TEST_RESULTS = {
},
'get_all_related_objects_with_model_hidden': {
BasePerson: (
('model_meta:baseperson_friends_base', None),
('model_meta:baseperson_friends_base', None),
('model_meta:baseperson_m2m_base', None),
('model_meta:baseperson_following_base', None),
('model_meta:baseperson_following_base', None),
('model_meta:baseperson_m2m_abstract', None),
('model_meta:baseperson_friends_abstract', None),
('model_meta:baseperson_friends_abstract', None),
('model_meta:baseperson_following_abstract', None),
('model_meta:baseperson_following_abstract', None),
('model_meta:person', None),
('model_meta:relating_basepeople', None),
('model_meta:relating_basepeople_hidden', None),
('model_meta:relating', None),
('model_meta:relating', None),
('model_meta.baseperson_friends_base', None),
('model_meta.baseperson_friends_base', None),
('model_meta.baseperson_m2m_base', None),
('model_meta.baseperson_following_base', None),
('model_meta.baseperson_following_base', None),
('model_meta.baseperson_m2m_abstract', None),
('model_meta.baseperson_friends_abstract', None),
('model_meta.baseperson_friends_abstract', None),
('model_meta.baseperson_following_abstract', None),
('model_meta.baseperson_following_abstract', None),
('model_meta.person', None),
('model_meta.relating_basepeople', None),
('model_meta.relating_basepeople_hidden', None),
('model_meta.relating', None),
('model_meta.relating', None),
),
Person: (
('model_meta:baseperson_friends_base', BasePerson),
('model_meta:baseperson_friends_base', BasePerson),
('model_meta:baseperson_m2m_base', BasePerson),
('model_meta:baseperson_following_base', BasePerson),
('model_meta:baseperson_following_base', BasePerson),
('model_meta:baseperson_m2m_abstract', BasePerson),
('model_meta:baseperson_friends_abstract', BasePerson),
('model_meta:baseperson_friends_abstract', BasePerson),
('model_meta:baseperson_following_abstract', BasePerson),
('model_meta:baseperson_following_abstract', BasePerson),
('model_meta:relating_basepeople', BasePerson),
('model_meta:relating_basepeople_hidden', BasePerson),
('model_meta:relating', BasePerson),
('model_meta:relating', BasePerson),
('model_meta:person_m2m_inherited', None),
('model_meta:person_friends_inherited', None),
('model_meta:person_friends_inherited', None),
('model_meta:person_following_inherited', None),
('model_meta:person_following_inherited', None),
('model_meta:relating_people', None),
('model_meta:relating_people_hidden', None),
('model_meta:relating', None),
('model_meta:relating', None),
('model_meta.baseperson_friends_base', BasePerson),
('model_meta.baseperson_friends_base', BasePerson),
('model_meta.baseperson_m2m_base', BasePerson),
('model_meta.baseperson_following_base', BasePerson),
('model_meta.baseperson_following_base', BasePerson),
('model_meta.baseperson_m2m_abstract', BasePerson),
('model_meta.baseperson_friends_abstract', BasePerson),
('model_meta.baseperson_friends_abstract', BasePerson),
('model_meta.baseperson_following_abstract', BasePerson),
('model_meta.baseperson_following_abstract', BasePerson),
('model_meta.relating_basepeople', BasePerson),
('model_meta.relating_basepeople_hidden', BasePerson),
('model_meta.relating', BasePerson),
('model_meta.relating', BasePerson),
('model_meta.person_m2m_inherited', None),
('model_meta.person_friends_inherited', None),
('model_meta.person_friends_inherited', None),
('model_meta.person_following_inherited', None),
('model_meta.person_following_inherited', None),
('model_meta.relating_people', None),
('model_meta.relating_people_hidden', None),
('model_meta.relating', None),
('model_meta.relating', None),
),
Relation: (
('model_meta:baseperson_m2m_base', None),
('model_meta:baseperson_m2m_abstract', None),
('model_meta:baseperson', None),
('model_meta:baseperson', None),
('model_meta:baseperson', None),
('model_meta:baseperson', None),
('model_meta:baseperson', None),
('model_meta:baseperson', None),
('model_meta:person_m2m_inherited', None),
('model_meta:person', None),
('model_meta:person', None),
('model_meta:person', None),
('model_meta:person', None),
('model_meta:person', None),
('model_meta:proxyperson', None),
('model_meta:proxyperson', None),
('model_meta:proxyperson', None),
('model_meta.baseperson_m2m_base', None),
('model_meta.baseperson_m2m_abstract', None),
('model_meta.baseperson', None),
('model_meta.baseperson', None),
('model_meta.baseperson', None),
('model_meta.baseperson', None),
('model_meta.baseperson', None),
('model_meta.baseperson', None),
('model_meta.person_m2m_inherited', None),
('model_meta.person', None),
('model_meta.person', None),
('model_meta.person', None),
('model_meta.person', None),
('model_meta.person', None),
('model_meta.proxyperson', None),
('model_meta.proxyperson', None),
('model_meta.proxyperson', None),
),
},
'get_all_related_objects_with_model_hidden_local': {
BasePerson: (
('model_meta:baseperson_friends_base', None),
('model_meta:baseperson_friends_base', None),
('model_meta:baseperson_m2m_base', None),
('model_meta:baseperson_following_base', None),
('model_meta:baseperson_following_base', None),
('model_meta:baseperson_m2m_abstract', None),
('model_meta:baseperson_friends_abstract', None),
('model_meta:baseperson_friends_abstract', None),
('model_meta:baseperson_following_abstract', None),
('model_meta:baseperson_following_abstract', None),
('model_meta:person', None),
('model_meta:relating_basepeople', None),
('model_meta:relating_basepeople_hidden', None),
('model_meta:relating', None),
('model_meta:relating', None),
('model_meta.baseperson_friends_base', None),
('model_meta.baseperson_friends_base', None),
('model_meta.baseperson_m2m_base', None),
('model_meta.baseperson_following_base', None),
('model_meta.baseperson_following_base', None),
('model_meta.baseperson_m2m_abstract', None),
('model_meta.baseperson_friends_abstract', None),
('model_meta.baseperson_friends_abstract', None),
('model_meta.baseperson_following_abstract', None),
('model_meta.baseperson_following_abstract', None),
('model_meta.person', None),
('model_meta.relating_basepeople', None),
('model_meta.relating_basepeople_hidden', None),
('model_meta.relating', None),
('model_meta.relating', None),
),
Person: (
('model_meta:person_m2m_inherited', None),
('model_meta:person_friends_inherited', None),
('model_meta:person_friends_inherited', None),
('model_meta:person_following_inherited', None),
('model_meta:person_following_inherited', None),
('model_meta:relating_people', None),
('model_meta:relating_people_hidden', None),
('model_meta:relating', None),
('model_meta:relating', None),
('model_meta.person_m2m_inherited', None),
('model_meta.person_friends_inherited', None),
('model_meta.person_friends_inherited', None),
('model_meta.person_following_inherited', None),
('model_meta.person_following_inherited', None),
('model_meta.relating_people', None),
('model_meta.relating_people_hidden', None),
('model_meta.relating', None),
('model_meta.relating', None),
),
Relation: (
('model_meta:baseperson_m2m_base', None),
('model_meta:baseperson_m2m_abstract', None),
('model_meta:baseperson', None),
('model_meta:baseperson', None),
('model_meta:baseperson', None),
('model_meta:baseperson', None),
('model_meta:baseperson', None),
('model_meta:baseperson', None),
('model_meta:person_m2m_inherited', None),
('model_meta:person', None),
('model_meta:person', None),
('model_meta:person', None),
('model_meta:person', None),
('model_meta:person', None),
('model_meta:proxyperson', None),
('model_meta:proxyperson', None),
('model_meta:proxyperson', None),
('model_meta.baseperson_m2m_base', None),
('model_meta.baseperson_m2m_abstract', None),
('model_meta.baseperson', None),
('model_meta.baseperson', None),
('model_meta.baseperson', None),
('model_meta.baseperson', None),
('model_meta.baseperson', None),
('model_meta.baseperson', None),
('model_meta.person_m2m_inherited', None),
('model_meta.person', None),
('model_meta.person', None),
('model_meta.person', None),
('model_meta.person', None),
('model_meta.person', None),
('model_meta.proxyperson', None),
('model_meta.proxyperson', None),
('model_meta.proxyperson', None),
),
},
'get_all_related_objects_with_model_proxy': {
@ -360,67 +360,67 @@ TEST_RESULTS = {
},
'get_all_related_objects_with_model_proxy_hidden': {
BasePerson: (
('model_meta:baseperson_friends_base', None),
('model_meta:baseperson_friends_base', None),
('model_meta:baseperson_m2m_base', None),
('model_meta:baseperson_following_base', None),
('model_meta:baseperson_following_base', None),
('model_meta:baseperson_m2m_abstract', None),
('model_meta:baseperson_friends_abstract', None),
('model_meta:baseperson_friends_abstract', None),
('model_meta:baseperson_following_abstract', None),
('model_meta:baseperson_following_abstract', None),
('model_meta:person', None),
('model_meta:relating_basepeople', None),
('model_meta:relating_basepeople_hidden', None),
('model_meta:relating', None),
('model_meta:relating', None),
('model_meta.baseperson_friends_base', None),
('model_meta.baseperson_friends_base', None),
('model_meta.baseperson_m2m_base', None),
('model_meta.baseperson_following_base', None),
('model_meta.baseperson_following_base', None),
('model_meta.baseperson_m2m_abstract', None),
('model_meta.baseperson_friends_abstract', None),
('model_meta.baseperson_friends_abstract', None),
('model_meta.baseperson_following_abstract', None),
('model_meta.baseperson_following_abstract', None),
('model_meta.person', None),
('model_meta.relating_basepeople', None),
('model_meta.relating_basepeople_hidden', None),
('model_meta.relating', None),
('model_meta.relating', None),
),
Person: (
('model_meta:baseperson_friends_base', BasePerson),
('model_meta:baseperson_friends_base', BasePerson),
('model_meta:baseperson_m2m_base', BasePerson),
('model_meta:baseperson_following_base', BasePerson),
('model_meta:baseperson_following_base', BasePerson),
('model_meta:baseperson_m2m_abstract', BasePerson),
('model_meta:baseperson_friends_abstract', BasePerson),
('model_meta:baseperson_friends_abstract', BasePerson),
('model_meta:baseperson_following_abstract', BasePerson),
('model_meta:baseperson_following_abstract', BasePerson),
('model_meta:relating_basepeople', BasePerson),
('model_meta:relating_basepeople_hidden', BasePerson),
('model_meta:relating', BasePerson),
('model_meta:relating', BasePerson),
('model_meta:person_m2m_inherited', None),
('model_meta:person_friends_inherited', None),
('model_meta:person_friends_inherited', None),
('model_meta:person_following_inherited', None),
('model_meta:person_following_inherited', None),
('model_meta:relating_people', None),
('model_meta:relating_people_hidden', None),
('model_meta:relating', None),
('model_meta:relating', None),
('model_meta:relating', None),
('model_meta:relating', None),
('model_meta.baseperson_friends_base', BasePerson),
('model_meta.baseperson_friends_base', BasePerson),
('model_meta.baseperson_m2m_base', BasePerson),
('model_meta.baseperson_following_base', BasePerson),
('model_meta.baseperson_following_base', BasePerson),
('model_meta.baseperson_m2m_abstract', BasePerson),
('model_meta.baseperson_friends_abstract', BasePerson),
('model_meta.baseperson_friends_abstract', BasePerson),
('model_meta.baseperson_following_abstract', BasePerson),
('model_meta.baseperson_following_abstract', BasePerson),
('model_meta.relating_basepeople', BasePerson),
('model_meta.relating_basepeople_hidden', BasePerson),
('model_meta.relating', BasePerson),
('model_meta.relating', BasePerson),
('model_meta.person_m2m_inherited', None),
('model_meta.person_friends_inherited', None),
('model_meta.person_friends_inherited', None),
('model_meta.person_following_inherited', None),
('model_meta.person_following_inherited', None),
('model_meta.relating_people', None),
('model_meta.relating_people_hidden', None),
('model_meta.relating', None),
('model_meta.relating', None),
('model_meta.relating', None),
('model_meta.relating', None),
),
Relation: (
('model_meta:baseperson_m2m_base', None),
('model_meta:baseperson_m2m_abstract', None),
('model_meta:baseperson', None),
('model_meta:baseperson', None),
('model_meta:baseperson', None),
('model_meta:baseperson', None),
('model_meta:baseperson', None),
('model_meta:baseperson', None),
('model_meta:person_m2m_inherited', None),
('model_meta:person', None),
('model_meta:person', None),
('model_meta:person', None),
('model_meta:person', None),
('model_meta:person', None),
('model_meta:proxyperson', None),
('model_meta:proxyperson', None),
('model_meta:proxyperson', None),
('model_meta.baseperson_m2m_base', None),
('model_meta.baseperson_m2m_abstract', None),
('model_meta.baseperson', None),
('model_meta.baseperson', None),
('model_meta.baseperson', None),
('model_meta.baseperson', None),
('model_meta.baseperson', None),
('model_meta.baseperson', None),
('model_meta.person_m2m_inherited', None),
('model_meta.person', None),
('model_meta.person', None),
('model_meta.person', None),
('model_meta.person', None),
('model_meta.person', None),
('model_meta.proxyperson', None),
('model_meta.proxyperson', None),
('model_meta.proxyperson', None),
),
},
'get_all_related_many_to_many_with_model': {
@ -643,12 +643,12 @@ class GetFieldByNameTests(OptionsBaseTests):
def test_get_related_object(self):
field_info = Person._meta.get_field_by_name('relating_baseperson')
self.assertEqual(field_info[1:], (BasePerson, False, False))
self.assertIsInstance(field_info[0], related.RelatedObject)
self.assertIsInstance(field_info[0], related.ForeignObjectRel)
def test_get_related_m2m(self):
field_info = Person._meta.get_field_by_name('relating_people')
self.assertEqual(field_info[1:], (None, False, True))
self.assertIsInstance(field_info[0], related.RelatedObject)
self.assertIsInstance(field_info[0], related.ForeignObjectRel)
def test_get_generic_foreign_key(self):
# For historic reasons generic foreign keys aren't available.

View File

@ -383,7 +383,7 @@ class OneToOneTests(TestCase):
be added to the related model.
"""
self.assertFalse(
hasattr(Target, HiddenPointer._meta.get_field('target').related.get_accessor_name())
hasattr(Target, HiddenPointer._meta.get_field('target').rel.get_accessor_name())
)
def test_related_object(self):