Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django one of 2 fields must not be null

I have a model similar to this one:

class Person(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    field1= models.IntegerField(null=True)
    field2 = models.IntegerField(null=True)

At least one of the fields field1 or field2 must not be null. How I can I verify it in the model?

like image 230
user972014 Avatar asked Oct 31 '18 14:10

user972014


People also ask

How to set a field as primary key in Django?

If you'd like to specify a custom primary key, specify primary_key=True on one of your fields. If Django sees you've explicitly set Field.primary_key , it won't add the automatic id column. Each model requires exactly one field to have primary_key=True (either explicitly declared or automatically added).

What is inheritance in Django?

In multi-table inheritance, each model corresponds to a database table. Django creates a OneToOneField field for the relationship in the child's model to its parent. Django would include an automatically generated OneToOneField field in the Text model and create a database table for each model.

What is proxy model in Django?

Proxy models allow us to change the Python behavior of a model without changing the database. vehicles/models.py. from django.db import models. class Car(models.Model): vin = models.CharField(max_length=17)

What does the model represent in Django?

Django web applications access and manage data through Python objects referred to as models. Models define the structure of stored data, including the field types and possibly also their maximum size, default values, selection list options, help text for documentation, label text for forms, etc.


2 Answers

Model.clean

One normally writes such tests in Model.clean [Django-doc]:

from django.core.exceptions import ValidationError

class Person(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    field1= models.IntegerField(null=True)
    field2 = models.IntegerField(null=True)

    def clean(self):
        super().clean()
        if self.field1 is None and self.field2 is None:
            raise ValidationError('Field1 or field2 are both None')

Note that this clean method is not validated by default when you .save() a model. It is typically only called by ModelForms built on top of this model. You can patch the .save() method for example like here to enforce validation when you .save() the model instance, but still there are ways to circumvent this through the ORM.

django-db-constraints (not supported by some databases)

If your database supports it (for example MySQL simply ignores the CHECK constraints), SQL offers a syntax to add extra constraints, and a Django package django-db-constraints [GitHub] provides some tooling to specify such constraints, like:

class Person(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    field1= models.IntegerField(null=True)
    field2 = models.IntegerField(null=True)

    class Meta:
        db_constraints = {
            'field_null': 'CHECK (field1 IS NOT NULL OR field2 IS NOT NULL)',
        }

Update: Django constraint framework

Since django-2.2, you can make use of the Django constraint framework [Django-doc]. With this framework, you can specify database constraints that are, given the database supports this, validated at database side. You thus can check if at least one of the two fields is not NULL with a CheckConstraint [Django-doc]:

class Person(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    field1= models.IntegerField(null=True)
    field2 = models.IntegerField(null=True)

    class Meta:
        constraints = [
            models.CheckConstraint(
                check=Q(field1__isnull=False) | Q(field2__isnull=False),
                name='not_both_null'
            )
        ]
like image 64
Willem Van Onsem Avatar answered Sep 27 '22 17:09

Willem Van Onsem


You can use Model.clean() method:

def clean(self):
    if self.field1 is None and self.field2 is None:
        raise ValidationError(_('field1 or field2 should not be null'))

See https://docs.djangoproject.com/en/2.1/ref/models/instances/#django.db.models.Model.clean

like image 42
Satevg Avatar answered Sep 27 '22 18:09

Satevg