3

I have the following model:

class MetaData(models.Model):
    created_at = models.DateTimeField(auto_now_add=True, auto_now=False)
    created_by = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True,
                                   related_name='%(app_label)s_%(class)s_created_by')
    updated_at = models.DateTimeField(auto_now_add=False, auto_now=True)
    updated_by = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True,
                                   related_name='%(app_label)s_%(class)s_updated_by')

The update_at field is also completed with a value at the creation time instead of being null. I expected to have a value, only after the first save/creation.

Am I doing something wrong ?

Arpit Solanki
  • 9,567
  • 3
  • 41
  • 57
user3541631
  • 3,686
  • 8
  • 48
  • 115

2 Answers2

9

In Django, you have auto_now and auto_now_add both.

From the docs:

DateField.auto_now

Automatically set the field to now every time the object is saved. (which means when it is first created too)

DateField.auto_now_add

Automatically set the field to now when the object is first created.

Edit:

If you want your updated_at field to be null when the object is first created, you can simply pass null=True alongside with auto_now=True to the DateTimeField:

class MetaData(models.Model):
    # ...
    updated_at = models.DateTimeField(auto_now=True, null=True)
    # ...
wencakisa
  • 5,850
  • 2
  • 15
  • 36
  • 1
    There is any solution to overwrite the behavior ? – user3541631 Aug 23 '17 at 18:52
  • @user3541631 I've just edited my answer with a possible solution. – wencakisa Aug 23 '17 at 19:16
  • @user3541631 better have a look at this about use of auto_now and auto_now_add https://stackoverflow.com/questions/1737017/django-auto-now-and-auto-now-add – Carmine Tambascia Sep 12 '17 at 21:44
  • 7
    @wencakisa Are you sure that "auto_now=True, null=True" will work? Just tested it on Postgres DB and it doesn't. Also I don't see how that implementation would be possible on Postgress – zoran Aug 08 '19 at 17:22
  • 2
    @zoran, as you already noted, I also confirm that `updated_at = models.DateTimeField(auto_now=True, null=True)` does **not(!)** work. it is set every time the object is saved (including when record is created). note: tested with django 3.0.4 – udo Mar 09 '20 at 16:32
1

There is also another way to do that. It is better to do what wencakisa recommended, unless you are using save method within your model.

As it is written in the documentation

The field is only automatically updated when calling Model.save(). The field isn’t updated when making updates to other fields in other ways such as QuerySet.update(), though you can specify a custom value for the field in an update like that.

So, even if you use auto_now, but call save method for example to generate a slug address, the time will be assigned to the field with auto_now when the object is created.

How to handle such situation and keep the field updated_at with a null value after creation and before update?

For example, if you are using an UpdateView, assign the time there in a form_valid method.

models.py

class MetaData(models.Model):
    created_at = models.DateTimeField(auto_now_add=True, auto_now=False)
    created_by = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True,
                                   related_name='%(app_label)s_%(class)s_created_by')
    updated_at = models.DateTimeField(null=True)
    updated_by = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True,
                                   related_name='%(app_label)s_%(class)s_updated_by')

    def save(self, *args, **kwargs):
        # SOMETHING HERE
        super().save(*args, **kwargs)

views.py

class UpdateMetaData(generic.UpdateView):
    # SOMETHING HERE

    def form_valid(self, form):
        self.object = form.save(commit=False)
        self.object.updated_at = timezone.now()
        self.object.save()
        return super().form_valid(form)

You should have null value for updated_at after creation and the right time value after update of the field.

I think that in the same way you can also assign the update author to updated_by.

Jakub Siwiec
  • 428
  • 4
  • 13