#35984 closed Bug (needsinfo)
functools.singledispatchmethod on Django models don't work
Reported by: | Simon Gomizelj | Owned by: | |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | 4.2 |
Severity: | Normal | Keywords: | |
Cc: | Triage Stage: | Unreviewed | |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
I'm on Django 4.2.17 and Python 3.13.1
The following code works on Python 3.12 and I believe https://github.com/python/cpython/issues/85160 is the cause
When traversing a relationship and then calling a singledispatchmethod
method registered on a model, the reference to self
seems to get cached and locked to the first created model.
class Test(models.Model): ... class Test2(models.Model): relationship = models.ForeignKey("Test", on_delete=models.CASCADE) @singledispatchmethod def bug(self, x): print(">", id(self)) @bug.register def _(self, x: int): print(">", id(self))
and then it's pretty easy to trigger:
for t in test.test2_set.all(): print("id:", id(t)) # always changes t.bug(4) # only ever prints a single id
Change History (5)
comment:1 by , 10 days ago
Version: | 5.1 → 4.2 |
---|
comment:2 by , 10 days ago
Component: | Uncategorized → Database layer (models, ORM) |
---|---|
Resolution: | → needsinfo |
Status: | new → closed |
Type: | Uncategorized → Bug |
comment:3 by , 10 days ago
Because honestly I'm not sure. I can't seem to replicate this behaviour outside of Django. Maybe Django is doing something non-standard. Either way, I have filed a cpython issue as well. We'll see where that goes.
comment:4 by , 10 days ago
I know what the issue is. There's a weak key map under the hood:
class singledispatchmethod: # snip def __get__(self, obj, cls=None): if self._method_cache is not None: try: _method = self._method_cache[obj] except TypeError: self._method_cache = None except KeyError: pass else: return _method
Since Django models __hash__
is driven by the pk, this is fundamentally incompatible with this optimization.
So more specifically, this bug then only occurs when the primary key is the same.
comment:5 by , 10 days ago
Apologies - absolutely a cpython bug. Pulling at this thread I figured out how to replicate it with just the Python stdlib.
Why create a bug report here when you believe cpython is at fault? Feel free to reopen with more explanation if you believe Django is at fault and can explain what action we should take.