Django’s Adding non-nullable field 'X' without a default error means your database schema is out of sync with your Django model definition because you’re trying to add a required field to an existing table, but Django doesn’t know what value to put in that field for rows that already exist.

Here’s a breakdown of the common causes and how to fix them:

Cause 1: Missing Default Value in Model Definition

This is the most straightforward cause. You added a CharField, IntegerField, or any other non-nullable field to your model but forgot to specify a default value.

Diagnosis: Look at your model definition in models.py. You’ll see a field declared like this:

class MyModel(models.Model):
    existing_field = models.CharField(max_length=100)
    new_required_field = models.IntegerField() # <-- Missing default

Fix: Add a default argument to the field definition.

class MyModel(models.Model):
    existing_field = models.CharField(max_length=100)
    new_required_field = models.IntegerField(default=0) # Or any sensible default

Why it works: When Django generates the migration and applies it, it adds the new column to the database. The default value tells the database (or Django during the migration) what to populate for all existing rows in that column.

Cause 2: Incorrectly Adding a Nullable Field First, Then Making it Non-Nullable

Sometimes, you might initially add a field as nullable (null=True) to get it into the database, then later decide it should be required. If you then remove null=True without a default, you’ll hit this error.

Diagnosis: Your models.py might look like this:

class MyModel(models.Model):
    existing_field = models.CharField(max_length=100)
    new_required_field = models.IntegerField(null=True) # Initially nullable

And then later changed to:

class MyModel(models.Model):
    existing_field = models.CharField(max_length=100)
    new_required_field = models.IntegerField() # Now non-nullable, but no default

Fix: When you make a field non-nullable again, you must provide a default.

class MyModel(models.Model):
    existing_field = models.CharField(max_length=100)
    new_required_field = models.IntegerField(default=0) # Add default

Why it works: Similar to Cause 1, the default ensures existing rows get a value. Django’s migration system tracks these changes and applies them correctly.

Cause 3: Using a Callable Default That Doesn’t Work for Migrations

You might use a callable (like a function or timezone.now) for your default, but if it’s not properly handled, migrations can fail.

Diagnosis: A model might have a default like this:

from django.utils import timezone

class MyModel(models.Model):
    created_at = models.DateTimeField(default=timezone.now) # This is usually fine, but can cause issues in complex scenarios

The problem often arises if the callable relies on external factors or context that isn’t available during the migration generation or application. For instance, a custom function that reads a configuration file might fail if that file isn’t present when makemigrations runs.

Fix: For migrations, it’s often safer to use simple, static defaults or a default value that can be directly translated. If you must use a callable that has external dependencies, consider a two-step migration:

  1. Add the field as nullable.
  2. Update the field with a default value for existing rows, and then make it non-nullable in a separate migration.

Alternatively, for simple callables like timezone.now, ensure your settings.py is configured correctly and that makemigrations can access the necessary modules. If the callable is complex, consider creating a migration operation to populate the field.

Example of a two-step fix:

Migration 1 (0001_initial.py or similar):

# Add the field as nullable
migrations.AddField(
    model_name='mymodel',
    name='new_field',
    field=models.IntegerField(null=True),
),

Migration 2 (0002_...py):

from django.db import migrations, models

def populate_new_field(apps, schema_editor):
    MyModel = apps.get_model('myapp', 'MyModel')
    # Populate with a sensible default for existing rows
    MyModel.objects.filter(new_field__isnull=True).update(new_field=0)

class Migration(migrations.Migration):
    dependencies = [
        ('myapp', '0001_initial'),
    ]
    operations = [
        migrations.RunPython(populate_new_field),
        migrations.AlterField(
            model_name='mymodel',
            name='new_field',
            field=models.IntegerField(default=0), # Now set default and make non-nullable
        ),
    ]

Why it works: The RunPython operation allows you to execute custom Python code within a migration. This lets you explicitly handle the population of existing rows before the schema change that makes the field non-nullable.

Cause 4: Database-Level Constraints vs. Django Migrations

This error can also occur if you’ve manually altered your database schema (e.g., via psql or mysql command line) to add a NOT NULL constraint to a column that Django doesn’t know about, or if Django’s migration history is out of sync with the actual database.

Diagnosis: Use your database’s introspection tools. For PostgreSQL:

\d myapp_mymodel;

Look for the column in question. If it shows NOT NULL and you haven’t defined a default in your Django model for it, this is the issue.

Fix: Option A: Add a default to your Django model and regenerate migrations. Option B: If you intended the field to be nullable, remove the NOT NULL constraint from the database. Option C: If you intended the field to be non-nullable and have a default, apply the default in the database directly and then ensure your Django model reflects this with a default value.

Why it works: Django’s migration system relies on a consistent view of the database schema. Direct database modifications without corresponding Django model changes or migration updates will lead to discrepancies and errors like this.

Cause 5: Complex Data Types or Custom Fields

If your non-nullable field is a complex type (like a JSONField with specific requirements) or a custom field, the default might not be handled as expected by the migration system.

Diagnosis: Review the definition of your custom or complex field. Ensure its default attribute is correctly implemented and compatible with Django’s migration framework. Check if the field’s __init__ or deconstruct methods are correctly handling default values.

Fix: For built-in complex fields like JSONField or ArrayField, ensure you provide a valid default (e.g., default=dict or default=list for empty structures). For custom fields, consult Django’s documentation on creating custom model fields and migrations. You might need to implement specific logic in your field’s deconstruct method or use a RunPython migration operation as described in Cause 3.

Why it works: Custom or complex field types require careful handling of their state and defaults, especially during schema changes. Ensuring these are correctly defined and integrated with Django’s migration process resolves the conflict.

Cause 6: Race Conditions or Concurrent Migrations

In environments with multiple developers or CI/CD pipelines, it’s possible for migrations to run concurrently or for a developer to apply local migrations that conflict with migrations pushed by others.

Diagnosis: Check your Git history and the django_migrations table in your database. See if multiple migration files were generated or applied around the same time, especially ones affecting the same model.

Fix: This often requires coordination.

  1. Identify the conflicting migrations.
  2. Communicate with your team.
  3. Consolidate migrations if possible.
  4. Use manage.py migrate --fake carefully if you know a migration has already been applied to the database but Django’s history doesn’t reflect it. This command tells Django to mark a migration as applied without actually running its SQL, which can fix the history but should be used with extreme caution.
  5. Ensure your settings.py uses DATABASES['default']['TEST'] correctly to avoid issues when running tests.

Why it works: By ensuring a single, ordered, and correctly applied sequence of migrations, you maintain the integrity of the database schema and Django’s understanding of it, preventing conflicts.

The next error you’ll likely encounter after fixing this is a RelatedObjectDoesNotExist if you’ve implemented ForeignKey fields without proper on_delete behavior or if referential integrity issues arise from previous data import problems.

Want structured learning?

Take the full Django course →