Django ManyToMany table missing even though migrations are applied (Docker + Postgres)
Problem
I am working on a Django project using a PostgreSQL container in Docker. I added a ManyToManyField to one of my models. I keep migrations in .gitignore because my local and production databases are different, so I only pushed the model changes to GitHub. On my server, I pulled the code and ran docker compose up, which automatically runs makemigrations and migrate. There were no errors or warnings, and the generated migration file correctly includes the ManyToMany Field. Django shows the migration as applied. However, when I try to access the ManyToMany relation at runtime, I get a database error saying the join table does not exist.
Environment
- Django
- PostgreSQL
- Docker
Model
class Contact(models.Model):
...
class CallCampaign(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
error = models.TextField(blank=True, null=True)
company = models.ForeignKey(Company, on_delete=models.CASCADE)
selected_contacts = models.ManyToManyField('Contact', blank=True)
...
Migration
The migration file includes the ManyToManyField:
migrations.CreateModel(
name='CallCampaign',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('error', models.TextField(blank=True, null=True))
('company', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='companies.company')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
('selected_contacts', models.ManyToManyField(blank=True, to='contacts.contact')),
],
),
The migration is marked as applied in the database.
What happens at runtime
>>> from contacts.models import CallCampaign
>>> c = CallCampaign.objects.first()
>>> c.selected_contacts
<ManyRelatedManager object at 0x...>
>>> c.selected_contacts.all()
Error:
django.db.utils.ProgrammingError:
relation "contacts_callcampaign_selected_contacts" does not exist
LINE 1: ..."."created_at" FROM "contacts_contact" INNER JOIN "contacts_...
Database tables
Existing tables:
contacts_callcampaign
contacts_contact
contacts_callhistory
Missing table:
contacts_callcampaign_selected_contacts
Migration status
The django_migrations table shows:
app | name | applied
--------|--------------|--------
contacts| 0001_initial | 2025-12-29 16:21:00
Commands I ran:
python manage.py makemigrations
# No changes detected
python manage.py migrate
# No migrations to apply
Django does not attempt to create the missing ManyToMany table.
Docker setup (simplified)
services:
db:
image: postgres:15
volumes:
- postgres_data:/var/lib/postgresql/data/
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
web:
build: .
command: >
sh -c "
python manage.py makemigrations --noinput &&
python manage.py migrate --noinput
"
depends_on:
db:
condition: service_healthy
volumes:
postgres_data:
The Postgres volume is persistent across restarts and rebuilds.
Questions
- How can a Django migration be marked as applied if the ManyToMany table was not created?
- Is this expected behavior in Django migrations? (i.e., no atomicity guarantee for the entire migration)
- How can this be prevented when using Docker with persistent databases?
- What can i do now?
I want to understand why this happens and what the correct way is for Django migrations and ManyToMany tables so this does not occur in the future.