Fixing Django IntegrityError: null value in column violates not-null constraint

Fixing Django IntegrityError: null value in column violates not-null constraint

While working on a Django project, I ran into an issue that resulted in this error:


django.db.utils.IntegrityError: null value in column "message_id" of relation "mailing_sending" violates not-null constraint
DETAIL:  Failing row contains (..., null, null, ...).
        

This happened while calling delete() on a model instance. Django was trying to set a foreign key field to NULL as part of a cascading delete, but the database rejected it due to a NOT NULL constraint.

Root Cause

In our Django model, we had defined a nullable foreign key:


message = models.ForeignKey(Message, on_delete=models.SET_NULL, null=True)
        

However, in the PostgreSQL database, the corresponding column message_id was still marked as NOT NULL:


message_id | integer |           | not null |
        

This mismatch between the model and the actual schema caused the IntegrityError on deletion.

The Fix

Option 1: Create and Apply the Correct Migration

To resolve this properly through Django:

  1. Ensure the model defines null=True and on_delete=models.SET_NULL.
  2. Create a manual migration if Django doesn’t detect any changes:
  3. python manage.py makemigrations --empty mailing
  4. Edit the migration file to include:
  5. 
    migrations.AlterField(
        model_name='sending',
        name='message',
        field=models.ForeignKey(
            to='mailing.message',
            on_delete=models.SET_NULL,
            null=True,
        ),
    )
                
  6. Then run the migration:
  7. python manage.py migrate

Option 2: Fix Directly in the Database

If you prefer a quick fix at the database level, just execute:


ALTER TABLE mailing_sending
ALTER COLUMN message_id DROP NOT NULL;
        

This brings the schema in line with the model.

Conclusion

This is a classic example of how model changes don’t take effect until migrations are created and applied. Always make sure your database constraints match your Django model definitions to avoid surprises like this one!

Bonus Tip: Use makemigrations and migrate after every model change — and check your database schema if things don't behave as expected.

Comments