From c39febd54d67689938b3c5325fac73e9360e5438 Mon Sep 17 00:00:00 2001 From: ryankwondev Date: Fri, 19 Apr 2024 19:22:18 +0900 Subject: [PATCH 1/2] :sparkles: Docs: Schema Migrations with Alembic --- docs/advanced/migrations.md | 98 ++++++++++++++++++++ docs_src/advanced/migrations/__init__.py | 0 docs_src/advanced/migrations/tutorial001._py | 27 ++++++ docs_src/advanced/migrations/tutorial002._py | 81 ++++++++++++++++ mkdocs.yml | 1 + 5 files changed, 207 insertions(+) create mode 100644 docs/advanced/migrations.md create mode 100644 docs_src/advanced/migrations/__init__.py create mode 100644 docs_src/advanced/migrations/tutorial001._py create mode 100644 docs_src/advanced/migrations/tutorial002._py diff --git a/docs/advanced/migrations.md b/docs/advanced/migrations.md new file mode 100644 index 000000000..d79250b30 --- /dev/null +++ b/docs/advanced/migrations.md @@ -0,0 +1,98 @@ +# Schema Migrations using Alembic + +SQLModel integrates with [Alembic](https://alembic.sqlalchemy.org/) to handle schema migrations. +Alembic is a lightweight database migration tool for usage with SQLAlchemy. +Since SQLModel is built on top of SQLAlchemy, it's easy to use Alembic with SQLModel. + +## Installation + +To use Alembic with SQLModel, first install it: + +
+ +```console +$ pip install alembic +---> 100% +Successfully installed alembic +``` + +
+ +Then, initialize Alembic in your project directory: + +```console +alembic init migrations +``` + +This will create a directory named `migrations` and a configuration file named `alembic.ini`. + +/// info + +`migrations` is the directory where Alembic will store the migration scripts. +You can choose any other name for this directory, but `migrations` is a common convention. + +/// + +## Integration + +By making `class Table(SQLModel, table=true)`, you can add tables' information to SQLModel(SQLAlchemy) Metadata. + +/// info + +Metadata is a container object that keeps together many different features of a database. +You can access [Working with Database Metadata](https://docs.sqlalchemy.org/en/20/core/metadata.html) for more information. + +/// + +Import SQLModel on `./migrations/script.py.mako` and add the following code: + +```python hl_lines="12" +{!./docs_src/advanced/migrations/tutorial001._py[ln:1-17]!} + +# More code here later 👇 +``` + +/// details | 👀 Full file preview + +```Python hl_lines="12" +{!./docs_src/advanced/migrations/tutorial001._py!} +``` + +/// + +Next, load your models and set the target metadata on `./migrations/env.py`. + +```python hl_lines="7 9 24" +{!./docs_src/advanced/migrations/tutorial002._py[ln:1-29]!} + +(...) +``` + +Lastly, set the database connection string in `./alembic.ini`. + +```python +# around line 63 +sqlalchemy.url = driver://user:pass@localhost/dbname +``` + +## Revise and Upgrade + +After setting up Alembic, you can create a new revision: + +```console +alembic revision --autogenerate -m "create table" +``` + +This will create a new revision file in `./migrations/versions/`. + +To apply the new revision and update the database schema, run: + +```console +alembic upgrade head +``` + +/// tip + +Remember to run `alembic upgrade head` to update the remote database's schema. + +/// \ No newline at end of file diff --git a/docs_src/advanced/migrations/__init__.py b/docs_src/advanced/migrations/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/docs_src/advanced/migrations/tutorial001._py b/docs_src/advanced/migrations/tutorial001._py new file mode 100644 index 000000000..6ce335109 --- /dev/null +++ b/docs_src/advanced/migrations/tutorial001._py @@ -0,0 +1,27 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +import sqlmodel +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision: str = ${repr(up_revision)} +down_revision: Union[str, None] = ${repr(down_revision)} +branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)} +depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)} + + +def upgrade() -> None: + ${upgrades if upgrades else "pass"} + + +def downgrade() -> None: + ${downgrades if downgrades else "pass"} diff --git a/docs_src/advanced/migrations/tutorial002._py b/docs_src/advanced/migrations/tutorial002._py new file mode 100644 index 000000000..5033ed996 --- /dev/null +++ b/docs_src/advanced/migrations/tutorial002._py @@ -0,0 +1,81 @@ +from logging.config import fileConfig + +from sqlalchemy import engine_from_config +from sqlalchemy import pool + +from alembic import context +from sqlmodel import SQLModel + +from app.models import * + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +if config.config_file_name is not None: + fileConfig(config.config_file_name) + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +target_metadata = SQLModel.metadata + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def run_migrations_offline() -> None: + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + ) + + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online() -> None: + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + connectable = engine_from_config( + config.get_section(config.config_ini_section, {}), + prefix="sqlalchemy.", + poolclass=pool.NullPool, + ) + + with connectable.connect() as connection: + context.configure( + connection=connection, target_metadata=target_metadata + ) + + with context.begin_transaction(): + context.run_migrations() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/mkdocs.yml b/mkdocs.yml index fa85062a8..cca422f38 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -99,6 +99,7 @@ nav: - Advanced User Guide: - advanced/index.md - advanced/decimal.md + - advanced/migrations.md - alternatives.md - help.md - contributing.md From a58a2c9266ef68d0cd6f94b4bee7188cdf65c5e2 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 19 Apr 2024 10:26:00 +0000 Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=8E=A8=20[pre-commit.ci]=20Auto=20for?= =?UTF-8?q?mat=20from=20pre-commit.com=20hooks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/advanced/migrations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/advanced/migrations.md b/docs/advanced/migrations.md index d79250b30..cb2ee163d 100644 --- a/docs/advanced/migrations.md +++ b/docs/advanced/migrations.md @@ -95,4 +95,4 @@ alembic upgrade head Remember to run `alembic upgrade head` to update the remote database's schema. -/// \ No newline at end of file +///