For Django 1.1.
I have this in my models.py:
class User(models.Model):
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
When updating a row I get:
[Sun Nov 15 02:18:12 2009] [error] /home/ptarjan/projects/twitter-meme/django/db/backends/mysql/base.py:84: Warning: Column 'created' cannot be null
[Sun Nov 15 02:18:12 2009] [error] return self.cursor.execute(query, args)
The relevant part of my database is:
`created` datetime NOT NULL,
`modified` datetime NOT NULL,
Is this cause for concern?
Side question: in my admin tool, those two fields aren't showing up. Is that expected?
This question is related to
python
django
datetime
django-models
django-admin
As for your Admin display, see this answer.
Note: auto_now
and auto_now_add
are set to editable=False
by default, which is why this applies.
You can use timezone.now()
for created and auto_now
for modified:
from django.utils import timezone
class User(models.Model):
created = models.DateTimeField(default=timezone.now())
modified = models.DateTimeField(auto_now=True)
If you are using a custom primary key instead of the default auto- increment int
, auto_now_add
will lead to a bug.
Here is the code of Django's default DateTimeField.pre_save withauto_now
and auto_now_add
:
def pre_save(self, model_instance, add):
if self.auto_now or (self.auto_now_add and add):
value = timezone.now()
setattr(model_instance, self.attname, value)
return value
else:
return super(DateTimeField, self).pre_save(model_instance, add)
I am not sure what the parameter add
is. I hope it will some thing like:
add = True if getattr(model_instance, 'id') else False
The new record will not have attr
id
, sogetattr(model_instance, 'id')
will return False will lead to not setting any value in the field.
Based on what I've read and my experience with Django so far, auto_now_add is buggy. I agree with jthanism --- override the normal save method it's clean and you know what's hapenning. Now, to make it dry, create an abstract model called TimeStamped:
from django.utils import timezone
class TimeStamped(models.Model):
creation_date = models.DateTimeField(editable=False)
last_modified = models.DateTimeField(editable=False)
def save(self, *args, **kwargs):
if not self.creation_date:
self.creation_date = timezone.now()
self.last_modified = timezone.now()
return super(TimeStamped, self).save(*args, **kwargs)
class Meta:
abstract = True
And then, when you want a model that has this time-stampy behavior, just subclass:
MyNewTimeStampyModel(TimeStamped):
field1 = ...
If you want the fields to show up in admin, then just remove the editable=False
option
I needed something similar today at work. Default value to be timezone.now()
, but editable both in admin and class views inheriting from FormMixin
, so for created in my models.py
the following code fulfilled those requirements:
from __future__ import unicode_literals
import datetime
from django.db import models
from django.utils.functional import lazy
from django.utils.timezone import localtime, now
def get_timezone_aware_now_date():
return localtime(now()).date()
class TestDate(models.Model):
created = models.DateField(default=lazy(
get_timezone_aware_now_date, datetime.date)()
)
For DateTimeField
, I guess remove the .date()
from the function and change datetime.date
to datetime.datetime
or better timezone.datetime
. I haven't tried it with DateTime
, only with Date
.
class Feedback(models.Model):
feedback = models.CharField(max_length=100)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
Here, we have created and updated columns that will have a timestamp when created, and when someone modified feedback.
auto_now_add will set time when an instance is created whereas auto_now will set time when someone modified his feedback.
I think the easiest (and maybe most elegant) solution here is to leverage the fact that you can set default
to a callable. So, to get around admin's special handling of auto_now, you can just declare the field like so:
from django.utils import timezone
date_field = models.DateField(default=timezone.now)
It's important that you don't use timezone.now()
as the default value wouldn't update (i.e., default gets set only when the code is loaded). If you find yourself doing this a lot, you could create a custom field. However, this is pretty DRY already I think.
Talking about a side question: if you want to see this fields in admin (though, you won't be able to edit it), you can add readonly_fields
to your admin class.
class SomeAdmin(ModelAdmin):
readonly_fields = ("created","modified",)
Well, this applies only to latest Django versions (I believe, 1.3 and above)
But I wanted to point out that the opinion expressed in the accepted answer is somewhat outdated. According to more recent discussions (django bugs #7634 and #12785), auto_now and auto_now_add are not going anywhere, and even if you go to the original discussion, you'll find strong arguments against the RY (as in DRY) in custom save methods.
A better solution has been offered (custom field types), but didn't gain enough momentum to make it into django. You can write your own in three lines (it's Jacob Kaplan-Moss' suggestion).
from django.db import models
from django.utils import timezone
class AutoDateTimeField(models.DateTimeField):
def pre_save(self, model_instance, add):
return timezone.now()
#usage
created_at = models.DateField(default=timezone.now)
updated_at = models.AutoDateTimeField(default=timezone.now)
If you alter your model class like this:
class MyModel(models.Model):
time = models.DateTimeField(auto_now_add=True)
time.editable = True
Then this field will show up in my admin change page
auto_now=True
didn't work for me in Django 1.4.1, but the below code saved me. It's for timezone aware datetime.
from django.utils.timezone import get_current_timezone
from datetime import datetime
class EntryVote(models.Model):
voted_on = models.DateTimeField(auto_now=True)
def save(self, *args, **kwargs):
self.voted_on = datetime.now().replace(tzinfo=get_current_timezone())
super(EntryVote, self).save(*args, **kwargs)
Here's the answer if you're using south and you want to default to the date you add the field to the database:
Choose option 2 then: datetime.datetime.now()
Looks like this:
$ ./manage.py schemamigration myapp --auto
? The field 'User.created_date' does not have a default specified, yet is NOT NULL.
? Since you are adding this field, you MUST specify a default
? value to use for existing rows. Would you like to:
? 1. Quit now, and add a default to the field in models.py
? 2. Specify a one-off value to use for existing columns now
? Please select a choice: 2
? Please enter Python code for your one-off default value.
? The datetime module is available, so you can do e.g. datetime.date.today()
>>> datetime.datetime.now()
+ Added field created_date on myapp.User
Is this cause for concern?
No, Django automatically adds it for you while saving the models, so, it is expected.
Side question: in my admin tool, those 2 fields aren't showing up. Is that expected?
Since these fields are auto added, they are not shown.
To add to the above, as synack said, there has been a debate on the django mailing list to remove this, because, it is "not designed well" and is "a hack"
Writing a custom save() on each of my models is much more pain than using the auto_now
Obviously you don't have to write it to every model. You can write it to one model and inherit others from it.
But, as auto_add
and auto_now_add
are there, I would use them rather than trying to write a method myself.
Source: Stackoverflow.com