Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Documented auto_now usage with bulk_update_with_history #1055

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Unreleased
- Added ``tracked_fields`` attribute to historical models (gh-1038)
- Fixed ``KeyError`` when running ``clean_duplicate_history`` on models with ``excluded_fields`` (gh-1038)
- Added support for Python 3.11 (gh-1053)
- Documented ``auto_now`` solution for ``bulk_update_with_history`` (gh-1054)

3.1.1 (2022-04-23)
------------------
Expand Down
33 changes: 33 additions & 0 deletions docs/common_issues.rst
Original file line number Diff line number Diff line change
Expand Up @@ -275,3 +275,36 @@ arguments using ``excluded_field_kwargs`` as follows:
history = HistoricalRecords(
excluded_field_kwargs={"organizer": set(["custom_argument"])}
)


Updated auto_now inside a bulk_update_with_history
--------------------------------------------------

If you are tracking update timestamps (e.g. ``updated_at``, ``modified_at``), these are only tracked when
``Model.save()`` is called. As a result, ``bulk_update_with_history`` won't update them.

As a convention, you can define a custom manager to update these timestamps automatically:

.. code-block:: python

from django.utils import timezone

class UpdatedAtFriendlyQuerySet(models.QuerySet):
def bulk_update(self, objs, fields, *args, **kwargs):
timestamp = timezone.now()
for obj in objs:
assert hasattr(obj, "updated_at"), "Expected bulk_update object to have `updated_at`"
obj.updated_at = timestamp
assert "updated_at" not in fields, "Encountered unexpected scenario where we set `updated_at` via `bulk_update`"
new_fields = tuple(fields) + ("updated_at",)
super().bulk_update(objs, new_fields, *args, **kwargs)

class UpdatedAtFriendlyManager(models.Manager.from_queryset(UpdatedAtFriendlyQuerySet)):
pass

class Poll(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)

# This must be the first Manager declared in this class, otherwise it won't be detected as the `default_manager`
objects = UpdatedAtFriendlyManager()