6

I have two fields in a model here:

is_active = models.BooleanField(default=False) active_from = models.DateTimeField(blank=True) 

When is_active is set to True, I'd like active_from to be updated to the current datetime.

How do I go about doing this? I'm open to alternatives if there's a cleaner way of doing this with one field as well.

Thanks!

EDIT: I'd like to contain this within the model to keep things encapsulated. This will be part of an API.

3
  • 2
    Might be good to use pre_save signal? docs.djangoproject.com/en/1.9/ref/signals/#pre-save here's an answer based on it: stackoverflow.com/questions/1355150/… Commented Jun 16, 2016 at 19:11
  • While a signal could be used, it is considered a best practice to use them only as a last resort according to Two Scoops of Django. FYI. Commented Jun 16, 2016 at 19:57
  • I haven't heard of it - is it written by the Django project developers? Commented Jun 16, 2016 at 19:58

4 Answers 4

9

One way to do it is to define a save() method on your custom model looking for change in is_active. There is no easy way to achieve this: you need to manually save the previous state of the value of is_active by defining a custom __init__-method. It could look something like this:

class MyModel(models.Model): is_active = models.BooleanField(default=False) active_from = models.DateTimeField(blank=True) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.__is_active = self.active def save(self, *args, **kwargs): if self.is_active and not self.__is_active: self.active_from = datetime.now() super().save(*args, **kwargs) 

This question has some answers which might help.

Sign up to request clarification or add additional context in comments.

3 Comments

What does if self.is_active and not self.__is_active check? I don't know what the __ changes
the self.__is_active is the value of the model before the changes were applied, as set in the __init__ method. The check basically means: if is_active is True now, and was False before, update active_from
this is a better answer than mine.
2

This is what I've pieced together so far. It seems like the most Django-esque way of doing this, but I'm happy to be corrected if not.

from django.db.models.signals import pre_save, post_save from django.dispatch import receiver import datetime class MyModel(models.Model): is_active = models.BooleanField(default=False) active_from = models.DateTimeField(blank=True) # Set active_from if active is set in an update @receiver(pre_save, sender=MyModel) def set_active_from_on_update(sender, instance, update_fields, **kwargs): if 'is_active' in update_fields and instance.is_active is True: instance.active_from = datetime.now() # Set active_from if active is set on create @receiver(post_save, sender=MyModel) def set_active_from_on_create(sender, instance, created, **kwargs): if created and instance.is_active is True: instance.active_from = datetime.now() 

My reasoning: update_fields in pre_save seems like the right place for any logic based on particular fields updating, but pre_save doesn't know if instance will be a new entry in the database or not, so post_save is needed to use the create boolean.

I think I could also do away with is_active and set active_from to null when it isn't active, but that doesn't seem as safe.

Comments

1

When you toggle is_active update active_from at the same time.

for example:

def toggle_user_active_and_update(request, *a, **kw): request.user.is_active = !request.user.is_active request.user.active_from = datetime.datetime.now() request.user.save() 

Comments

1

Another alternative is the FieldTracker from django-model-utils. I'm going to be using this moving forwards, as it makes more complex manipulation easier during save().

1 Comment

Would your share an example with us? Once you can

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.