Migrations Using Alembic
Although Pydbantic can handle migrations automatically, alembic may also be used if preferred. Configuration can be done in a few easy steps
Install alembic
Generally no need to install alembic, as it is also a dependency of automatic migrations with pydbantic
Define Models & DB instance
#models.py
from pydbantic import DataBaseModel, PrimaryKey, Default
def time_now_str():
return datetime.now().isoformat()
def stringify_uuid():
return str(uuid.uuid4())
class Employee(DataBaseModel):
id: str = PrimaryKey(default=stringify_uuid)
salary: float
is_employed: bool
date_employed: str = Default(default=time_now_str)
Connect with Database
#db.py
from pydbantic import Database
from models import Employee
db = Database.create(
'sqlite:///company.db',
tables=[Employee],
use_alembic=True
)
Initialize alembic
alembic init migrations
Within the current directory, alembic will create a migrations
folder that will store versions
and its env.py
that will need to updated. We will remove most of the boiler plate code and simply import db
and use db.alembic_migrate()
.
Update migrations/env.py
file
#migrations/env.py
from alembic import context
from db import db
def run_migrations_offline() -> None:
db.alembic_migrate()
def run_migrations_online() -> None:
db.alembic_migrate()
if context.is_offline_mode():
run_migrations_offline()
else:
run_migrations_online()
Creating the first migration file
Until now, we have just told alembic where to store our migrations, and where our db is configured. We still have not created any database tables to match our Employee
model.
alembic revision -m "init_migration" --autogenerate
Alembic is capable of detecting schema changes and often can do most of the work to build your migrations. Notice the new file in migrations/versions/
matching the -m "init_migration"
commit message.
Trigger Migration
The final step once we have created a migration file is to trigger the migration. The instructions defined in the latest migrations/versions
will be followed.
alembic upgrade head
Run Application
Now we are all set, we can Create new persistent instances of our model and trust they are safely stored.
#app.py
import asyncio
from db import db
from models import Employee
async def main():
await Employee.create(
salary=10000,
is_employed=True
)
all_employees = await Employee.all()
print(all_employees)
asyncio.run(main())
Adding a new Model
# models.py
import uuid
from datetime import datetime
from typing import Optional, Union
from pydantic import BaseModel
from pydbantic import DataBaseModel, PrimaryKey, Default
def time_now_str():
return datetime.now().isoformat()
def stringify_uuid():
return str(uuid.uuid4())
class Positions(DataBaseModel):
name: str = PrimaryKey()
level: int = 4
class Employee(DataBaseModel):
id: str = PrimaryKey(default=stringify_uuid)
salary: float
is_employed: bool
date_employed: str = Default(default=time_now_str)
position: Union[Positions, None] = Positions(name='Manager')
Connect to database
#db.py
from pydbantic import Database
from models import Employee, Positions
db = Database.create(
'sqlite:///company.db',
tables=[Employee, Positions],
use_alembic=True
)
Create new revision
alembic revision -m "added Positions" --autogenerate
Migrate!
alembic upgrade head