From 155a127afbd531180eaa2fd875fe111178cfb64c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anssi=20K=C3=A4=C3=A4ri=C3=A4inen?= Date: Mon, 23 Feb 2015 09:31:58 +0200 Subject: [PATCH] [1.8.x] Fixed #24381 -- removed ForeignObjectRel opts and to_opts These cached properies were causing problems with pickling, and in addition they were confusingly defined: field.rel.model._meta was not the same as field.rel.opts. Instead users should use field.rel.related_model._meta inplace of field.rel.opts, and field.rel.to._meta in place of field.rel.to_opts. Backport of f95122e541df5bebb9b5ebb6226b0013e5edc893 from master --- django/contrib/admin/utils.py | 2 +- django/db/models/fields/related.py | 24 ++++++++++-------------- tests/queryset_pickle/tests.py | 3 +++ 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/django/contrib/admin/utils.py b/django/contrib/admin/utils.py index 5de0f09a48..65983cb0e6 100644 --- a/django/contrib/admin/utils.py +++ b/django/contrib/admin/utils.py @@ -316,7 +316,7 @@ def label_for_field(name, model, model_admin=None, return_attr=False): label = field.verbose_name except AttributeError: # field is likely a ForeignObjectRel - label = field.opts.verbose_name + label = field.related_model._meta.verbose_name except FieldDoesNotExist: if name == "__unicode__": label = force_text(model._meta.verbose_name) diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py index c07a01ced9..fd06223146 100644 --- a/django/db/models/fields/related.py +++ b/django/db/models/fields/related.py @@ -485,7 +485,7 @@ class SingleRelatedObjectDescriptor(object): value, instance._meta.object_name, self.related.get_accessor_name(), - self.related.opts.object_name, + self.related.related_model._meta.object_name, ) ) elif value is not None: @@ -1290,14 +1290,6 @@ class ForeignObjectRel(object): def model(self): return self.to - @cached_property - def opts(self): - return self.related_model._meta - - @cached_property - def to_opts(self): - return self.to._meta - @cached_property def hidden(self): return self.is_hidden() @@ -1330,7 +1322,11 @@ class ForeignObjectRel(object): return self.field.one_to_one def __repr__(self): - return '<%s: %s.%s>' % (type(self).__name__, self.opts.app_label, self.opts.model_name) + return '<%s: %s.%s>' % ( + type(self).__name__, + self.related_model._meta.app_label, + self.related_model._meta.model_name, + ) def get_choices(self, include_blank=True, blank_choice=BLANK_CHOICE_DASH, limit_to_currently_related=False): @@ -1381,7 +1377,7 @@ class ForeignObjectRel(object): # 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 + opts = model._meta if model else self.related_model._meta model = model or self.related_model if self.multiple: # If this is a symmetrical m2m relation on self, there is no reverse accessor. @@ -1390,9 +1386,9 @@ class ForeignObjectRel(object): 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.default_related_name % { + 'model_name': opts.model_name.lower(), + 'app_label': opts.app_label.lower(), } return opts.model_name + ('_set' if self.multiple else '') diff --git a/tests/queryset_pickle/tests.py b/tests/queryset_pickle/tests.py index 0d9687bf16..d735413e1f 100644 --- a/tests/queryset_pickle/tests.py +++ b/tests/queryset_pickle/tests.py @@ -43,6 +43,9 @@ class PickleabilityTestCase(TestCase): def test_membermethod_as_default(self): self.assert_pickles(Happening.objects.filter(number4=1)) + def test_filter_reverse_fk(self): + self.assert_pickles(Group.objects.filter(event=1)) + def test_doesnotexist_exception(self): # Ticket #17776 original = Event.DoesNotExist("Doesn't exist")