From 1c104ab567b81d615d1dc57828b362773dc75f84 Mon Sep 17 00:00:00 2001 From: fakz9 Date: Sat, 29 Jun 2024 22:30:23 +0300 Subject: [PATCH] test --- alembic.ini | 2 +- alembic/env.py | 77 ++++++++++++++++++++++++++++++++++----------- backend/__init__.py | 0 backend/config.py | 10 ++++++ backend/session.py | 24 ++++++++++++++ migrate.sh | 2 ++ models/__init__.py | 4 +++ models/base.py | 13 ++++++++ test.py | 3 ++ 9 files changed, 116 insertions(+), 19 deletions(-) create mode 100644 backend/__init__.py create mode 100644 backend/config.py create mode 100644 backend/session.py create mode 100755 migrate.sh create mode 100644 models/__init__.py create mode 100644 models/base.py create mode 100644 test.py diff --git a/alembic.ini b/alembic.ini index 1b1cbf6..9275d7b 100644 --- a/alembic.ini +++ b/alembic.ini @@ -60,7 +60,7 @@ version_path_separator = os # Use os.pathsep. Default configuration used for ne # are written from script.py.mako # output_encoding = utf-8 -sqlalchemy.url = sqlalchemy.url = postgresql://{PG_LOGIN}:{PG_PASSWORD}@{PG_HOST}/:{PG_PORT}/{PG_DATABASE} +sqlalchemy.url = postgresql://{PG_LOGIN}:{PG_PASSWORD}@{PG_HOST}/:{PG_PORT}/{PG_DATABASE} [post_write_hooks] diff --git a/alembic/env.py b/alembic/env.py index 36112a3..c5e86e2 100644 --- a/alembic/env.py +++ b/alembic/env.py @@ -1,10 +1,14 @@ +import asyncio +import backend.config as settings from logging.config import fileConfig -from sqlalchemy import engine_from_config -from sqlalchemy import pool +from sqlalchemy.engine import Connection from alembic import context +from backend.session import engine +from models import BaseModel + # this is the Alembic Config object, which provides # access to the values within the .ini file in use. config = context.config @@ -18,12 +22,28 @@ if config.config_file_name is not None: # for 'autogenerate' support # from myapp import mymodel # target_metadata = mymodel.Base.metadata -target_metadata = None +target_metadata = BaseModel.metadata +print(target_metadata.schema) + # 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 include_object(object, name, type_, reflected, compare_to): + if type_ == 'table' and object.schema != target_metadata.schema: + return False + return True + + +def get_url(): + url = config.get_main_option("sqlalchemy.url").format( + PG_LOGIN=settings.PG_LOGIN, + PG_PASSWORD=settings.PG_PASSWORD, + PG_HOST=settings.PG_HOST, + PG_DATABASE=settings.PG_DATABASE, + ) + return url def run_migrations_offline() -> None: @@ -38,38 +58,59 @@ def run_migrations_offline() -> None: script output. """ - url = config.get_main_option("sqlalchemy.url") + url = get_url() context.configure( url=url, target_metadata=target_metadata, literal_binds=True, dialect_opts={"paramstyle": "named"}, + version_table_schema=target_metadata.schema, + include_schemas=True ) with context.begin_transaction(): + """ + By default search_path is setted to "$user",public + that why alembic can't create foreign keys correctly + """ + context.execute('SET search_path TO public') context.run_migrations() -def run_migrations_online() -> None: - """Run migrations in 'online' mode. +def do_run_migrations(connection: Connection) -> None: + # Here we set our custom schema configuration + context.configure( + connection=connection, + target_metadata=target_metadata, + version_table="asia_alembic_version", + # Table in the db which will save the current alembic version of this schema + version_table_schema=target_metadata.schema, # Alternative: "public", to put version tables in seperate schema + include_schemas=True, + include_object=include_object, + ) + with context.begin_transaction(): + # context.execute(f'set search_path to {target_metadata.schema}') - In this scenario we need to create an Engine + context.run_migrations() + + +async def run_async_migrations() -> None: + """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, - ) + connectable = engine - with connectable.connect() as connection: - context.configure( - connection=connection, target_metadata=target_metadata - ) + async with connectable.connect() as connection: + await connection.run_sync(do_run_migrations) - with context.begin_transaction(): - context.run_migrations() + await connectable.dispose() + + +def run_migrations_online() -> None: + """Run migrations in 'online' mode.""" + + asyncio.run(run_async_migrations()) if context.is_offline_mode(): diff --git a/backend/__init__.py b/backend/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/config.py b/backend/config.py new file mode 100644 index 0000000..907b938 --- /dev/null +++ b/backend/config.py @@ -0,0 +1,10 @@ +import os + +from dotenv import load_dotenv, find_dotenv + +load_dotenv(find_dotenv()) +PG_LOGIN = os.environ.get('PG_LOGIN') +PG_PASSWORD = os.environ.get('PG_PASSWORD') +PG_PORT = os.environ.get('PG_PORT') +PG_HOST = os.environ.get('PG_HOST') +PG_DATABASE = os.environ.get('PG_DATABASE') diff --git a/backend/session.py b/backend/session.py new file mode 100644 index 0000000..f6af392 --- /dev/null +++ b/backend/session.py @@ -0,0 +1,24 @@ +from typing import AsyncGenerator + +from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker, AsyncSession + +from .config import ( + PG_HOST, + PG_LOGIN, + PG_PORT, + PG_DATABASE, + PG_PASSWORD, +) + +database_url = ( + f"postgresql+asyncpg://" + f"{PG_LOGIN}:{PG_PASSWORD}@" + f"{PG_HOST}:{PG_PORT}/{PG_DATABASE}" +) +engine = create_async_engine(database_url) +session_factory = async_sessionmaker(engine, expire_on_commit=False, autoflush=False, autocommit=False) + + +async def get_session() -> AsyncGenerator[AsyncSession, None]: + async with session_factory() as session: + yield session diff --git a/migrate.sh b/migrate.sh new file mode 100755 index 0000000..850100f --- /dev/null +++ b/migrate.sh @@ -0,0 +1,2 @@ +alembic revision --autogenerate +alembic upgrade head \ No newline at end of file diff --git a/models/__init__.py b/models/__init__.py new file mode 100644 index 0000000..41f8611 --- /dev/null +++ b/models/__init__.py @@ -0,0 +1,4 @@ +from sqlalchemy.orm import configure_mappers +from .base import * + +configure_mappers() diff --git a/models/base.py b/models/base.py new file mode 100644 index 0000000..8ba1432 --- /dev/null +++ b/models/base.py @@ -0,0 +1,13 @@ +from sqlalchemy import MetaData, ForeignKey +from sqlalchemy.orm import declarative_base, Mapped, mapped_column + +BaseModel = declarative_base(metadata=MetaData(schema='stocks')) + + +class DailyStock(BaseModel): + __tablename__ = 'daily_stocks' + __table_args__ = { + 'schema': 'stocks', + } + product_id: Mapped[int] = mapped_column( primary_key=True) + sold_today: Mapped[int] = mapped_column() diff --git a/test.py b/test.py new file mode 100644 index 0000000..6aad900 --- /dev/null +++ b/test.py @@ -0,0 +1,3 @@ +from alembic.env import run_migrations_offline + +run_migrations_offline() \ No newline at end of file