Stack Exchange
Django 2.2 - how to express a constraint between two fields
I have a couple of models that look like this:
class AlarmStatus(Enum):
    UNDEFINED = "Undefined"
    PENDING = "Pending"
    CUSTOMER_PENDING = "Customer Pending"

    @classmethod
    def choices(cls):
        return tuple((i.name, i.value) for i in cls)


class Alarm(models.Model):
    managed = models.BooleanField(default=False, blank=False, verbose_name="Managed ?")
    status = models.CharField(
        max_length=20, default=AlarmStatus.UNDEFINED.name, choices=AlarmStatus.choices()
    )

    class Meta:
        constraints = [
            models.CheckConstraint(
                check=Q(managed=False) & Q(status=AlarmStatus.UNDEFINED.name),
                name="if_managed_is_False_status_must_be_UNDEFINED")
               ]

I would like to enforce at the db level the following invariant:
if an alarm managed field is False, then its status field MUST be "UNDEFINED".
I.e. I want to declare illegal the following state/combination:
managed == False AND status == (PENDING | CUSTOMER_PENDING)
How can I enforce this?
I tried with the above constraint but it is clearly wrong, because it is enforcing that every row managed field must be False AND every row status must be UNDEFINED.
Thanks

An implication [wiki] (i.e. A → B) can be expressed as ¬A∨B, so you can represent this as:
models.CheckConstraint(
    check=Q(managed=True) | Q(status=AlarmStatus.UNDEFINED.name),
    name="if_managed_is_False_status_must_be_UNDEFINED"
)
It thus means that the check is satisfied if managed is True (since then we do not care about the status, or the status should be UNDEFINED.

Brilliant: now if I try to update the status of any unmanaged alarm to any value other than UNDEFINED, I get back an IntegrityError. The only issue is that it still allows me to update a managed alarm with status = UNDEFINED...django automatically set the managed field back to False but I would like to return an IntegrityError in this case too.

@pastoreerrante: this might something with the database. What happens if you make an update at the database side?

`>>> managed_alarm = Alarm.objects.get(pk=2)



managed_alarm.status
'PENDING'
managed_alarm.managed
True
managed_alarm.status = "UNDEFINED"
managed_alarm.save()



` trying with django python shell I can reproduce the issue. If I set an unmanaged alarm to any value other than UNDEFINED I get instead the integrity error.
