[Django]-How to tell Django model that only one or other field must be not empty?

5👍

✅

You can add override the clean() method [Django-doc] to check this.

Furthermore we can add database constraints with Django’s constraint API and work with a CheckConstraint [Django-doc]. If the database supports this, it will reject entries where the two ForeignKeys are none, or if both are no None:

from django.core.exceptions import ValidationError
from django.db.models import Q

class OrderLign(models.Model):
    order = models.ForeignKey(
        Order,
        on_delete=models.CASCADE
    )
    item = models.ForeignKey(
        Item,
        on_delete=models.RESTRICT,
        blank=True,
        null=True
    )
    pack = models.ForeignKey(
        Pack,
        on_delete=models.RESTRICT,
        blank=True,
        null=True
    )

    def clean(self, *args, **kwargs):
        if (self.item is None) == (self.pack is None):
            raise ValidationError('item or pack should be filled in')
        return super().clean(*args, **kwargs)

    class Meta:
        constraints = [
            models.CheckConstraint(
                check=Q(item=None, pack__isnull=False) |
                      Q(pack=None, item__isnull=False),
                name='one of the two not null'
            )
        ]

2👍

I have solved my problem with some modification to the response of abdul-aziz-barkat and help of Willem.

from django.core.exceptions import ValidationError
from django.db.models import Q

class OrderLign(models.Model):
    class Meta:
        verbose_name = "ligne commande"
        constraints = [
            models.CheckConstraint(
                check=Q(item__isnull=True, pack__isnull=False) | Q(pack__isnull=True, item__isnull=False),
                name='Seul un article OU un pack peut être liée à une seule et même ligne commande.'
            )
        ]

def clean(self, *args, **kwargs):
    if self.item is None and self.pack is None:
        raise ValidationError('Une ligne commande doit forcemment avoir un article OU un pack')
    if self.item is not None and self.pack is not None:
        raise ValidationError('Une ligne commande ne peut avoir un article ET un pack')
    return super().clean(*args, **kwargs)

That work fine, with this code you can exlusively add an Orderlign with an item OR a pack

Leave a comment