Skip to content

Commit

Permalink
Make skip_history_when_saving work when creating a new object (#1262)
Browse files Browse the repository at this point in the history
* Made skip_history_when_saving work when creating

...a model object - not just when updating an object.

Also improved the docs on saving without creating historical records,
and fixed an unrelated `PollWithManyToMany` instance being used in the
assertions in the existing test for `skip_history_when_saving`.

* Apply suggestions from code review

Co-authored-by: Tim Schilling <schilling711@gmail.com>

* More docs suggestions from code review

---------

Co-authored-by: Tim Schilling <schilling711@gmail.com>
  • Loading branch information
ddabble and tim-schilling authored Jun 7, 2024
1 parent 2a2bec9 commit 08b4de7
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 24 deletions.
2 changes: 2 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ Changes
Unreleased
----------

- Made ``skip_history_when_saving`` work when creating an object - not just when
updating an object (gh-1262)

3.7.0 (2024-05-29)
------------------
Expand Down
52 changes: 36 additions & 16 deletions docs/querying_history.rst
Original file line number Diff line number Diff line change
Expand Up @@ -175,30 +175,50 @@ model history.
<Poll: Poll object as of 2010-10-25 18:04:13.814128>
Save without a historical record
--------------------------------
Save without creating historical records
----------------------------------------

If you want to save a model without a historical record, you can use the following:
If you want to save model objects without triggering the creation of any historical
records, you can do the following:

.. code-block:: python
class Poll(models.Model):
question = models.CharField(max_length=200)
history = HistoricalRecords()
poll.skip_history_when_saving = True
poll.save()
# We recommend deleting the attribute afterward
del poll.skip_history_when_saving
This also works when creating an object, but only when calling ``save()``:

.. code-block:: python
# Note that `Poll.objects.create()` is not called
poll = Poll(question="Why?")
poll.skip_history_when_saving = True
poll.save()
del poll.skip_history_when_saving
def save_without_historical_record(self, *args, **kwargs):
self.skip_history_when_saving = True
try:
ret = self.save(*args, **kwargs)
finally:
del self.skip_history_when_saving
return ret
.. note::
Historical records will always be created when calling the ``create()`` manager method.

Alternatively, call the ``save_without_historical_record()`` method on each object
instead of ``save()``.
This method is automatically added to a model when registering it for history-tracking
(i.e. defining a ``HistoricalRecords`` manager field on the model),
and it looks like this:

.. code-block:: python
poll = Poll(question='something')
poll.save_without_historical_record()
def save_without_historical_record(self, *args, **kwargs):
self.skip_history_when_saving = True
try:
ret = self.save(*args, **kwargs)
finally:
del self.skip_history_when_saving
return ret
Or disable history records for all models by putting following lines in your ``settings.py`` file:
Or disable the creation of historical records for *all* models
by adding the following line to your settings:

.. code-block:: python
Expand Down
7 changes: 4 additions & 3 deletions simple_history/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,9 +180,9 @@ def contribute_to_class(self, cls, name):
def add_extra_methods(self, cls):
def save_without_historical_record(self, *args, **kwargs):
"""
Save model without saving a historical record
Save the model instance without creating a historical record.
Make sure you know what you're doing before you use this method.
Make sure you know what you're doing before using this method.
"""
self.skip_history_when_saving = True
try:
Expand Down Expand Up @@ -651,8 +651,9 @@ def get_meta_options(self, model):
def post_save(self, instance, created, using=None, **kwargs):
if not getattr(settings, "SIMPLE_HISTORY_ENABLED", True):
return
if not created and hasattr(instance, "skip_history_when_saving"):
if hasattr(instance, "skip_history_when_saving"):
return

if not kwargs.get("raw", False):
self.create_historical_record(instance, created and "+" or "~", using=using)

Expand Down
22 changes: 17 additions & 5 deletions simple_history/tests/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2424,21 +2424,21 @@ def test_m2m_relation(self):
self.assertEqual(self.poll.history.all()[0].places.count(), 0)
self.assertEqual(poll_2.history.all()[0].places.count(), 2)

def test_skip_history(self):
def test_skip_history_when_updating_an_object(self):
skip_poll = PollWithManyToMany.objects.create(
question="skip history?", pub_date=today
)
self.assertEqual(self.poll.history.all().count(), 1)
self.assertEqual(self.poll.history.all()[0].places.count(), 0)
self.assertEqual(skip_poll.history.all().count(), 1)
self.assertEqual(skip_poll.history.all()[0].places.count(), 0)

skip_poll.skip_history_when_saving = True

skip_poll.question = "huh?"
skip_poll.save()
skip_poll.places.add(self.place)

self.assertEqual(self.poll.history.all().count(), 1)
self.assertEqual(self.poll.history.all()[0].places.count(), 0)
self.assertEqual(skip_poll.history.all().count(), 1)
self.assertEqual(skip_poll.history.all()[0].places.count(), 0)

del skip_poll.skip_history_when_saving
place_2 = Place.objects.create(name="Place 2")
Expand All @@ -2448,6 +2448,18 @@ def test_skip_history(self):
self.assertEqual(skip_poll.history.all().count(), 2)
self.assertEqual(skip_poll.history.all()[0].places.count(), 2)

def test_skip_history_when_creating_an_object(self):
initial_poll_count = PollWithManyToMany.objects.count()

skip_poll = PollWithManyToMany(question="skip history?", pub_date=today)
skip_poll.skip_history_when_saving = True
skip_poll.save()
skip_poll.places.add(self.place)

self.assertEqual(skip_poll.history.count(), 0)
self.assertEqual(PollWithManyToMany.objects.count(), initial_poll_count + 1)
self.assertEqual(skip_poll.places.count(), 1)

@override_settings(SIMPLE_HISTORY_ENABLED=False)
def test_saving_with_disabled_history_doesnt_create_records(self):
# 1 from `setUp()`
Expand Down

0 comments on commit 08b4de7

Please sign in to comment.