[Fixed]-When to use NullBooleanField in Django

41👍

The question you need to answer to find out whether you should use the BooleanField or the NullBooleanField is actually concerning the possible states of the value of the field you want to represent in your model:

2 possible states:

  • user has drunk water
  • user has not drunk water

β†’ use BooleanField

3 possible states:

  • user has drunk water
  • user has not drunk water
  • it is not known whether the user has or has not drunk water

β†’ use NullBooleanField.

UPDATE:

NullBooleanField is deprecated in version 3.1. Instead use BooleanField with null=True.

10👍

Django 2.1 introduced null=True for BooleanField. Using NullBooleanField is now discouraged.

So use, x = BooleanField(null=True) instead of x = NullBooleanField()

Here’s a simple use case: If you only need to record the “Yes” or “No” status, use Boolean without null. But if you want to have 3 conditions say, “Yes”, “No”, and “Don’t Know”, use it with null=True.

1👍

I think you should use NullBooleanField only when you have three possible choices: Unknown, Yes (True) and No (False).

In your case you have only two possible values – Yes (user has drunk water) and No (user has NOT drunk water) so a BooleanField would be better.

One more reason to use a BooleanField in your case is because the default form widget for this field is a CheckboxInput (docs), while the default form widget for a NullBooleanField is a NullBooleanSelect (docs). And since you use a checkbox, a BooleanField would do the job better.

0👍

Take advantage of the NULL properties

I use it quite often when I need to enforce some specific constrains in my data, but allow some others. Multiple NULL values can coexist in a column defined UNIQUE. Let’s take an address model implementation as an example:

The business rules are:

  • A user can have up to 1 billing address
  • A user can have multiple shipping addresses

One way to implement that is by making a single address table with a foreign key to the user and an extra flag that indicates if that address is a billing address or not:

class Address(models.Model):
    ...  # <- address fields

    user = models.ForeignKey(User, on_delete=models.CASCADE)
    billing_address = models.NullBooleanField(default=None)

You can now simply enforce the business rules at a database level by making user and billing_address unique together.:

class Meta:
    constraints = [
        models.UniqueConstraint(
            fields=['user', 'billing_address'],
            name='ensure single billing address'
        )
    ]

The trick to make this work is that the billing_address must be True when the address is a billing address but it should be None (instead of False) when the address is a shipping address.

You can further enforce the validation by adding another constraint to make sure that no False values are added. But this is usually not necessary and could be done at the application level:

class Meta:
    constraints = [
        models.UniqueConstraint(
            fields=['user', 'billing_address'],
            name='ensure single billing address'
        ),
        models.CheckConstraint(
            check=~Q(billing_address=False),
            name='no False allowed'
        )
    ]

0👍

Biggest advantage of using NullBooleanField for PostgreSQL database is your table won’t be re-written, any new field without null=True will cause a table re-write, it’s fine for small tables but could take a significant amount of time and resource for large tables and you won’t be able to write to your table during a re-write

👤ahmed

Leave a comment