summaryrefslogtreecommitdiff
path: root/django/db/models/fields/reverse_related.py
diff options
context:
space:
mode:
authorKeryn Knight <keryn@kerynknight.com>2021-08-05 19:11:14 +0100
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2021-11-03 11:27:04 +0100
commita697424969f7f464bf6492b09a6cdac135499e02 (patch)
tree179e1165bc361e3fe8b822135790ea971449a4d4 /django/db/models/fields/reverse_related.py
parent3ff7b15bb79f2ee5b7af245c55ae14546243bb77 (diff)
downloaddjango-a697424969f7f464bf6492b09a6cdac135499e02.tar.gz
Fixed #32996 -- Cached PathInfos on relations.
PathInfo values are ostensibly static over the lifetime of the object for which they're requested, so the data can be memoized, quickly amortising the cost over the process' duration.
Diffstat (limited to 'django/db/models/fields/reverse_related.py')
-rw-r--r--django/db/models/fields/reverse_related.py25
1 files changed, 22 insertions, 3 deletions
diff --git a/django/db/models/fields/reverse_related.py b/django/db/models/fields/reverse_related.py
index 65950590e2..6f0c788bbd 100644
--- a/django/db/models/fields/reverse_related.py
+++ b/django/db/models/fields/reverse_related.py
@@ -71,7 +71,7 @@ class ForeignObjectRel(FieldCacheMixin):
When filtering against this relation, return the field on the remote
model against which the filtering should happen.
"""
- target_fields = self.get_path_info()[-1].target_fields
+ target_fields = self.path_infos[-1].target_fields
if len(target_fields) > 1:
raise exceptions.FieldError("Can't use target_field for multicolumn relations.")
return target_fields[0]
@@ -138,6 +138,18 @@ class ForeignObjectRel(FieldCacheMixin):
def __hash__(self):
return hash(self.identity)
+ def __getstate__(self):
+ state = self.__dict__.copy()
+ # Delete the path_infos cached property because it can be recalculated
+ # at first invocation after deserialization. The attribute must be
+ # removed because subclasses like ManyToOneRel may have a PathInfo
+ # which contains an intermediate M2M table that's been dynamically
+ # created and doesn't exist in the .models module.
+ # This is a reverse relation, so there is no reverse_path_infos to
+ # delete.
+ state.pop('path_infos', None)
+ return state
+
def get_choices(
self, include_blank=True, blank_choice=BLANK_CHOICE_DASH,
limit_choices_to=None, ordering=(),
@@ -195,7 +207,14 @@ class ForeignObjectRel(FieldCacheMixin):
return opts.model_name + ('_set' if self.multiple else '')
def get_path_info(self, filtered_relation=None):
- return self.field.get_reverse_path_info(filtered_relation)
+ if filtered_relation:
+ return self.field.get_reverse_path_info(filtered_relation)
+ else:
+ return self.field.reverse_path_infos
+
+ @cached_property
+ def path_infos(self):
+ return self.get_path_info()
def get_cache_name(self):
"""
@@ -234,7 +253,7 @@ class ManyToOneRel(ForeignObjectRel):
self.field_name = field_name
def __getstate__(self):
- state = self.__dict__.copy()
+ state = super().__getstate__()
state.pop('related_model', None)
return state