-
Notifications
You must be signed in to change notification settings - Fork 499
Open
Description
When two models have history and a HistoricOneToOneField relation, using hasattr (or getattr with a default) on the reverse side raises an error.
To Reproduce
class A(Model):
history = HistoricalRecords()
class B(Model):
history = HistoricalRecords()
a = HistoricOneToOneField(A, on_delete=models.CASCADE)
a = A.objects.create()
assert not hasattr(a, 'b') # Works fine
historic_a = a.history.as_of(now())
assert not hasattr(historic_a, 'b') # Raises an error- Django Simple History Version: 3.11.0
- Django Version: 5.2.11
The error I get is
assert not hasattr(historic_a, 'b')
~~~~~~~^^^^^^^^^^^^^^^^^
File "lib/python3.14/site-packages/django/db/models/fields/related_descriptors.py", line 523, in __get__
rel_obj = self.get_queryset(instance=instance).get(**filter_args)
File "lib/python3.14/site-packages/django/db/models/query.py", line 635, in get
raise self.model.DoesNotExist(
"%s matching query does not exist." % self.model._meta.object_name
)
app_name.HistoricalB.DoesNotExist: HistoricalB matching query does not exist.After looking into this, the source of the bug is Django's ReverseOneToOneDescriptor.__get__:
try:
rel_obj = self.get_queryset(instance=instance).get(**filter_args)
except self.related.related_model.DoesNotExist:
rel_obj = NoneAs you can see, it assumes that the model for get_queryset is self.related.related_model which is incorrect for HistoricReverseOneToOneDescriptor which inherits HistoricDescriptorMixin that overrides it.
I worked around this by inheriting HistoricReverseOneToOneDescriptor and reimplementing the __get__ but replacing this part with
queryset = self.get_queryset(instance=instance)
try:
rel_obj = queryset.get(**filter_args)
except queryset.model.DoesNotExist:
...So, should I:
- Open a PR with my workaround?
- Try to upstream the fix in Django?
Other suggestions are welcome
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels