Search code examples
pythondjangodjango-modelsdjango-model-field

No exception raised when saving an empty field with null=False - Django


I have the following model:

class User(models.Model):
    email = models.EmailField(max_length=254, null=False, unique=True)
    referral_code = models.CharField(max_length=10, null=False, unique=True)

And used the Django shell to save a user instance with referral_code undefined:

u = User(email="[email protected]")
u.save()

This did not raise an exception. My understanding was that null=False would require referral_code to be set - Is this not the case? How would I achieve that behaviour?

update

I notice that the field is set to u.referral_code='', so given the uniqueness constraint, this has raised an exception when I tried to repeat the process with a new instance. I would rather the exception was thrown because I did not set the field...


Solution

  • The value of your referral_code is not null, it is '' (blank string). This is the default of CharField.


    Updated along with question:

    You can raise an error before the data is stored in the database by overriding save on the model

    class User(models.Model):
        email = models.EmailField(max_length=254, null=False, unique=True)
        referral_code = models.CharField(max_length=10, null=False, unique=True)
    
        def save(self, *args, **kwargs):
            assert self.email, "The 'email' field must be populated."
            super().save(*args, **kwargs)
    

    It should be noted that this is not to be preferred over form validation where possible.


    Update now that newer features of Django are available:

    Django's Constraints allow you to add custom database constraints:

    class User(models.Model):
        email = models.EmailField(max_length=254, null=False, unique=True)
        referral_code = models.CharField(max_length=10, null=False, unique=True)
    
        class Meta:
            constraints = [
                models.CheckConstraint(
                    check=(~models.Q(referral_code='')),
                    name='referral_code_populated',
                )
            ]