2

When I define a non nullable field in django it allows me to save a model instance without specifying any value for this non-nullable field. This is not what I would expect. How can I force this to yield an error?

Postgres 9.1 django 2.1 windows python 3.6

from django.db import models
class Wwtp(models.Model):
    name = models.CharField(max_length=100, null=False,
                            blank=False, unique=True)
    short_name = models.CharField(
        max_length=10, null=False, blank=False, unique=True)

As expected, I am not allowed to save it with an explicit empty short_name.

mdl.Wwtp.objects.create(name='Wwtp4', short_name=None)

But I am allowed to save an instance of Wwtp without specifying short_name:

mdl.Wwtp.objects.create(name='Wwtp4')

and when I try:

mdl.Wwtp.objects.create()

it gives me

django.db.utils.IntegrityError: duplicate key value violates unique constraint "api_wwtp_short_name_key"
DETAIL:  Key (short_name)=() already exists.

Apparently django filled the database with an empty value for short_name while it is not allowed to do so... How can I force the database to not allow this?

Martin
  • 55
  • 9

1 Answers1

2

You can't with CharField. The empty value is an empty string '', not NULL. You already have blank=False set, so if you clean your model or model forms before saving them, you'll catch that. But it cannot be enforced at the database level.

Note that blank=False, null=False is the default, so you really don't have to specify that.

Also, if you really only want to support PostgreSQL, you could make a custom migration using RunSQL to create your column on the table, manually adding the SQL needed to add the constraint (e.g. using CHECK). See here for how to ensure Django also knows the column was created and doesn't try to add it in the next migration. There's an example here.

[Edit] In Django 2.2, you can add a CheckConstraint in the model's Meta class constraints attribute:

from django.db.models import CheckConstraint, Q

    (...)
    class Meta:
        constraints = [
            CheckConstraint(
                check=~Q(name=''), 
                name='name_not_empty'),
            CheckConstraint(
                check=~Q(short_name=''),
                name='short_name_not_empty']
dirkgroten
  • 20,112
  • 2
  • 29
  • 42
  • Thanks for your answer. Indeed it only seems to hold for CharFields; integer fields and datetimefields give errors on calling save on model that lacks them. When I call clean_fields() on that model it also raises exceptions; why is clean_fields() not called on save()? – Martin Jun 04 '19 at 11:28
  • Because that might be too restrictive in certain cases. And clean is supposed to be called in order to warn the user about wrong input, when saving it’s too late. You can then only “crash” and throw an exception. You want to use a ModelForm (the admin site does it), validate your form and return a friendly user response. – dirkgroten Jun 04 '19 at 12:04
  • Also re your 1st comment, IntegerField and DateTimeFields are NULL when empty in the db. There is no such thing as '' for them. – dirkgroten Jun 04 '19 at 12:05