﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
36167	Default value in migrations allows None/NULL	ivasilyev1	ivasilyev1	"When adding/altering a non-null model field the prompt for default value allows user input of string `""None""`.  Django then creates a migration with `default_value=None` and everything seems ok.

If there is existing data in the DB and the generated migration is attempted to be applied, a runtime constraint error will be thrown.

Simple proof:

 - Create test model with `null=True` field

{{{
class TestModel(models.Model):
    test_field = models.TextField(null=True, help_text='Test field')
}}}

and `makemigrations` to create initial migration

{{{
class Migration(migrations.Migration):
    initial = True
    dependencies = [
    ]
    operations = [
        migrations.CreateModel(
            name='TestModel',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('test_field', models.TextField(help_text='Test field', null=True)),
            ],
        ),
    ]
}}}

- Create any arbitrary dummy record

{{{
>>> from testapp.models import *
>>> TestModel().save()
>>> exit()
}}}

- Switch the field to `null=False` and `makemigrations` again, selecting the default value option and entering `None`

{{{
$ python3 manage.py makemigrations
It is impossible to change a nullable field 'test_field' on testmodel to non-nullable without providing 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) Ignore for now. Existing rows that contain NULL values will have to be handled manually, for example with a RunPython or RunSQL operation.
 3) Quit and manually define a default value in models.py.
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
>>> None
Migrations for 'testapp':
  testapp/migrations/0002_alter_testmodel_test_field.py
    ~ Alter field test_field on testmodel
}}}

which creates the following migration without error

{{{
    operations = [
        migrations.AlterField(
            model_name=""testmodel"",
            name=""test_field"",
            field=models.TextField(default=None, help_text=""Test field""),
            preserve_default=False,
        ),
    ]
}}}

- Apply the new migration with `migrate`

{{{
python3 manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions, testapp
Running migrations:
  Applying testapp.0002_alter_testmodel_test_field...Traceback (most recent call last):
...
django.db.utils.IntegrityError: column ""test_field"" of relation ""testapp_testmodel"" contains null values
}}}


A bit of an edge case because if there is no data present in the table it seems to work fine. But the combination of `default=None` doesn't really make sense to be allowed in the first place if the goal is to prevent NULL values.

The default value prompter should probably not allow this input"	Uncategorized	closed	Uncategorized	5.1	Normal	wontfix			Unreviewed	1	0	1	0	1	0
