245

What's the best way to set a creation date for an object automatically, and also a field that will record when the object was last updated?

models.py:

created_at = models.DateTimeField(False, True, editable=False)
updated_at = models.DateTimeField(True, True, editable=False)

views.py:

if request.method == 'POST':
    form = MyForm(request.POST)
    if form.is_valid():
        obj = form.save(commit=False)
        obj.user = request.user
        obj.save()
        return HttpResponseRedirect('obj_list')

I get the error:

objects_object.created_at may not be NULL

Do I have to manually set this value myself? I thought that was the point of the parameters passed to DateTimeField (or are they just defaults, and since I've set editable=False they don't get displayed on the form, hence don't get submitted in the request, and therefore don't get put into the form?).

What's the best way of doing this? An __init__ method?

Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
Roger
  • 4,911
  • 6
  • 29
  • 26

2 Answers2

467

You can use the auto_now and auto_now_add options for updated_at and created_at respectively.

class MyModel(models.Model):
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
phoenix
  • 7,988
  • 6
  • 39
  • 45
Manoj Govindan
  • 72,339
  • 21
  • 134
  • 141
  • 4
    it produces this error: `You are trying to add a non-nullable field 'created_at' to gameuser without a default; we can't do that (the database needs something to populate existing rows). Please select a fix: 1) Provide a one-off default now (will be set on all existing rows) 2) Quit, and let me add a default in models.py Select an option: 1 Please enter the default value now, as valid Python The datetime and django.utils.timezone modules are available, so you can do e.g. timezone.now() >>> timezone.now()` – Kaleem Ullah Jun 21 '16 at 10:56
  • 2
    delete the rows on the databas, or check option 1 and add timezone.now() – mullerivan Mar 10 '17 at 04:39
  • Select an option: 1 Please enter the default value now, as valid Python The datetime and django.utils.timezone modules are available, so you can do e.g. timezone.now() >>> timezone.now() Migrations for 'aml_validations': – mullerivan Mar 10 '17 at 04:46
  • (fields.E160) The options auto_now, auto_now_add, and default are mutually exclusive. Only one of these options may be present-- Migration error – Hardik Gajjar Apr 27 '17 at 12:49
  • 4
    [Django REST Framework seems to think this is a fine approach](https://www.django-rest-framework.org/api-guide/fields/#auto_now-and-auto_now_add-model-fields). – phoenix Jun 25 '19 at 12:50
  • 8
    I think @gregoltsov's comment is out of date at this point. Using `auto_now_add` or `auto_now` works just fine. It sets the field before saving the model (https://github.com/django/django/blob/stable/3.0.x/django/db/models/fields/__init__.py#L1343). – yndolok Jul 23 '20 at 20:34
  • @gregoltsov comment is outdated as [another answer to the same question he mentioned](https://stackoverflow.com/a/3330128/2281751) states. – Dandelion Apr 07 '21 at 11:11
  • 1
    Thanks @yndolok and Dandelion - I've removed my comment so it doesn't confuse people landing on this answer! – gregoltsov Apr 20 '21 at 11:35
114

Well, the above answer is correct, auto_now_add and auto_now would do it, but it would be better to make an abstract class and use it in any model where you require created_at and updated_at fields.

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

    class Meta:
        abstract = True

Now anywhere you want to use it you can do a simple inherit and you can use timestamp in any model you make like.

class Posts(TimeStampMixin):
    name = models.CharField(max_length=50)
    ...
    ...

In this way, you can leverage object-oriented reusability, in Django DRY(don't repeat yourself)

Chitrank Dixit
  • 3,961
  • 4
  • 39
  • 59
  • 10
    Very soon you find yourself with 7 layers of inheritance not understanding what the devil is going on... or better yet 7 layers of inheritance collapsed into one class with the name "Utility". – Spidey Aug 18 '21 at 14:58
  • @Spidey I'm conflicted about your comment: on one hand I very much agree that abstraction abuse (especially the OOP flavored one) is more often than not a bad thing. However in this case, it seems to me like a case of "good (because it's predictable and generally useful) pattern / practice encapsulation", which is, when you think about it, the essence of frameworks like Django and such. – cjauvin Feb 27 '22 at 17:01
  • 1
    @cjauvin I understand your position, I've been there myself but I honestly think this example falls under `composition over inheritance`. Taking inspiration from libs/frameworks is okay, but remember that project code doesn't need to be as reusable. It needs to be quickly modifiable though and adding layers of inheritance can hurt in that regard. – Spidey Feb 27 '22 at 19:29
  • if everybody had that mindset we'd never have taken off from assembly. – Eric Feb 16 '23 at 20:40