Reputation: 2786
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.
Upvotes: 6
Views: 20226
Reputation: 4229
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.
Upvotes: 9
Reputation: 2786
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()
.
Upvotes: 1
Reputation: 2786
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.
Upvotes: 2
Reputation: 1964
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()
Upvotes: 1