#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 , 12 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 , 11 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.nowinstead 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.