From 4658c4c223fba9364526946ce84c0bd7de86aca1 Mon Sep 17 00:00:00 2001 From: Jakub Miazek Date: Sun, 22 May 2022 15:07:40 +0200 Subject: [PATCH] add migrations --- alembic.ini | 72 +++++++++++ alembic/env.py | 6 + alembic/script.py.mako | 1 - alembic/versions/421df2c2140f_init_tables.py | 121 +++++++++++++++++++ db/create.sql | 2 + docker-compose.yml | 2 +- the_app/main.py | 10 +- the_app/models/nonsense.py | 4 + the_app/models/stuff.py | 3 + 9 files changed, 214 insertions(+), 7 deletions(-) create mode 100644 alembic.ini create mode 100644 alembic/versions/421df2c2140f_init_tables.py diff --git a/alembic.ini b/alembic.ini new file mode 100644 index 0000000..64ef121 --- /dev/null +++ b/alembic.ini @@ -0,0 +1,72 @@ +# A generic, single database configuration. + +[alembic] +# path to migration scripts +script_location = alembic + +# template used to generate migration files +# file_template = %%(rev)s_%%(slug)s + +# timezone to use when rendering the date +# within the migration file as well as the filename. +# string value is passed to dateutil.tz.gettz() +# leave blank for localtime +# timezone = + +# max length of characters to apply to the +# "slug" field +#truncate_slug_length = 40 + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + +# set to 'true' to allow .pyc and .pyo files without +# a source .py file to be detected as revisions in the +# versions/ directory +# sourceless = false + +# version location specification; this defaults +# to alembic/versions. When using multiple version +# directories, initial revisions must be specified with --version-path +# version_locations = %(here)s/bar %(here)s/bat alembic/versions + +# the output encoding used when revision files +# are written from script.py.mako +# output_encoding = utf-8 + + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARN +handlers = console +qualname = + +[logger_sqlalchemy] +level = DEBUG +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = DEBUG +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(asctime)s %(levelname)-8s %(thread)d <%(name)s> %(message)r +datefmt = %H:%M:%S diff --git a/alembic/env.py b/alembic/env.py index 4d208a7..47c2b0f 100644 --- a/alembic/env.py +++ b/alembic/env.py @@ -1,8 +1,14 @@ import asyncio +import os +import sys from alembic import context from sqlalchemy.ext.asyncio import create_async_engine + +parent_dir = os.path.abspath(os.path.join(os.getcwd())) +sys.path.append(parent_dir) + from the_app.models.base import Base as app_base target_metadata = app_base.metadata diff --git a/alembic/script.py.mako b/alembic/script.py.mako index 0fdcfba..2c01563 100644 --- a/alembic/script.py.mako +++ b/alembic/script.py.mako @@ -7,7 +7,6 @@ Create Date: ${create_date} """ from alembic import op import sqlalchemy as sa -import tm.db ${imports if imports else ""} # revision identifiers, used by Alembic. diff --git a/alembic/versions/421df2c2140f_init_tables.py b/alembic/versions/421df2c2140f_init_tables.py new file mode 100644 index 0000000..4ed79e6 --- /dev/null +++ b/alembic/versions/421df2c2140f_init_tables.py @@ -0,0 +1,121 @@ +"""init tables + +Revision ID: 421df2c2140f +Revises: +Create Date: 2022-05-22 13:02:56.908424 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = '421df2c2140f' +down_revision = None +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('character', + sa.Column('id', sa.String(length=32), nullable=False), + sa.Column('name', sa.String(length=64), nullable=False), + sa.Column('speech_count', sa.Integer(), nullable=False), + sa.Column('abbrev', sa.String(length=32), nullable=True), + sa.Column('description', sa.String(length=2056), nullable=True), + sa.PrimaryKeyConstraint('id', name='character_pkey'), + schema='shakespeare' + ) + op.create_table('wordform', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('plain_text', sa.String(length=64), nullable=False), + sa.Column('phonetic_text', sa.String(length=64), nullable=False), + sa.Column('stem_text', sa.String(length=64), nullable=False), + sa.Column('occurences', sa.Integer(), nullable=False), + sa.PrimaryKeyConstraint('id', name='wordform_pkey'), + schema='shakespeare' + ) + op.create_table('work', + sa.Column('id', sa.String(length=32), nullable=False), + sa.Column('title', sa.String(length=32), nullable=False), + sa.Column('long_title', sa.String(length=64), nullable=False), + sa.Column('year', sa.Integer(), nullable=False), + sa.Column('genre_type', sa.String(length=1), nullable=False), + sa.Column('source', sa.String(length=16), nullable=False), + sa.Column('total_words', sa.Integer(), nullable=False), + sa.Column('total_paragraphs', sa.Integer(), nullable=False), + sa.Column('notes', sa.Text(), nullable=True), + sa.PrimaryKeyConstraint('id', name='work_pkey'), + schema='shakespeare' + ) + op.create_table('nonsense', + sa.Column('id', postgresql.UUID(as_uuid=True), autoincrement=True, nullable=True), + sa.Column('name', sa.String(), nullable=False), + sa.Column('description', sa.String(), nullable=False), + sa.PrimaryKeyConstraint('name'), + sa.UniqueConstraint('id'), + sa.UniqueConstraint('name'), + schema='the_others' + ) + op.create_table('stuff', + sa.Column('id', postgresql.UUID(as_uuid=True), autoincrement=True, nullable=True), + sa.Column('name', sa.String(), nullable=False), + sa.Column('description', sa.String(), nullable=False), + sa.PrimaryKeyConstraint('name'), + sa.UniqueConstraint('id'), + sa.UniqueConstraint('name'), + schema='the_others' + ) + op.create_table('chapter', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('work_id', sa.String(length=32), nullable=False), + sa.Column('section_number', sa.Integer(), nullable=False), + sa.Column('chapter_number', sa.Integer(), nullable=False), + sa.Column('description', sa.String(length=256), nullable=False), + sa.ForeignKeyConstraint(['work_id'], ['shakespeare.work.id'], name='chapter_work_id_fkey'), + sa.PrimaryKeyConstraint('id', name='chapter_pkey'), + sa.UniqueConstraint('work_id', 'section_number', 'chapter_number', name='chapter_work_id_section_number_chapter_number_key'), + schema='shakespeare' + ) + op.create_table('character_work', + sa.Column('character_id', sa.String(length=32), nullable=False), + sa.Column('work_id', sa.String(length=32), nullable=False), + sa.ForeignKeyConstraint(['character_id'], ['shakespeare.character.id'], name='character_work_character_id_fkey'), + sa.ForeignKeyConstraint(['work_id'], ['shakespeare.work.id'], name='character_work_work_id_fkey'), + sa.PrimaryKeyConstraint('character_id', 'work_id', name='character_work_pkey'), + schema='shakespeare' + ) + op.create_table('paragraph', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('work_id', sa.String(length=32), nullable=False), + sa.Column('paragraph_num', sa.Integer(), nullable=False), + sa.Column('character_id', sa.String(length=32), nullable=False), + sa.Column('plain_text', sa.Text(), nullable=False), + sa.Column('phonetic_text', sa.Text(), nullable=False), + sa.Column('stem_text', sa.Text(), nullable=False), + sa.Column('paragraph_type', sa.String(length=1), nullable=False), + sa.Column('section_number', sa.Integer(), nullable=False), + sa.Column('chapter_number', sa.Integer(), nullable=False), + sa.Column('char_count', sa.Integer(), nullable=False), + sa.Column('word_count', sa.Integer(), nullable=False), + sa.ForeignKeyConstraint(['character_id'], ['shakespeare.character.id'], name='paragraph_character_id_fkey'), + sa.ForeignKeyConstraint(['work_id', 'section_number', 'chapter_number'], ['shakespeare.chapter.work_id', 'shakespeare.chapter.section_number', 'shakespeare.chapter.chapter_number'], name='paragraph_chapter_fkey'), + sa.ForeignKeyConstraint(['work_id'], ['shakespeare.work.id'], name='paragraph_work_id_fkey'), + sa.PrimaryKeyConstraint('id', name='paragraph_pkey'), + schema='shakespeare' + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('paragraph', schema='shakespeare') + op.drop_table('character_work', schema='shakespeare') + op.drop_table('chapter', schema='shakespeare') + op.drop_table('stuff', schema='the_others') + op.drop_table('nonsense', schema='the_others') + op.drop_table('work', schema='shakespeare') + op.drop_table('wordform', schema='shakespeare') + op.drop_table('character', schema='shakespeare') + # ### end Alembic commands ### diff --git a/db/create.sql b/db/create.sql index 6d6618e..b541fb0 100644 --- a/db/create.sql +++ b/db/create.sql @@ -2,8 +2,10 @@ DROP DATABASE IF EXISTS devdb; CREATE DATABASE devdb; \connect devdb; CREATE SCHEMA shakespeare; +CREATE SCHEMA the_others; DROP DATABASE IF EXISTS testdb; CREATE DATABASE testdb; \connect testdb; CREATE SCHEMA shakespeare; +CREATE SCHEMA the_others; diff --git a/docker-compose.yml b/docker-compose.yml index e376e4d..a87a3c2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,7 +7,7 @@ services: - .env - .secrets command: bash -c " - uvicorn the_app.main:app + alembic upgrade head && uvicorn the_app.main:app --host 0.0.0.0 --port 8080 --lifespan=on --use-colors --loop uvloop --http httptools --reload --log-level debug diff --git a/the_app/main.py b/the_app/main.py index 6d69815..add621f 100644 --- a/the_app/main.py +++ b/the_app/main.py @@ -14,16 +14,16 @@ app.include_router(stuff_router) app.include_router(nonsense_router) -async def start_db(): - async with engine.begin() as conn: - await conn.run_sync(Base.metadata.create_all) - await engine.dispose() +# async def start_db(): +# async with engine.begin() as conn: +# await conn.run_sync(Base.metadata.create_all) +# await engine.dispose() @app.on_event("startup") async def startup_event(): logger.info("Starting up...") - await start_db() + # await start_db() @app.on_event("shutdown") diff --git a/the_app/models/nonsense.py b/the_app/models/nonsense.py index 52178e6..2d96a23 100644 --- a/the_app/models/nonsense.py +++ b/the_app/models/nonsense.py @@ -10,6 +10,9 @@ from the_app.models.base import Base class Nonsense(Base): __tablename__ = "nonsense" + __table_args__ = ( + {"schema": "the_others"}, + ) id = Column(UUID(as_uuid=True), unique=True, default=uuid.uuid4, autoincrement=True) name = Column(String, nullable=False, primary_key=True, unique=True) description = Column(String, nullable=False) @@ -18,6 +21,7 @@ class Nonsense(Base): self.name = name self.description = description + @classmethod async def find(cls, db_session: AsyncSession, name: str): """ diff --git a/the_app/models/stuff.py b/the_app/models/stuff.py index d29cfef..2d775d1 100644 --- a/the_app/models/stuff.py +++ b/the_app/models/stuff.py @@ -10,6 +10,9 @@ from the_app.models.base import Base class Stuff(Base): __tablename__ = "stuff" + __table_args__ = ( + {"schema": "the_others"}, + ) id = Column(UUID(as_uuid=True), unique=True, default=uuid.uuid4, autoincrement=True) name = Column(String, nullable=False, primary_key=True, unique=True) description = Column(String, nullable=False)