diff --git a/CHANGES.rst b/CHANGES.rst index ebe9a79f..f834d83c 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -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) ------------------ diff --git a/docs/querying_history.rst b/docs/querying_history.rst index 23b30f78..bdf33971 100644 --- a/docs/querying_history.rst +++ b/docs/querying_history.rst @@ -175,30 +175,50 @@ model history. -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 diff --git a/simple_history/models.py b/simple_history/models.py index b7e137da..3ffe42a5 100644 --- a/simple_history/models.py +++ b/simple_history/models.py @@ -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: @@ -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) diff --git a/simple_history/tests/tests/test_models.py b/simple_history/tests/tests/test_models.py index 4854fc3a..44384ca9 100644 --- a/simple_history/tests/tests/test_models.py +++ b/simple_history/tests/tests/test_models.py @@ -2424,12 +2424,12 @@ 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 @@ -2437,8 +2437,8 @@ def test_skip_history(self): 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") @@ -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()`