Skip to main content

Adding Fields to Models With Pre-existing Data

When you add a field to a model that already has stored data, Django must populate that field with some value for the pre-existing data. For example, consider the following model:

class Course(models.Model):
name = models.CharField("Name", max_length=128)
enrolled_students = models.IntegerField("Students")

Which contains in the underlying database (let's say, SQLite) the following rows:

Model With Pre-existing Data

Now let's say you want to add the time field:

class Course(models.Model):
name = models.CharField("Name", max_length=128)
enrolled_students = models.IntegerField("Students")
TIMES_OF_DAY = (
(1, "Morning"),
(2, "Afternoon"),
(3, "Evening")
)
time = models.PositiveSmallIntegerField("Time", choices=TIMES_OF_DAY)

If you run python manage.py makemigrations myapp to create a new migration, Django will complain saying:

It is impossible to add a non-nullable field 'time' to course without specifying a default. This is because the database needs something to populate existing rows.
Please select a fix:
1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
2) Quit and manually define a default value in models.py.
Select an option:

This means that the pre-existing courses ("Advanced Python", "Java", "PHP" and "Django") will now have a new time column that cannot be empty (because the default value of the null argument in the definition of a field is False.) So, Django offers two solutions: either you enter a one-time value to populate the time field for all pre-existing courses, or you exit and modify models.py so that time has a default value or accepts null values, and run the makemigrations command again.

If you choose the first option, Django will launch an interactive console in which you will have to enter any value to populate the time field of the pre-existing courses:

Select an option: 1
Please enter the default value as valid Python.
The datetime and django.utils.timezone modules are available, so it is possible to provide e.g. timezone.now as a value.
Type 'exit' to exit this prompt
>>> 1

After this, continue with the migration by running python manage.py migrate myapp. Now in the database you will see that all pre-existing courses have time=1 ("Morning"):

Populated Field

The second option was to exit and modify the time definition to accept null values:

class Course(models.Model):
name = models.CharField("Name", max_length=128)
enrolled_students = models.IntegerField("Students")
TIMES_OF_DAY = (
(1, "Morning"),
(2, "Afternoon"),
(3, "Evening")
)
time = models.PositiveSmallIntegerField("Time", choices=TIMES_OF_DAY, null=True)

This lets you to create and apply the migration without problem, since pre-existing courses are simply filled with NULL (or the equivalent in the configured database engine):

Field Populated With NULL

This implies that all those courses, when retrieved as Python objects, will have time=None:

>>> from myapp.models import Course
>>> course_swift = Course.objects.get(name="Swift")
>>> course_swift.time is None
True

The third option was also to modify models.py, but by giving the default parameter:

class Course(models.Model):
name = models.CharField("Name", max_length=128)
enrolled_students = models.IntegerField("Students")
TIMES_OF_DAY = (
(1, "Morning"),
(2, "Afternoon"),
(3, "Evening")
)
time = models.PositiveSmallIntegerField("Time", choices=TIMES_OF_DAY, default=3)

Like null=True, this lets you make and run without problems. The new column will be populated with the specified default value:

Field Populated With Default Value

Besides migrations, Django will also use the default value when creating Course instances if an explicit value is not given to time. For example:

>>> from myapp.models import Course
>>> new_course = Course.objects.create(name="Java", enrolled_students=10)
>>> new_course.time
3

This also applies to forms generated from the Course model using a ModelForm. If the user does not submit a value for the time field, the default will be used.