Skip to content

Commit

Permalink
Deferred fields patch (#1393)
Browse files Browse the repository at this point in the history
* added patch and test

* added myself to AUTHORS

* adde my change to CHANGES.rst

* fixed bug to connect to pre_delete instead of post_delete

* implemented suggestion made by @jwaschkau in issue #1064

* added .python-version to the .gitignore

* -  added check to signal to see if instance is history enabled before loading its deferred fields

* check that fields is "truthy" before calling refresh_from_db

* added to test to ensure by-pass logic is covered
  • Loading branch information
JordanHyatt authored Nov 20, 2024
1 parent 4325591 commit 6c10248
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
.idea
.tox/
.venv/
.python-version
/.project
/.pydevproject
/.ve
Expand Down
1 change: 1 addition & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Unreleased
- Made ``skip_history_when_saving`` work when creating an object - not just when
updating an object (gh-1262)
- Improved performance of the ``latest_of_each()`` history manager method (gh-1360)
- Fixed issue with deferred fields causing DoesNotExist error (gh-678)
- Added HistoricOneToOneField (gh-1394)

3.7.0 (2024-05-29)
Expand Down
18 changes: 18 additions & 0 deletions simple_history/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ def finalize(self, sender, **kwargs):
# so the signal handlers can't use weak references.
models.signals.post_save.connect(self.post_save, sender=sender, weak=False)
models.signals.post_delete.connect(self.post_delete, sender=sender, weak=False)
models.signals.pre_delete.connect(self.pre_delete, sender=sender, weak=False)

m2m_fields = self.get_m2m_fields_from_model(sender)

Expand Down Expand Up @@ -668,6 +669,23 @@ def post_delete(self, instance, using=None, **kwargs):
else:
self.create_historical_record(instance, "-", using=using)

def pre_delete(self, instance, **kwargs):
"""
pre_delete method to ensure all deferred fileds are loaded on the model
"""
# First check that history is enabled (on model and globally)
if not getattr(settings, "SIMPLE_HISTORY_ENABLED", True):
return
if not hasattr(instance._meta, "simple_history_manager_attribute"):
return
fields = self.fields_included(instance)
field_attrs = {field.attname for field in fields}
deferred_attrs = instance.get_deferred_fields()
# Load all deferred fields that are present in fields_included
fields = field_attrs.intersection(deferred_attrs)
if fields:
instance.refresh_from_db(fields=fields)

def get_change_reason_for_object(self, instance, history_type, using):
"""
Get change reason for object.
Expand Down
12 changes: 12 additions & 0 deletions simple_history/tests/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -945,6 +945,18 @@ def test_history_with_unknown_field(self):
with self.assertNumQueries(0):
new_record.diff_against(old_record, excluded_fields=["unknown_field"])

def test_delete_with_deferred_fields(self):
Poll.objects.create(question="what's up bro?", pub_date=today)
Poll.objects.create(question="what's up sis?", pub_date=today)
Poll.objects.only("id").first().delete()
Poll.objects.defer("question").all().delete()
# Make sure bypass logic runs
Place.objects.create(name="cool place")
Place.objects.defer("name").first().delete()
with self.settings(SIMPLE_HISTORY_ENABLED=False):
Place.objects.create(name="cool place")
Place.objects.defer("name").all().delete()

def test_history_with_custom_queryset(self):
PollWithQuerySetCustomizations.objects.create(
id=1, pub_date=today, question="Question 1"
Expand Down

0 comments on commit 6c10248

Please sign in to comment.