Skip to content

fix: catch correct DoesNotExist in HistoricReverseOneToOneDescriptor.…#1590

Open
awais786 wants to merge 1 commit intodjango-commons:masterfrom
awais786:fix/historic-reverse-onetoone-hasattr
Open

fix: catch correct DoesNotExist in HistoricReverseOneToOneDescriptor.…#1590
awais786 wants to merge 1 commit intodjango-commons:masterfrom
awais786:fix/historic-reverse-onetoone-hasattr

Conversation

@awais786
Copy link
Copy Markdown

@awais786 awais786 commented Mar 17, 2026

Closes #1588

Django's ReverseOneToOneDescriptor.get catches self.related.related_model.DoesNotExist, but when HistoricDescriptorMixin.get_queryset() returns a queryset over the historical model (e.g. HistoricalB instead of B), only the historical model's DoesNotExist matches. Override get in HistoricReverseOneToOneDescriptor to catch queryset.model.DoesNotExist so that hasattr/getattr on a missing reverse OneToOne returns False/None instead of raising.

Adds a regression test to HistoricOneToOneFieldTest covering the missing-relation case.

from onkol.apps.users.models import A
from django.utils.timezone import now

# Create object
a = A.objects.create()

# Get historical version at current time
historic_a = a.history.as_of(now())

# Assertions
assert not hasattr(a, 'b')
assert not hasattr(historic_a, 'b')

Description

Related Issue

Motivation and Context

How Has This Been Tested?

Screenshots (if appropriate):

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist:

  • I have run the pre-commit run command to format and lint.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have read the CONTRIBUTING document.
  • I have added tests to cover my changes.
  • I have added my name and/or github handle to AUTHORS.rst
  • I have added my change to CHANGES.rst
  • All new and existing tests passed.

…__get__

Closes django-commons#1588

Django's ReverseOneToOneDescriptor.__get__ catches self.related.related_model.DoesNotExist,
but when HistoricDescriptorMixin.get_queryset() returns a queryset over the historical model
(e.g. HistoricalB instead of B), only the historical model's DoesNotExist matches. Override
__get__ in HistoricReverseOneToOneDescriptor to catch queryset.model.DoesNotExist so that
hasattr/getattr on a missing reverse OneToOne returns False/None instead of raising.

Adds a regression test to HistoricOneToOneFieldTest covering the missing-relation case.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

When accessing missing reverse OneToOne, hasattr raises an exception

1 participant