Rython
Rython

Reputation: 46

How to update a DateTimeField ONLY WHEN a BooleanField is checked from False to True?

# models.py
class Appointment(models.Model):
    # not including some model fields and instead focusing on the model fields that are of concern
    records_sent = models.BooleanField(default=False)
    record_sent_date = models.DateTimeField(blank=True, null=True)
    records_received = models.BooleanField(default=False)
    record_received_date = models.DateTimeField(blank=True, null=True)


# views.py
class AppointmentUpdateView(UpdateView):
    model = Appointment
    fields = ['records_sent', 'records_received']

    def form_valid(self, form):
        """ Update sent/received datetimes to current time
        when sent/received is checked from false to true.
        """
        appointment = self.object
        if form.instance.records_sent:
            appointment.records_sent_date = timezone.now()
        if form.instance.records_received:
            appointment.records_received_date = timezone.now()
        return super().form_valid(form)

My main concern has to do with my if-statement logic in my Class View's form_valid method. Currently, if my BooleanFields are checked True via POST request, the timezone updates to now(), which is fine. But let's say I set records_sent=True on 2:00 pm. If I set records_received=True on 4:00 pm, records_sent ALSO updates its time to 4:00 pm because the POST request sent records_sent AND records_received = True in the form, subsequently triggering the if-statement again when it should be only applying to records_received.

How can I make it so that datetime.now() triggers ONLY when booleanfield is set from False to True, rather than having it also trigger from True to True?

Upvotes: 1

Views: 640

Answers (3)

Roham
Roham

Reputation: 2110

One way is to save your status before changing in your model class attributes and __init__ can help you like following codes:

class Appointment(models.Model):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.b_records_sent = self.records_sent
        self.b_records_received = self.records_received

    records_sent = models.BooleanField(default=False)
    record_sent_date = models.DateTimeField(blank=True, null=True)
    records_received = models.BooleanField(default=False)
    record_received_date = models.DateTimeField(blank=True, null=True)

and then you should add some extra conditions to form_valid method of your form to something like:

class AppointmentUpdateView(UpdateView):
    model = Appointment
    fields = ['records_sent', 'records_received']

    def form_valid(self, form):
        """ Update sent/received datetimes to current time
        when sent/received is checked from false to true.
        """
        appointment = self.object
        if form.instance.records_sent and appointment.b_records_sent != form.instance.records_sent:
            appointment.records_sent_date = timezone.now()
        if form.instance.records_received and appointment.b_records_received != form.instance.records_received:
            appointment.records_received_date = timezone.now()
        return super().form_valid(form)

This way the date time fields saves to table only if your boolean fields change but if you only want to save the time just once (i.e. whenever it's status changes to True) you can do something like following:

class AppointmentUpdateView(UpdateView):
    model = Appointment
    fields = ['records_sent', 'records_received']

    def form_valid(self, form):
        """ Update sent/received datetimes to current time
        when sent/received is checked from false to true.
        """
        appointment = self.object
        if form.instance.records_sent and appointment.record_sent_date is None:
            appointment.records_sent_date = timezone.now()
        if form.instance.records_received and appointment.record_received_date is None:
            appointment.records_received_date = timezone.now()
        return super().form_valid(form)

Upvotes: 0

Alexander Yudkin
Alexander Yudkin

Reputation: 472

You shouldn't use validators for this logic, and it isn't view's logic at all. Do it with the model's power. You can handle it with the extended save method or the better way is using the signal.

@receiver(pre_save, sender=Appointment)
def update_dates(sender, instance, **kwargs):
    if instance.pk:  # if instance is already exist
        now = timezone.now()
        obj = sender._default_manager.get(pk=instance.pk)
        if instance.records_sent and not obj.records_sent:
            instance.records_sent_date = now
        if instance.records_received and not obj.records_received:
            instance.records_received_date = now

This way guarantees the dates will be updated anytime when booleans will be changed to True, even if you changed the values via the admin panel, or shell, or another way.

Upvotes: 2

Oluwafemi Sule
Oluwafemi Sule

Reputation: 38962

Compare the form field to the model instance field to determine if the field wasn't set already.

if not appointment.records_sent and form.instance.records_sent:
    appointment.records_sent_date = timezone.now()
if not appointment.records_received and form.instance.records_received:
    appointment.records_received_date = timezone.now()
return super().form_valid(form)

Upvotes: 0

Related Questions