138π
Iβve recently faced this situation while testing my application. I needed to "force" an expired timestamp. In my case, I did the trick by using a queryset update. Like this:
# my model
class FooBar(models.Model):
title = models.CharField(max_length=255)
updated_at = models.DateTimeField(auto_now=True, auto_now_add=True)
# my tests
foo = FooBar.objects.get(pk=1)
# force a timestamp
lastweek = datetime.datetime.now() - datetime.timedelta(days=7)
FooBar.objects.filter(pk=foo.pk).update(updated_at=lastweek)
# do the testing.
61π
You canβt really disable auto_now/auto_now_add in another way than you already do. If you need the flexibility to change those values, auto_now
/auto_now_add
is not best choice. It is often more flexible to use default
and/or override the save()
method to do manipulation right before the object is saved.
Using default
and an overridden save()
method, one way to solve your problem would be to define your model like this:
class FooBar(models.Model):
createtime = models.DateTimeField(default=datetime.datetime.now)
lastupdatetime = models.DateTimeField()
def save(self, *args, **kwargs):
if not kwargs.pop('skip_lastupdatetime', False):
self.lastupdatetime = datetime.datetime.now()
super(FooBar, self).save(*args, **kwargs)
In your code, where you want to skip the automatic lastupdatetime change, just use
new_entry.save(skip_lastupdatetime=True)
If your object is saved in the admin interface or other places, save() will be called without the skip_lastupdatetime argument, and it will behave just as it did before with auto_now
.
- [Django]-Creating email templates with Django
- [Django]-Getting Django admin url for an object
- [Django]-How to implement FirebaseDB with a Django Web Application
54π
You can also use the update_fields
parameter of save()
and pass your auto_now
fields. Hereβs an example:
# Date you want to force
new_created_date = date(year=2019, month=1, day=1)
# The `created` field is `auto_now` in your model
instance.created = new_created_date
instance.save(update_fields=['created'])
Hereβs the explanation from Djangoβs documentation: https://docs.djangoproject.com/en/stable/ref/models/instances/#specifying-which-fields-to-save
- [Django]-How to repeat a "block" in a django template
- [Django]-How to access outermost forloop.counter with nested for loops in Django templates?
- [Django]-Unittest Django: Mock external API, what is proper way?
30π
I used the suggestion made by the asker, and created some functions. Here is the use case:
turn_off_auto_now(FooBar, "lastupdatetime")
turn_off_auto_now_add(FooBar, "createtime")
new_entry.createtime = date
new_entry.lastupdatetime = date
new_entry.save()
Hereβs the implementation:
def turn_off_auto_now(ModelClass, field_name):
def auto_now_off(field):
field.auto_now = False
do_to_model(ModelClass, field_name, auto_now_off)
def turn_off_auto_now_add(ModelClass, field_name):
def auto_now_add_off(field):
field.auto_now_add = False
do_to_model(ModelClass, field_name, auto_now_add_off)
def do_to_model(ModelClass, field_name, func):
field = ModelClass._meta.get_field_by_name(field_name)[0]
func(field)
Similar functions can be created to turn them back on.
- [Django]-STATIC_ROOT vs STATIC_URL in Django
- [Django]-Automatic creation date for Django model form objects
- [Django]-How do I make many-to-many field optional in Django?
26π
I went the context manager way for reusability.
@contextlib.contextmanager
def suppress_autotime(model, fields):
_original_values = {}
for field in model._meta.local_fields:
if field.name in fields:
_original_values[field.name] = {
'auto_now': field.auto_now,
'auto_now_add': field.auto_now_add,
}
field.auto_now = False
field.auto_now_add = False
try:
yield
finally:
for field in model._meta.local_fields:
if field.name in fields:
field.auto_now = _original_values[field.name]['auto_now']
field.auto_now_add = _original_values[field.name]['auto_now_add']
Use like so:
with suppress_autotime(my_object, ['updated']):
my_object.some_field = some_value
my_object.save()
Boom.
- [Django]-Django: why i can't get the tracebacks (in case of error) when i run LiveServerTestCase tests?
- [Django]-Django logging of custom management commands
- [Django]-Use Python standard logging in Celery
12π
For those looking at this when they are writing tests, there is a python library called freezegun which allows you to fake the time β so when the auto_now_add
code runs, it gets the time you actually want. So:
from datetime import datetime, timedelta
from freezegun import freeze_time
with freeze_time('2016-10-10'):
new_entry = FooBar.objects.create(...)
with freeze_time('2016-10-17'):
# use new_entry as you wish, as though it was created 7 days ago
It can also be used as a decorator β see the link above for basic docs.
- [Django]-Detect mobile, tablet or Desktop on Django
- [Django]-Django: Reverse for 'detail' with arguments '('',)' and keyword arguments '{}' not found
- [Django]-Django import error β No module named core.management
5π
You can override auto_now_add
without special code.
I came across this question when I tried to create an object with particular date:
Post.objects.create(publication_date=date, ...)
where publication_date = models.DateField(auto_now_add=True)
.
So this is what Iβve done:
post = Post.objects.create(...)
post.publication_date = date
post.save()
This has successfully overridden auto_now_add
.
As a more long-term solution, overriding save
method is the way to go: https://code.djangoproject.com/ticket/16583
- [Django]-Split views.py in several files
- [Django]-Where to put business logic in django
- [Django]-Django Rest Framework β Could not resolve URL for hyperlinked relationship using view name "user-detail"
3π
A bit more clean version of context manager from https://stackoverflow.com/a/35943149/1731460
NOTE: Do NOT use this context manager in your views/forms or anywhere in your Django app.
This context manager alter internal state of field (by temporarily setting auto_now and auto_now_add to False).
That will cause Django to not populate these fields with timezone.now() during execution of context
managerβs body for concurrent requests (ie. same process, different thread).
Although this can be used for standalone scripts (ex. management commands, data migration)
which are not run in the same process with Django app.
from contextlib import contextmanager
@contextmanager
def suppress_auto_now(model, field_names=None):
"""
Temp disable auto_now and auto_now_add for django fields
@model - model class or instance
@field_names - list of field names to suppress or all model's
fields that support auto_now_add, auto_now"""
def get_auto_now_fields(user_selected_fields):
for field in model._meta.get_fields():
field_name = field.name
if user_selected_fields and field_name not in user_selected_fields:
continue
if hasattr(field, 'auto_now') or hasattr(field, 'auto_now_add'):
yield field
fields_state = {}
for field in get_auto_now_fields(user_selected_fields=field_names):
fields_state[field] = {
'auto_now': field.auto_now,
'auto_now_add': field.auto_now_add
}
for field in fields_state:
field.auto_now = False
field.auto_now_add = False
try:
yield
finally:
for field, state in fields_state.items():
field.auto_now = state['auto_now']
field.auto_now_add = state['auto_now_add']
You can use it even with Factories (factory-boy)
with suppress_auto_now(Click, ['created']):
ClickFactory.bulk_create(post=obj.post, link=obj.link, created__iter=created)
- [Django]-How can I list urlpatterns (endpoints) on Django?
- [Django]-Django 1.7 β App 'your_app_name' does not have migrations
- [Django]-Django 2 β How to register a user using email confirmation and CBVs?
3π
From django docs
DateField.auto_now_add
Automatically set the field to now when the object is first created. Useful for creation of timestamps. Note that the current date is always used; itβs not just a default value that you can override. So even if you set a value for this field when creating the object, it will be ignored. If you want to be able to modify this field, set the following instead of auto_now_add=True
:
For DateField: default=date.today
β from datetime.date.today()
For DateTimeField: default=timezone.now
β from django.utils.timezone.now()
- [Django]-Python 3 list(dictionary.keys()) raises error. What am I doing wrong?
- [Django]-How do you Serialize the User model in Django Rest Framework
- [Django]-Access web server on VirtualBox/Vagrant machine from host browser?
2π
I needed to disable auto_now for a DateTime field during a migration and was able to do this.
events = Events.objects.all()
for event in events:
for field in event._meta.fields:
if field.name == 'created_date':
field.auto_now = False
event.save()
- [Django]-Django Form File Field disappears on form error
- [Django]-How to test Django's UpdateView?
- [Django]-Function decorators with parameters on a class based view in Django
2π
Iβm late to the party, but similar to several of the other answers, this is a solution I used during a database migration. The difference from the other answers is that this disables all auto_now fields for the model under the assumption that thereβs really no reason to have more than one such field.
def disable_auto_now_fields(*models):
"""Turns off the auto_now and auto_now_add attributes on a Model's fields,
so that an instance of the Model can be saved with a custom value.
"""
for model in models:
for field in model._meta.local_fields:
if hasattr(field, 'auto_now'):
field.auto_now = False
if hasattr(field, 'auto_now_add'):
field.auto_now_add = False
Then to use it, you can simply do:
disable_auto_now_fields(Document, Event, ...)
And it will go through and nuke all of your auto_now
and auto_now_add
fields for all of the model classes you pass in.
- [Django]-How to get Request.User in Django-Rest-Framework serializer?
- [Django]-Django, creating a custom 500/404 error page
- [Django]-How do I POST with jQuery/Ajax in Django?
1π
While not exactly an answer (the question mentions data migration), here is an approach for testing with pytest.
Basically, it is possible to define a fixture to monkeypatch certain field instance attributes. Example may be adapted to loop through fields, etc.
@pytest.fixture
def disable_model_auto_dates(monkeypatch):
"""Disables auto dates on SomeModel."""
# might be local, might be on top
from project.models import SomeModel
field = SomeModel._meta.get_field('created_at')
monkeypatch.setattr(field, 'auto_now', False)
monkeypatch.setattr(field, 'auto_now_add', False)
- [Django]-Using Python's os.path, how do I go up one directory?
- [Django]-How to customize activate_url on django-allauth?
- [Django]-Django: Record with max element
0π
copy of Django β Models.DateTimeField β Changing dynamically auto_now_add value
Well , I spent this afternoon find out and the first problem is how fetch model object and where in code . Iβm in restframework in serializer.py , for example in __init__
of serializer it could not have the Model yet . Now in to_internal_value you can get the model class , after get the Field and after modify the field properties like in this example :
class ProblemSerializer(serializers.ModelSerializer):
def to_internal_value(self, data):
ModelClass = self.Meta.model
dfil = ModelClass._meta.get_field('date_update')
dfil.auto_now = False
dfil.editable = True
- [Django]-"gettext()" vs "gettext_lazy()" in Django
- [Django]-Setting Django up to use MySQL
- [Django]-Django get objects not referenced by foreign key
0π
I needed solution that will work with update_or_create
, Iβve came to this solution based on @andreaspelme code.
Only change is that You can set skipping by setting modified field to skip
not only by actually passing kwarg skip_modified_update
to save() method.
Just yourmodelobject.modified='skip'
and update will be skipped!
from django.db import models
from django.utils import timezone
class TimeTrackableAbstractModel(models.Model):
created = models.DateTimeField(default=timezone.now, db_index=True)
modified = models.DateTimeField(default=timezone.now, db_index=True)
class Meta:
abstract = True
def save(self, *args, **kwargs):
skip_modified_update = kwargs.pop('skip_modified_update', False)
if skip_modified_update or self.modified == 'skip':
self.modified = models.F('modified')
else:
self.modified = timezone.now()
super(TimeTrackableAbstractModel, self).save(*args, **kwargs)
- [Django]-Django override save for model only in some cases?
- [Django]-What is the most efficient way to store a list in the Django models?
- [Django]-How does the get_or_create function in Django return two values?
0π
Hereβs another variation and simplification of the useful answer from @soulseekah https://stackoverflow.com/a/35943149/202168
This one can suppress fields on multiple models simultaneously β useful in conjunction with factory_boy
such as when you have a SubFactory that also has fields that need suppressing
It looks like:
@contextmanager
def suppress_autonow(*fields: DeferredAttribute):
_original_values = {}
for deferred_attr in fields:
field = deferred_attr.field
_original_values[field] = {
'auto_now': field.auto_now,
'auto_now_add': field.auto_now_add,
}
field.auto_now = False
field.auto_now_add = False
try:
yield
finally:
for field, values in _original_values.items():
field.auto_now = values['auto_now']
field.auto_now_add = values['auto_now_add']
And is used like (with factory_boy
):
with suppress_autonow(Comment.created_at, Post.created_at):
PostFactory.create_batch(10) # if e.g. PostFactory also creates Comments
or just Django:
with suppress_autonow(FooBar.createtime, FooBar.lastupdatetime):
foobar = FooBar(
createtime=datetime(2013, 4, 6),
lastupdatetime=datetime(2016, 7, 9),
)
foobar.save()
i.e. you pass in the actual fields you want to suppress.
Note that you must pass them as class fields (i.e. Comment.created_at
) and not instance fields (not my_comment.created_at
)
NOTE:
This will break if you pass non-Date/DateTime/Time field to the fields
args. if it bothers you, add in an extra isinstance
check after field = deferred_attr.field
- [Django]-Unable to find a locale path to store translations for file __init__.py
- [Django]-Allowing RabbitMQ-Server Connections
- [Django]-Django β How to set default value for DecimalField in django 1.3?