[Django] #34137: model.refresh_from_db() doesn't seem to clear cached generic foreign keys

8 views
Skip to first unread message

Django

unread,
Nov 3, 2022, 7:33:06 AM11/3/22
to django-...@googlegroups.com
#34137: model.refresh_from_db() doesn't seem to clear cached generic foreign keys
------------------------------------------+------------------------
Reporter: pascal chambon | Owner: nobody
Type: Uncategorized | Status: new
Component: Uncategorized | Version: 3.2
Severity: Normal | Keywords:
Triage Stage: Unreviewed | Has patch: 0
Needs documentation: 0 | Needs tests: 0
Patch needs improvement: 0 | Easy pickings: 0
UI/UX: 0 |
------------------------------------------+------------------------
In my code, Users have a generic foreign key like so:

{{{
controlled_entity_content_type = models.ForeignKey(ContentType,
blank=True, null=True, on_delete=models.CASCADE)
controlled_entity_object_id = models.PositiveIntegerField(blank=True,
null=True)
controlled_entity =
GenericForeignKey("controlled_entity_content_type",
"controlled_entity_object_id")
}}}

However, in unit-tests, when I refresh a user instance, the
controlled_entity relation isn't cleared from cache, as can be seen here
with IDs:


{{{
old_controlled_entity = authenticated_user.controlled_entity
authenticated_user.refresh_from_db()
new_controlled_entity = authenticated_user.controlled_entity
assert id(old_controlled_entity) != id(new_controlled_entity) #
FAILS
}}}


And this leads to subtle bugs like non-transitive equalities in tests :


{{{
assert authenticated_user.controlled_entity ==
fixtures.client1_project2_organization3
assert fixtures.client1_project2_organization3.get_pricing_plan()
== pricing_plan
assert authenticated_user.controlled_entity.get_pricing_plan() ==
pricing_plan # FAILS
}}}

Calling "authenticated_user.controlled_entity.refresh_from_db()" solevd
this particular bug, but "authenticated_user.refresh_from_db()" isn't
enough.


Tested under Django3.2.13 but the code of refresh_from_db() hasn't changed
since then in Git's main branch (except few cosmetic adjustments on code
format).

I'm a bit lost in the code of refresh_from_db(), but I've just seen that
the generic relation appears once in this loop, but is not considered as
"cached" in the if() branch.

{{{
for field in self._meta.related_objects:
#print("%% CLEARING RELATED FIELD", field)
if field.is_cached(self):
#print("%% DONE") # not called
field.delete_cached_value(self)
}}}

--
Ticket URL: <https://code.djangoproject.com/ticket/34137>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

Django

unread,
Nov 3, 2022, 7:34:33 AM11/3/22
to django-...@googlegroups.com
#34137: model.refresh_from_db() doesn't seem to clear cached generic foreign keys
--------------------------------+--------------------------------------

Reporter: pascal chambon | Owner: nobody
Type: Uncategorized | Status: new
Component: Uncategorized | Version: 3.2
Severity: Normal | Resolution:

Keywords: | Triage Stage: Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
--------------------------------+--------------------------------------
Description changed by pascal chambon:

Old description:

New description:

In my code, Users have a generic foreign key like so:

{{{
controlled_entity_content_type = models.ForeignKey(ContentType,
blank=True, null=True, on_delete=models.CASCADE)
controlled_entity_object_id = models.PositiveIntegerField(blank=True,
null=True)
controlled_entity =
GenericForeignKey("controlled_entity_content_type",
"controlled_entity_object_id")
}}}

However, in unit-tests, when I refresh a user instance, the
controlled_entity relation isn't cleared from cache, as can be seen here
with IDs:


{{{
old_controlled_entity = authenticated_user.controlled_entity
authenticated_user.refresh_from_db()
new_controlled_entity = authenticated_user.controlled_entity
assert id(old_controlled_entity) != id(new_controlled_entity) #
FAILS
}}}


And this leads to subtle bugs like non-transitive equalities in tests :


{{{
assert authenticated_user.controlled_entity ==
fixtures.client1_project2_organization3
assert fixtures.client1_project2_organization3.get_pricing_plan()
== pricing_plan
assert authenticated_user.controlled_entity.get_pricing_plan() ==
pricing_plan # FAILS
}}}

Calling "authenticated_user.controlled_entity.refresh_from_db()" solved


this particular bug, but "authenticated_user.refresh_from_db()" isn't
enough.


Tested under Django3.2.13 but the code of refresh_from_db() hasn't changed
since then in Git's main branch (except few cosmetic adjustments on code
format).

I'm a bit lost in the code of refresh_from_db(), but I've just seen that
the generic relation appears once in this loop, but is not considered as
"cached" in the if() branch.

{{{
for field in self._meta.related_objects:
#print("%% CLEARING RELATED FIELD", field)
if field.is_cached(self):
#print("%% DONE") # not called
field.delete_cached_value(self)
}}}

--

--
Ticket URL: <https://code.djangoproject.com/ticket/34137#comment:1>

Reply all
Reply to author
Forward
0 new messages