27
loading...
This website collects cookies to deliver better user experience
SQLAlchemy is the Python SQL toolkit and Object Relational Mapper that gives application developers the full power and flexibility of SQL.
It provides a full suite of well-known enterprise-level persistence patterns, designed for efficient and high-performing database access, adapted into a simple and Pythonic domain language. - from official docs
⚠️ The project pydantic
and my Twitter handle @pydantic
are not associated, just saying.
$ mkdir fastapi-ormar-alembic && cd $_
$ mkdir .venv
$ pipenv install fastapi uvicorn ormar alembic aiosqlite
⚠️ Pinned dependencies are available in the Piplock file in the source code repository.
db.py
, in the root of the project. This file will contain the database setup and an example table.import databases
import ormar
import sqlalchemy
database = databases.Database("sqlite:///db.sqlite")
metadata = sqlalchemy.MetaData()
class BaseMeta(ormar.ModelMeta):
database = database
metadata = metadata
class Users(ormar.Model):
class Meta(BaseMeta):
tablename = "users"
id: int = ormar.Integer(primary_key=True)
email: str = ormar.String(max_length=64, unique=True)
password: str = ormar.String(max_length=128)
BaseMeta
lets you add the database
and metadata
variables to all your models. (fastapi-ormar-alembic) $ alembic init migrations
├── Pipfile
├── Pipfile.lock
├── alembic.ini
├── db.py
├── db.sqlite
├── main.py
└── migrations
alembic.ini
file. You're actually modifying a single like:sqlalchemy.url = sqlite:///db.sqlite
migrations/env.py
:...
from db import BaseMeta
...
target_metadata = BaseMeta.metadata
--autogenerate
option to generate migrations based on the metadata automatically:(fastapi-ormar-alembic) $ alembic revision --autogenerate -m "Added users table"
INFO [alembic.runtime.migration] Context impl SQLiteImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
INFO [alembic.autogenerate.compare] Detected added table 'users'
Generating /home/amalshaji/Workspace/Python/blog-code-repository/fastapi-ormar-
alembic/migrations/versions/c07fe5d55962_added_users_table.py ... done
(fastapi-ormar-alembic) $ alembic upgrade head
migrations/versions/c07fe5d55962_added_users_table.py
File name inferred from the migration output.
"""Added users table
Revision ID: c07fe5d55962
Revises:
Create Date: 2021-08-14 11:55:46.845709
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'c07fe5d55962'
down_revision = None
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('users',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('email', sa.String(length=64), nullable=False),
sa.Column('password', sa.String(length=128), nullable=False),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('email')
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('users')
# ### end Alembic commands ###
# db.py
class Users(ormar.Model):
class Meta(BaseMeta):
tablename = "users"
id: int = ormar.Integer(primary_key=True)
email: str = ormar.String(max_length=64, unique=True)
password: str = ormar.String(max_length=128)
is_active: bool = ormar.Boolean(default=True) # new
(fastapi-ormar-alembic) $ alembic revision --autogenerate -m "Added is_active to users table"
INFO [alembic.runtime.migration] Context impl SQLiteImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
INFO [alembic.autogenerate.compare] Detected added column 'users.is_active'
Generating /home/amalshaji/Workspace/Python/blog-code-repository/fastapi-ormar-
alembic/migrations/versions/026a9a23ebbe_added_is_active_to_users_table.py ... done
(fastapi-ormar-alembic) $ alembic upgrade head
migrations/versions/026a9a23ebbe_added_is_active_to_users_table.py
:"""Added is_active to users table
Revision ID: 026a9a23ebbe
Revises: c07fe5d55962
Create Date: 2021-08-14 12:20:36.817128
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '026a9a23ebbe'
down_revision = 'c07fe5d55962'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('users', sa.Column('is_active', sa.Boolean(), nullable=True))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('users', 'is_active')
# ### end Alembic commands ###
$ sqlite3 db.sqlite
SQLite version 3.31.1 2020-01-27 19:55:54
Enter ".help" for usage hints.
sqlite> .schema users
CREATE TABLE users (
id INTEGER NOT NULL,
email VARCHAR(64) NOT NULL,
password VARCHAR(128) NOT NULL, is_active BOOLEAN,
PRIMARY KEY (id),
UNIQUE (email)
);
sqlite> .quit
from fastapi import FastAPI
from db import database
app = FastAPI()
@app.on_event("startup")
async def startup():
if not database.is_connected:
await database.connect()
@app.on_event("shutdown")
async def shutdown():
if database.is_connected:
await database.disconnect()
db.py
with the following BaseMeta
definition....
class BaseMeta(ormar.ModelMeta):
database = database
metadata = metadata
...
accounts/models.py
class Users(ormar.Model):
class Meta(BaseMeta):
tablename = "users"
...
posts/models.py
class Posts(ormar.Model):
class Meta(BaseMeta):
tablename = "posts"
...
env.py
...
from accounts.models import Users
from posts.models import Posts
from db import BaseMeta
...
target_metadata = BaseMeta.metadata