ilstam
ilstam

Reputation: 1594

django - update date automatically after a value change

In the following django model:

class MyModel(models.Model):
    published = models.BooleanField(default=False)
    pub_date = models.DateTimeField('date published')

I want the pub_date field to be automatically updated with the current date, every time the published field changes to True.

What is the smart way?

Thanks.

Upvotes: 25

Views: 35390

Answers (7)

Ghazi
Ghazi

Reputation: 804

A really tricky easy way to do it, is by actually getting the query after you update it, and invoking save() on it, this all happens in the view, I will create the simplest implementation of a view that receive a post request:

def update_published_view(request,pk):
    if request.method == 'POST':
       models.MyModel.objects.filter(pk=pk).update(published=request.POST.get('is_published','')
       my_model_instance = models.MyModel.objects.get(pk=pk) # the trick here is to access 
#the instance after it is updated and then invoke `save()` 
#on the query to actually update the date field
       my_model_instance.save()
return render(request,'somepage.html')

and don't forget to set auto_now to True in pub_date field in MyModel model.

Upvotes: 0

Jamil Noyda
Jamil Noyda

Reputation: 3639

just Override Update method

from django.utils import timezone


class Vehicle(models.Model):

    class Meta:
        ordering = ['-created']

    created = models.DateTimeField(default=timezone.now)
    updated = models.DateTimeField(default=timezone.now)

    def update(self, *args, **kwargs):
        kwargs.update({'updated': timezone.now})
        super().update(*args, **kwargs)

        return self

Upvotes: 0

Bogdan Goie
Bogdan Goie

Reputation: 1297

the following only changes your pub_date if published has been modified from False to True:

from datetime import datetime

def __init__(self, *args, **kwargs):
    super(MyModel, self).__init__(*args, **kwargs)
    self.old_published = self.published

def save(self, *args, **kwargs):
    if self.published and self.old_published != self.published:
        self.pub_date = datetime.now()
    super(Model, self).save(*args, **kwargs)

Upvotes: 9

Alasdair
Alasdair

Reputation: 308779

If you are setting the object as published in the Django admin, a nice way to do this is to override the save_model method of your model admin class.

from datetime import datetime

from django.contrib import admin


class MyModelAdmin(admin.ModelAdmin):
    def save_model(self, request, obj, form, change):
        if obj.published and 'published' in form.changed_data
            obj.pub_date = datetime.now()
            obj.save()

admin.site.register(MyModel, MyModelAdmin)

If you are setting the published flag elsewhere, then you can override the save() method, or use the pre_save signal. This isn't as elegant, because it's harder to tell whether the published flag has changed. I think you need to re-fetch the object from the database to check.

Upvotes: 12

ilstam
ilstam

Reputation: 1594

All answers were useful, but I finally did it this way:

def save(self, *args, **kwargs):
    if self.published and self.pub_date is None:
        self.pub_date = timezone.now()
    elif not self.published and self.pub_date is not None:
        self.pub_date = None
    super(Model, self).save(*args, **kwargs)

Upvotes: 13

Eric Bulloch
Eric Bulloch

Reputation: 897

You want to add the auto_now field and set it to True. This will update with the current timestamp each time you update the model.

pub_date = models.DateTimeField('date_published', auto_now=True)

You can read about it here


Edit

Sorry you really just want to change the timestamp when the value of published is set to True. A really easy way to do this is to get the original value of the model and then override the save method so that it gets updated when it is set to True. Here is what you add to your code:

class MyModel(models.Model):
    published = models.BooleanField(default=False)
    pub_date = models.DateTimeField('date published')

    def __init__(self, *args, **kwargs):
        super(MyModel, self).__init__(*args, **kwargs)
        self._published = self.published

    def save(self, *args, **kwargs):
        if not self._published and self.published:
            self.pub_date = django.timezone.now()
        super(MyModel, self).save(*args, **kwargs)

Upvotes: 25

kylieCatt
kylieCatt

Reputation: 11039

Create a publish() method:

def publish(self):
    self.published = True
    self.pub_date = datetime.datetime.now()
    self.save()

Upvotes: 2

Related Questions