Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 86 additions & 26 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
services:
postgres:
image: ankane/pgvector:latest
image: postgres
ports:
- 5432:5432
env:
Expand All @@ -32,18 +32,13 @@ jobs:
- name: Start MySQL
run: sudo systemctl start mysql.service
- uses: actions/checkout@v5
- uses: actions/setup-python@v6
- uses: astral-sh/setup-uv@v6
with:
enable-cache: true
activate-environment: true
python-version: ${{ matrix.python-version }}
- uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/uv.lock') }}
restore-keys: |
${{ runner.os }}-pip-
- uses: astral-sh/setup-uv@v6
- name: Install dependencies and check style
run: uv run make check
- name: Install dependencies
run: make deps options='--no-extra=asyncmy --no-extra=psycopg'
- name: Install TortoiseORM v0.21
if: matrix.tortoise-orm == 'tortoise021'
run: uv pip install --upgrade "tortoise-orm>=0.21,<0.22"
Expand All @@ -64,33 +59,98 @@ jobs:
run: |
uv pip uninstall tortoise-orm
uv pip install --upgrade "git+https://github.com/tortoise/tortoise-orm"
- name: CI
- name: Build sdist
run: make _build
- name: Check style
run: make _check
- name: Run tests
env:
MYSQL_PASS: root
MYSQL_HOST: 127.0.0.1
MYSQL_PORT: 3306
POSTGRES_PASS: 123456
POSTGRES_HOST: 127.0.0.1
POSTGRES_PORT: 5432
run: uv run make _testall
- name: Verify aiomysql support
# Only check the latest version of tortoise
if: matrix.tortoise-orm == 'tortoisedev'
run: |
uv pip uninstall asyncmy
uv run make test_mysql
uv pip install asyncmy
run: make _testall
- name: Show test coverage
run: make report

asyncmySupport:
runs-on: ubuntu-latest
steps:
- name: Start MySQL
run: sudo systemctl start mysql.service
- uses: actions/checkout@v5
- uses: astral-sh/setup-uv@v6
with:
enable-cache: true
python-version: '3.13'
activate-environment: true
- name: Install dependencies
run: uv sync --all-groups --extra toml --extra asyncmy
- name: Test MySQL with asyncmy
run: make test_mysql
env:
MYSQL_PASS: root
MYSQL_HOST: 127.0.0.1
MYSQL_PORT: 3306
- name: Verify psycopg/tortoise-vector support
# Only check the latest version of tortoise
if: matrix.tortoise-orm == 'tortoisedev'
run: |
uv run make test_psycopg
uv run make test_postgres_vector
- name: Show test coverage
run: make report

postgresVector:
runs-on: ubuntu-latest
services:
postgres:
image: ankane/pgvector:latest
ports:
- 5432:5432
env:
POSTGRES_PASSWORD: 123456
POSTGRES_USER: postgres
options: --health-cmd=pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
steps:
- uses: actions/checkout@v5
- uses: astral-sh/setup-uv@v6
with:
enable-cache: true
python-version: '3.13'
activate-environment: true
- name: Install dependencies
run: make deps options='--no-extra=asyncmy --no-extra=psycopg'
- name: Test tortoise vector
run: make test_postgres_vector
env:
POSTGRES_PASS: 123456
POSTGRES_HOST: 127.0.0.1
POSTGRES_PORT: 5432
- name: Show test coverage
run: make report

psycopgSupport:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
steps:
- uses: actions/checkout@v5
- uses: actions/setup-python@v6
with:
python-version: '3.x'
- uses: ikalnytskyi/action-setup-postgres@v7
with:
username: postgres
password: 123456
- uses: astral-sh/setup-uv@v6
with:
enable-cache: true
- name: Install dependencies
run: |
uv pip install --system --group dev --group test -e ".[toml,psycopg,mysql]"
- name: Test psycopg
run: make test_psycopg
env:
POSTGRES_HOST: 127.0.0.1
POSTGRES_PASS: 123456
POSTGRES_PORT: 5432
- name: Show test coverage
run: make report
15 changes: 10 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
checkfiles = aerich/ tests/ conftest.py
src_dir = aerich
checkfiles = $(src_dir) tests/ conftest.py
py_warn = PYTHONDEVMODE=1
pytest_opts = --cov=$(src_dir) --cov-append --tb=native -q
MYSQL_HOST ?= "127.0.0.1"
MYSQL_PORT ?= 3306
MYSQL_PASS ?= "123456"
Expand Down Expand Up @@ -40,20 +42,23 @@ test_sqlite:
$(py_warn) TEST_DB=sqlite://:memory: pytest $(pytest_opts)

