#22571 closed Cleanup/optimization (fixed)
Document implications of using auto_now_add=True and get_or_create
Reported by: | Owned by: | Yamila | |
---|---|---|---|
Component: | Documentation | Version: | 1.4 |
Severity: | Normal | Keywords: | integrityerror auto_now_add get_or_create duplicatekey |
Cc: | me@… | Triage Stage: | Accepted |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description (last modified by )
Example:
# Given this simple model class Foo(models.Model): name = models.CharField(max_length=100) date_added = models.DateTimeField(auto_now_add=True) # This will always be true, even if an instance # with this name and today's date already exists bar, created = Foo.objects.get_or_create( name = 'Alex', date_added = some_datetime_obj ) print created # >> True # The problem is, auto_now_add does some stuff that # makes it uneditable, and messes up my expectations # when using it with get_or_create # Here's the solution class Foo(models.Model): name = models.CharField(max_length=100) date_added = models.DateTimeField(default=datetime.today()) bar, created = Foo.objects.get_or_create( name = 'Alex', date_added = some_datetime_obj ) print created # >> False Error: django.db.utils.IntegrityError: duplicate key value violates unique constraint
These two links document the issue:
http://alexkehayias.tumblr.com/post/33889961953/django-gotcha-duplicate-models-when-using
Change History (10)
comment:1 by , 11 years ago
Component: | Uncategorized → Documentation |
---|---|
Summary: | DateTimeField(auto_now_add=True) Breaks → Document implications of using auto_now_add=True and get_or_create |
Triage Stage: | Unreviewed → Accepted |
Type: | Uncategorized → Cleanup/optimization |
comment:2 by , 11 years ago
Your solution is not a solution at all, because your default on the DateTimeField is evaluated only once at model loading. You can use Django's django.utils.timezone.now
(w/o the trailing ()
).
comment:3 by , 11 years ago
Description: | modified (diff) |
---|
follow-up: 5 comment:4 by , 11 years ago
Note that your date_added is a DateTimeField, not a DateField, so it takes time into account. That's why using "today's date" won't match the existing value of date_added (since it's today's date + a particular time). If however some_datetime_obj in your first is exactly the same date and time, no new object will be created. If you don't want to consider the time in your get_or_create(), you can either use a DateField for date_added, or filter against year/month/day of your DateTimeField. So I think this ticket is invalid.
follow-up: 6 comment:5 by , 11 years ago
Replying to mardini:
Note that your date_added is a DateTimeField, not a DateField, so it takes time into account. That's why using "today's date" won't match the existing value of date_added (since it's today's date + a particular time). If however some_datetime_obj in your first is exactly the same date and time, no new object will be created. If you don't want to consider the time in your get_or_create(), you can either use a DateField for date_added, or filter against year/month/day of your DateTimeField. So I think this ticket is invalid.
I agree that the second example is a bit wrong but there's a legitimate issue to be documented here.
from django.db import models from django.utils import timezone class Auto(models.Model): added = models.DateTimeField(auto_now_add=True) class Default(models.Model): added = models.DateTimeField(default=timezone.now)
In [1]: from app.models import Auto, Default, timezone In [2]: added = timezone.now() In [3]: Auto.objects.get_or_create(added=added) Out[3]: (<Auto: Auto object>, True) In [4]: Auto.objects.get_or_create(added=added) Out[4]: (<Auto: Auto object>, True) In [5]: Default.objects.get_or_create(added=added) Out[5]: (<Default: Default object>, True) In [6]: Default.objects.get_or_create(added=added) Out[6]: (<Default: Default object>, False)
comment:6 by , 10 years ago
Cc: | added |
---|
Replying to charettes:
In [1]: from app.models import Auto, Default, timezone In [2]: added = timezone.now() In [3]: Auto.objects.get_or_create(added=added) Out[3]: (<Auto: Auto object>, True) In [4]: Auto.objects.get_or_create(added=added) Out[4]: (<Auto: Auto object>, True) In [5]: Default.objects.get_or_create(added=added) Out[5]: (<Default: Default object>, True) In [6]: Default.objects.get_or_create(added=added) Out[6]: (<Default: Default object>, False)
Isn't this expected? The doc says -
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.
And the code also shows that the value with always set to timezone.now()
if auto_now_add
is set, even if you provide a value when creating it.
Maybe we could update the doc to clarify what happens -
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 to this field when creating the object, it will be ignored. If you want to be able to modify this field, set
default=timezone.now
instead of thisauto_now_add=True
.
comment:7 by , 10 years ago
Owner: | changed from | to
---|---|
Status: | new → assigned |
I suppose the documentation could have a note about this.