What is up with blank and null in Django model fields?

How many times have you defined a model and thought how should I define my fields visa ve blank and true. Let me demystify this for you/.

Our model

Lets define an address model

class Address(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    address_line_one = models.CharField(blank=True, max_length=100)
    address_line_two = models.CharField(blank=True, max_length=100)
    address_line_three = models.CharField(blank=True, max_length=100)
    address_line_four = models.CharField(blank=True, max_length=100)
    town_or_city = models.CharField(blank=True, max_length=100)
    county = models.CharField(blank=True, max_length=100)
    postcode = models.CharField(blank=True, max_length=10)

Is this right? Lets answer a couple questions and then lets go through it field by field and maybe make some changes.

What do blank and null mean?

null is purely database-related, whereas blank is validation-related

So if null=True the database will accept no value or an empty value

If null=False the database will not accept an empty string or no value and it will blow up with an integrity error

If blank=True no validation will happen on this field when it is used in a form or in the django admin

If blank=False you will get required field errors in the django admin for your model and if using a model form.

What are the defaults

One of the first questions you can ask is what are the defaults? They are a helpful guide. Django being a framework is opinionated , so in its opinion the defaults are
null = False

blank = False

What are the usual combos?

null=False, blank=False

The default position and the safest :) which means if you define a field CharField( max_length=100) it the same as CharField(null=False, blank=False, max_length=100)

null=True, blank=False

This doesn't make any sense, a database field allowing null but the form not allowing empty or nothing. Crazy!!

null=False, blank=True

blank=True can be used with fields having null=False, but this will require implementing clean() on the model in order to programmatically supply any missing values.

null=True, blank=True

This makes sense for fields that sometimes have values but you are not concerned if they don't have. address_line_four is a great candidate for this.

One piece of advice in the docs is Avoid using null on string-based fields such as CharField and TextField. This is a good guide but sometimes we need to deviate from this to ensure data integrity.

Model revisited

So now with our new knowledge what should our model definition look like:

user = null=False, blank=False - Correct as I never want an address to exist that is not related to a user

address_line_one = null=False, blank=True - Incorrect in my case. I think that if an address exists it needs to have at least and address_line_one so I think this should be null=False, blank=False

address_line_two = null=False, blank=True - Incorrect. I am happy for this field to be empty in my database and my form fields. Not all addresses have a second line So it should be null=True, blank=True. This applies to all the other address lines

town_or_city = null=False, blank=True - Incorrect for my app. But may be correct in another app. Maybe the app you are building can have an address that does not belong to a city, like a caravan in a dessert. In my case my app is only interested in addresses that belong to addresses in UK cities. https://peoplevsparkingtickets.co.uk/ so I say null=False, blank=False

county = null=False, blank=True - Incorrect for me , my app is not so precious about the county so null=False, blank=False

postcode = null=False, blank=True - Incorrect. In the UK you cannot have and address without a postcode so null=False, blank=False

When do we use null=False, blank=True then? Say you wanted to store a calculated field or a field that was a combination of the other fields.

say postcode_prefix then that would be a good time to use it.

So our model now becomes:

class Address(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    address_line_one = models.CharField(max_length=100)
    address_line_two = models.CharField(null=True, blank=True, max_length=100)
    address_line_three = models.CharField(null=True, blank=True, max_length=100)
    address_line_four = models.CharField(null=True, blank=True, max_length=100)
    town_or_city = models.CharField(max_length=100)
    county = models.CharField(null=True, blank=True, max_length=100)
    postcode = models.CharField(max_length=10)
    postcode_prefix = models.CharField(null=False, blank=True, max_length=4)

Hope that helps and please do let me know on twitter @chriswedgwood if I have got something wrong :) #alwayslearning