test_mysql:
$(py_warn) TEST_DB="mysql://root:$(MYSQL_PASS)@$(MYSQL_HOST):$(MYSQL_PORT)/test_\{\}" pytest -vv -s
$(py_warn) TEST_DB="mysql://root:$(MYSQL_PASS)@$(MYSQL_HOST):$(MYSQL_PORT)/test_\{\}" pytest -vv -s $(pytest_opts)

test_postgres:
$(py_warn) TEST_DB="postgres://postgres:$(POSTGRES_PASS)@$(POSTGRES_HOST):$(POSTGRES_PORT)/test_\{\}" pytest -vv -s
$(py_warn) TEST_DB="postgres://postgres:$(POSTGRES_PASS)@$(POSTGRES_HOST):$(POSTGRES_PORT)/test_\{\}" pytest -vv -s $(pytest_opts)

test_postgres_vector:
$(py_warn) AERICH_TEST_VECTOR=1 TEST_DB="postgres://postgres:$(POSTGRES_PASS)@$(POSTGRES_HOST):$(POSTGRES_PORT)/test_\{\}" pytest -vv -s tests/test_inspectdb.py::test_inspect_vector
$(py_warn) AERICH_TEST_VECTOR=1 TEST_DB="postgres://postgres:$(POSTGRES_PASS)@$(POSTGRES_HOST):$(POSTGRES_PORT)/test_\{\}" pytest -vv -s tests/test_inspectdb.py::test_inspect_vector $(pytest_opts)

test_psycopg:
$(py_warn) TEST_DB="psycopg://postgres:$(POSTGRES_PASS)@$(POSTGRES_HOST):$(POSTGRES_PORT)/test_\{\}" pytest -vv -s
$(py_warn) TEST_DB="psycopg://postgres:$(POSTGRES_PASS)@$(POSTGRES_HOST):$(POSTGRES_PORT)/test_\{\}" pytest -vv -s $(pytest_opts)

_testall: test_sqlite test_postgres test_mysql
testall: deps _testall

report:
coverage report -m

