Migrations
Django does not change the database immediately when we edit a model.
The flow is:
models.py
-> makemigrations
-> migration file
-> migrate
-> database table1. Write Or Change The Model
Example file:
src/organizations/models.pyExample model:
import uuid
from django.db import models
class Organization(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
name = models.CharField(max_length=255)
slug = models.SlugField(max_length=255, unique=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.nameWhy:
idis a UUID, so the public API does not expose integer IDs like1,2,3.nameis the visible organization name.slugis a URL-friendly unique identifier.created_atis set once when the row is created.updated_atchanges every time the row is saved.__str__controls how the object appears in Django admin and shell.
2. Create Migration
uv run python src/manage.py makemigrationsOr for one app:
uv run python src/manage.py makemigrations organizationsThis creates a file like:
src/organizations/migrations/0001_initial.pyImportant: makemigrations does not change the database. It creates a migration file.
3. Apply Migration
uv run python src/manage.py migrateThis changes the database.
For Organization, it creates a table like:
organizations_organization4. Check Migration Status
uv run python src/manage.py showmigrationsChecked migrations look like:
[X] 0001_initialUnchecked migrations look like:
[ ] 0001_initialImportant Notes
Migration files are source code. Commit them to git.
The local SQLite database is not source code. Do not commit it:
src/db.sqlite3If you change a model, run:
uv run python src/manage.py makemigrations
uv run python src/manage.py migrateChanging primary key type is sensitive.
Example:
integer id -> UUID idIf rows already exist, this can break local data unless handled carefully.
For local learning data, it is sometimes okay to reset the local database. For production data, never delete the database; write a careful migration plan.
Migrations must remain portable across every database backend the project claims
to support. Run them against SQLite and PostgreSQL when a change depends on
database behavior. Changing DATABASE_URL selects another database; it does not
move existing records between databases.
Membership Migration Note
Migration organizations.0002_organizationmembership introduces access control.
It deliberately does not assign owners to organizations that existed beforehand,
because the old schema contains no reliable user-to-organization relationship.
After applying it to an existing development database, use a Django superuser in
/admin/ to create one owner membership for each existing organization.
Database Setup
Bubllio CRM uses Django's ORM and database backends. Application code does not contain custom database adapters. Developers select a supported backend through one environment variable: DATABASEURL.
Test first-run onboarding locally
Use the onboarding sandbox when you need to repeat the initial administrator, workspace, and SMTP flow. It never reads or changes the normal development database.
