Skip to content

Commit

Permalink
polymorphic accessors now use builtin caching from underlying fields
Browse files Browse the repository at this point in the history
  • Loading branch information
otto001 authored and pgammans committed Apr 4, 2024
1 parent 3312f5f commit a68a7a8
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 5 deletions.
18 changes: 13 additions & 5 deletions polymorphic/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,11 +192,15 @@ def __init__(self, *args, **kwargs):
return
self.__class__.polymorphic_super_sub_accessors_replaced = True

def create_accessor_function_for_model(model, accessor_name):
def create_accessor_function_for_model(model, field):
def accessor_function(self):
objects = getattr(model, "_base_objects", model.objects)
attr = objects.get(pk=self.pk)
return attr
try:
rel_obj = field.get_cached_value(self)
except KeyError:
objects = getattr(model, "_base_objects", model.objects)
rel_obj = objects.get(pk=self.pk)
field.set_cached_value(self, rel_obj)
return rel_obj

return accessor_function

Expand All @@ -209,10 +213,14 @@ def accessor_function(self):
type(orig_accessor),
(ReverseOneToOneDescriptor, ForwardManyToOneDescriptor),
):

field = orig_accessor.related \
if isinstance(orig_accessor, ReverseOneToOneDescriptor) else orig_accessor.field

setattr(
self.__class__,
name,
property(create_accessor_function_for_model(model, name)),
property(create_accessor_function_for_model(model, field)),
)

def _get_inheritance_relation_fields_and_models(self):
Expand Down
23 changes: 23 additions & 0 deletions polymorphic/tests/test_orm.py
Original file line number Diff line number Diff line change
Expand Up @@ -965,6 +965,29 @@ def test_parent_link_and_related_name(self):
# test that we can delete the object
t.delete()

def test_polymorphic__accessor_caching(self):
blog_a = BlogA.objects.create(name="blog")

blog_base = BlogBase.objects.non_polymorphic().get(id=blog_a.id)
blog_a = BlogA.objects.get(id=blog_a.id)

# test reverse accessor & check that we get back cached object on repeated access
self.assertEqual(blog_base.bloga, blog_a)
self.assertIs(blog_base.bloga, blog_base.bloga)
cached_blog_a = blog_base.bloga

# test forward accessor & check that we get back cached object on repeated access
self.assertEqual(blog_a.blogbase_ptr, blog_base)
self.assertIs(blog_a.blogbase_ptr, blog_a.blogbase_ptr)
cached_blog_base = blog_a.blogbase_ptr

# check that refresh_from_db correctly clears cached related objects
blog_base.refresh_from_db()
blog_a.refresh_from_db()

self.assertIsNot(cached_blog_a, blog_base.bloga)
self.assertIsNot(cached_blog_base, blog_a.blogbase_ptr)

def test_polymorphic__aggregate(self):
"""test ModelX___field syntax on aggregate (should work for annotate either)"""

Expand Down

0 comments on commit a68a7a8

Please sign in to comment.