_build:
rm -fR dist/
uv build
Expand Down
85 changes: 56 additions & 29 deletions aerich/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@
from aerich.inspectdb.mysql import InspectMySQL
from aerich.inspectdb.postgres import InspectPostgres
from aerich.inspectdb.sqlite import InspectSQLite
from aerich.migrate import MIGRATE_TEMPLATE, Migrate
from aerich.migrate import Migrate
from aerich.models import Aerich
from aerich.utils import (
decompress_dict,
file_module_info,
get_app_connection,
get_app_connection_name,
Expand Down Expand Up @@ -101,9 +102,10 @@ def _get_m2m_tables(
backward_fk = forward_fk = ""
exists = "IF NOT EXISTS " if safe else ""
through_table_name = field_object.through
backward_type = self._get_pk_field_sql_type(model._meta.pk)
forward_type = self._get_pk_field_sql_type(field_object.related_model._meta.pk)
comment = ""
backward_type = forward_type = comment = ""
if func := getattr(self, "_get_pk_field_sql_type", None):
backward_type = func(model._meta.pk)
forward_type = func(field_object.related_model._meta.pk)
if desc := field_object.description:
comment = self._table_comment_generator(table=through_table_name, comment=desc)
m2m_create_string = self.M2M_TABLE_TEMPLATE.format(
Expand Down Expand Up @@ -163,8 +165,8 @@ def __init__(
Migrate.app = app
self._init_when_aenter = True

async def init(self) -> None:
await Migrate.init(self.tortoise_config, self.app, self.location)
async def init(self, offline: bool = False) -> None:
await Migrate.init(self.tortoise_config, self.app, self.location, offline=offline)

async def __aenter__(self) -> Command:
if self._init_when_aenter:
Expand Down Expand Up @@ -209,11 +211,12 @@ async def _upgrade(
upgrade = m.upgrade
if not fake:
await conn.execute_script(await upgrade(conn))
await Aerich.create(
version=version_file,
app=self.app,
content=get_models_describe(self.app),

model_state_str = getattr(m, "MODELS_STATE", None)
models_state = (
decompress_dict(model_state_str) if model_state_str else get_models_describe(self.app)
)
await Aerich.create(version=version_file, app=self.app, content=models_state)

async def upgrade(self, run_in_transaction: bool = True, fake: bool = False) -> list[str]:
migrated = []
Expand Down Expand Up @@ -299,30 +302,48 @@ async def inspectdb(self, tables: list[str] | None = None) -> str:

@overload
async def migrate(
self, name: str = "update", empty: bool = False, no_input: Literal[True] = True
self,
name: str = "update",
empty: bool = False,
no_input: Literal[True] = True,
offline: bool = False,
) -> str: ...

@overload
async def migrate(
self, name: str = "update", empty: bool = False, no_input: bool = False
self,
name: str = "update",
empty: bool = False,
no_input: bool = False,
offline: bool = False,
) -> str | None: ...

async def migrate(
self, name: str = "update", empty: bool = False, no_input: bool = False
self,
name: str = "update",
empty: bool = False,
no_input: bool = False,
offline: bool = False,
) -> str | None:
# return None if same version migration file already exists, and new one not generated
try:
return await Migrate.migrate(name, empty, no_input)
return await Migrate.migrate(name, empty, no_input, offline)
except NotInitedError as e:
raise NotInitedError("You have to call .init() first before migrate") from e

async def init_db(self, safe: bool, pre_sql: str | None = None) -> None:
await self._do_init(safe, pre_sql)

async def _do_init(self, safe: bool, pre_sql: str | None = None, offline: bool = False) -> None:
location = self.location
app = self.app
config = self.tortoise_config

await Tortoise.init(config=self.tortoise_config)
connection = get_app_connection(self.tortoise_config, app)
if pre_sql:
await Tortoise.init(config=config)
connection = get_app_connection(config, app)
if offline:
await Migrate.init(config, app, location, offline=True)
elif pre_sql:
await connection.execute_script(pre_sql)

dirname = Path(location, app)
Expand All @@ -332,20 +353,26 @@ async def init_db(self, safe: bool, pre_sql: str | None = None) -> None:
# If directory is empty, go ahead, otherwise raise FileExistsError
for unexpected_file in dirname.glob("*"):
raise FileExistsError(str(unexpected_file))

await generate_schema_for_client(connection, safe)

schema = get_schema_sql(connection, safe)

version = await Migrate.generate_version()
version = await Migrate.generate_version(offline=offline)
aerich_content = get_models_describe(app)
await Aerich.create(
version=version,
app=app,
content=aerich_content,
)
version_file = Path(dirname, version)
content = MIGRATE_TEMPLATE.format(upgrade_sql=schema, downgrade_sql="")
with open(version_file, "w", encoding="utf-8") as f:
f.write(content)
content = Migrate.build_migration_file_text(upgrade_sql=schema, models_state=aerich_content)
version_file.write_text(content, encoding="utf-8")
Migrate._last_version_content = aerich_content
if not offline:
await generate_schema_for_client(connection, safe)
await Aerich.create(version=version, app=app, content=aerich_content)

async def init_migrations(self, safe: bool) -> None:
await self._do_init(safe, offline=True)

async def fix_migrations(self) -> list[str]:
"""
Fix migration files to include models state for aerich 0.6.0+
:return: List of updated migration files
"""
Migrate.app = self.app
Migrate.migrate_location = Path(self.location, self.app)
return await Migrate.fix_migrations(self.tortoise_config)
Loading