From ccb934986f2ed5b8a1798b2f528ab6f640c79162 Mon Sep 17 00:00:00 2001 From: Alex Muszynski Date: Wed, 4 Jun 2025 21:43:39 -0400 Subject: [PATCH] feat: alembic migration added --- backend/.env | 1 + backend/Makefile | 0 backend/alembic.ini | 141 ++++++++++++++++++ backend/alembic/README | 1 + .../alembic/__pycache__/env.cpython-313.pyc | Bin 0 -> 2597 bytes backend/alembic/env.py | 74 +++++++++ backend/alembic/script.py.mako | 28 ++++ .../a3ac646e53a8_init.cpython-313.pyc | Bin 0 -> 2215 bytes backend/alembic/versions/a3ac646e53a8_init.py | 41 +++++ backend/app/__pycache__/crud.cpython-313.pyc | Bin 2039 -> 2030 bytes .../app/__pycache__/database.cpython-313.pyc | Bin 534 -> 552 bytes backend/app/__pycache__/main.cpython-313.pyc | Bin 2673 -> 2659 bytes .../app/__pycache__/models.cpython-313.pyc | Bin 714 -> 714 bytes .../app/__pycache__/schemas.cpython-313.pyc | Bin 1094 -> 929 bytes backend/app/crud.py | 2 +- backend/app/database.py | 6 +- backend/app/main.py | 2 +- backend/app/schemas.py | 5 +- backend/docs/migration.md | 13 ++ backend/pyproject.toml | 2 + backend/uv.lock | 41 +++++ 21 files changed, 348 insertions(+), 9 deletions(-) create mode 100644 backend/.env create mode 100644 backend/Makefile create mode 100644 backend/alembic.ini create mode 100644 backend/alembic/README create mode 100644 backend/alembic/__pycache__/env.cpython-313.pyc create mode 100644 backend/alembic/env.py create mode 100644 backend/alembic/script.py.mako create mode 100644 backend/alembic/versions/__pycache__/a3ac646e53a8_init.cpython-313.pyc create mode 100644 backend/alembic/versions/a3ac646e53a8_init.py create mode 100644 backend/docs/migration.md diff --git a/backend/.env b/backend/.env new file mode 100644 index 0000000..2033f79 --- /dev/null +++ b/backend/.env @@ -0,0 +1 @@ +DATABASE_URL=postgresql://user:password@localhost:5432/mydatabase diff --git a/backend/Makefile b/backend/Makefile new file mode 100644 index 0000000..e69de29 diff --git a/backend/alembic.ini b/backend/alembic.ini new file mode 100644 index 0000000..692d73a --- /dev/null +++ b/backend/alembic.ini @@ -0,0 +1,141 @@ +# A generic, single database configuration. + +[alembic] +# path to migration scripts. +# this is typically a path given in POSIX (e.g. forward slashes) +# format, relative to the token %(here)s which refers to the location of this +# ini file +script_location = %(here)s/alembic + +# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s +# Uncomment the line below if you want the files to be prepended with date and time +# see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file +# for all available tokens +# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s + +# sys.path path, will be prepended to sys.path if present. +# defaults to the current working directory. for multiple paths, the path separator +# is defined by "path_separator" below. +prepend_sys_path = . + + +# timezone to use when rendering the date within the migration file +# as well as the filename. +# If specified, requires the python>=3.9 or backports.zoneinfo library and tzdata library. +# Any required deps can installed by adding `alembic[tz]` to the pip requirements +# string value is passed to ZoneInfo() +# 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 /versions. When using multiple version +# directories, initial revisions must be specified with --version-path. +# The path separator used here should be the separator specified by "path_separator" +# below. +# version_locations = %(here)s/bar:%(here)s/bat:%(here)s/alembic/versions + +# path_separator; This indicates what character is used to split lists of file +# paths, including version_locations and prepend_sys_path within configparser +# files such as alembic.ini. +# The default rendered in new alembic.ini files is "os", which uses os.pathsep +# to provide os-dependent path splitting. +# +# Note that in order to support legacy alembic.ini files, this default does NOT +# take place if path_separator is not present in alembic.ini. If this +# option is omitted entirely, fallback logic is as follows: +# +# 1. Parsing of the version_locations option falls back to using the legacy +# "version_path_separator" key, which if absent then falls back to the legacy +# behavior of splitting on spaces and/or commas. +# 2. Parsing of the prepend_sys_path option falls back to the legacy +# behavior of splitting on spaces, commas, or colons. +# +# Valid values for path_separator are: +# +# path_separator = : +# path_separator = ; +# path_separator = space +# path_separator = newline +# +# Use os.pathsep. Default configuration used for new projects. +path_separator = os + +# set to 'true' to search source files recursively +# in each "version_locations" directory +# new in Alembic version 1.10 +# recursive_version_locations = false + +# the output encoding used when revision files +# are written from script.py.mako +# output_encoding = utf-8 + +# database URL. This is consumed by the user-maintained env.py script only. +# other means of configuring database URLs may be customized within the env.py +# file. +sqlalchemy.url = postgresql://user:password@localhost:5432/mydatabase + + +[post_write_hooks] +# post_write_hooks defines scripts or Python functions that are run +# on newly generated revision scripts. See the documentation for further +# detail and examples + +# format using "black" - use the console_scripts runner, against the "black" entrypoint +# hooks = black +# black.type = console_scripts +# black.entrypoint = black +# black.options = -l 79 REVISION_SCRIPT_FILENAME + +# lint with attempts to fix using "ruff" - use the exec runner, execute a binary +# hooks = ruff +# ruff.type = exec +# ruff.executable = %(here)s/.venv/bin/ruff +# ruff.options = check --fix REVISION_SCRIPT_FILENAME + +# Logging configuration. This is also consumed by the user-maintained +# env.py script only. +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARNING +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARNING +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/backend/alembic/README b/backend/alembic/README new file mode 100644 index 0000000..98e4f9c --- /dev/null +++ b/backend/alembic/README @@ -0,0 +1 @@ +Generic single-database configuration. \ No newline at end of file diff --git a/backend/alembic/__pycache__/env.cpython-313.pyc b/backend/alembic/__pycache__/env.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d9d3a30896c2f9a1af79e950b92ada904d207d55 GIT binary patch literal 2597 zcmZ`*%}*Og6rc63?cK#6d;}5})$hEir3yJy%1P5xefJmCk#g#rT`y}|vPLs+-h2Dr%zN|u zEywY=jNpl!e6+@i2>s3${=>J0;~0m~V^lx|ZV4@Nl*3F8Erk|&%DXbZBrJw$*p&s4 zBQ)a5;ic%JNJWfR#eA?_W)pkmEio(bWetZPOW3=Kb*^u|!aH>Jj z2)_shS`mChKH^KkoI!Wd3LMFM3ZVjD5N5bScuv5bZ@QPj_wwf8$RQ|oN0!)GKeh^d zq3sd&n+z(oX8V92K@~0=Rjz;`-!O1Zt5U_*OvA#OfhWvL zMTb&O;F?(`)3SVLO|!69(gr0Kw$}(-vU=i8&0fRz8wsG(}ZO-BwCEk=F<%Sh4UX(e-JJ=eMx6uGQ^tA@j%cH*Vj;wuws+YD3X<78!QFpy)d6*YvkLO&$=nVUx)$cGHrYX1iIc z8jEJx)J+Vi#+~R?3R4IB{LG303rBNi)ip6DwS@V%N5;=Dzx_gfXM5p5=xPc5O`(53mOQvLu|JU7AILoU z^m+f(KTHfd2 z3UB6+<%=GNyxF5Fv{@$n@ayv^#yG<;w+uiW(1N8BL!p`pWZ*%YL6;#PVC@r%OEHRN znQ(=fo?+VnC;g%?syWJJ`uR)P9u_OA_>(!nylv_V1saZa671;;wCv*%p 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/backend/alembic/script.py.mako b/backend/alembic/script.py.mako new file mode 100644 index 0000000..480b130 --- /dev/null +++ b/backend/alembic/script.py.mako @@ -0,0 +1,28 @@ +"""${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 +${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: + """Upgrade schema.""" + ${upgrades if upgrades else "pass"} + + +def downgrade() -> None: + """Downgrade schema.""" + ${downgrades if downgrades else "pass"} diff --git a/backend/alembic/versions/__pycache__/a3ac646e53a8_init.cpython-313.pyc b/backend/alembic/versions/__pycache__/a3ac646e53a8_init.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cebecc62508b6131e79d399c79f4412a4789731a GIT binary patch literal 2215 zcmb_dL2MI86rEj<*Y?IvNSc5|RIoxw>Q;%tiBn=BRGOqA37EB#|6CGB$K%G@3Y?8W~F^62pmOBgYaWvivI?5XD3mRLHdwv2uj@&szW< z6PQ>irdikBTxw2NAf#2w9C zw1;en9G};6i|{aU%_RAGBNx9;C@&>D?tNwmX*k2hG7FTpuR`}iupapA6EH2KEeZJp zAD+2==5F8p{?-1fky~{e&ft`h$AcV0C&OA8)8DgWu!*v>PCH6h#;mZcz-t zll^^BD8i*yF=SryBp#0-pzj)eDA~{`J_RfOaqRJbF{0%0a2v}`NDm?g%zuIy_z#FY zW4i_~L=THd*Dxq;P$!iS84$|z|)_Lb0G<-3x+`<(UDK0=x+s5(#fE)JU ziqCNuBC+aQUWNe_8msD3k%_j(q=|xA%3CZj zlRh^!J(XdB3nw!Zr%q;A_q)`{YqUH^$`imdhiZo9&`y5RCX7pP`U;QAxj{M6Scvym z_j+^y)(7CTzk#VqzO%Q_R;D?xW^T`{=y%_~Kf5}6=X4c6F4gcx1JpoZ1Tc{nA#L zvmF9Ll*!e}2M6ojv9&j=7pkY8oL|RtJlJ#NtM%@|wSCpm>cEqpI-YiK1g{}c;*_YB zj_qLqRn-eQRb`S>E*fx0&V9-$%>s?K)M$V9HB&Qr6D`vy_X%(xALNjAsH$dJ1xMr0 zXR9jZET%FWyEv<8po3wp^UC+INv@t=on!}QE22*PioB;Y^Vk#Oi2wD(7} z?^zIa9$XHt9IB%O8+`-Ik(E+KdC*^Zuhwy}js`Z4CL5~P9*ul5wATH!XQm5 literal 0 HcmV?d00001 diff --git a/backend/alembic/versions/a3ac646e53a8_init.py b/backend/alembic/versions/a3ac646e53a8_init.py new file mode 100644 index 0000000..10a722d --- /dev/null +++ b/backend/alembic/versions/a3ac646e53a8_init.py @@ -0,0 +1,41 @@ +"""init + +Revision ID: a3ac646e53a8 +Revises: +Create Date: 2025-06-04 21:36:22.283823 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = 'a3ac646e53a8' +down_revision: Union[str, None] = None +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.drop_index(op.f('ix_items_id'), table_name='items') + op.drop_index(op.f('ix_items_name'), table_name='items') + op.drop_table('items') + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('items', + sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False), + sa.Column('name', sa.VARCHAR(), autoincrement=False, nullable=True), + sa.Column('description', sa.VARCHAR(), autoincrement=False, nullable=True), + sa.PrimaryKeyConstraint('id', name=op.f('items_pkey')) + ) + op.create_index(op.f('ix_items_name'), 'items', ['name'], unique=False) + op.create_index(op.f('ix_items_id'), 'items', ['id'], unique=False) + # ### end Alembic commands ### diff --git a/backend/app/__pycache__/crud.cpython-313.pyc b/backend/app/__pycache__/crud.cpython-313.pyc index ab6117ad8d4d394c6225cb7ad698b4530abd8bf6..79c89c30d84abdda5d430cdb1c4ae859620ddb7c 100644 GIT binary patch delta 56 zcmey)|Bj#cGcPX}0}!10>X5N!Bd;wxYY{h)J2{M9g-Zj-USTX*6)sJnG$RlfXHTwW R4`92lYk7$(axDl+m- zc3?D=f+#3r0~&CP8KjggJr$&-h<$P;qkEJDP#oltVh}l-{2SQuk5OvA-6*4GQUm(&qGeJ2{o5FRT|iC@=Gr$-NAam?IOQ-1J6x% O@yqOzjVwiCK*IpPCP@td delta 263 zcmZ3%GL41%GcPX}0|;naP2`qlteL2;84<%8!~+&XA(R+`S%O(jnIM8i>@kc=3^DAc zj8HyD3^Py#TQFM;M=-l3a}j3@ixPt=)Py3gbY@MCiH9t0Z}GS|hB!Jo2D`?G2KiL! z7UUO~q!*z5X%7FiV}78jT27o|9)Bw3l77#r#5R;DDDBqk*mr%sk)RAl6y zY{O{Ck)B$TnpalDHo2YA-9!Xv4agnEEqoTGcPX}0}xcabI1_i$ScPzKB&GRjOg<>;7vlEab93TQGT5Elne7U9g7FtXgCwpsrp10$o_g!Jh-6LUU*m@C*P I&*i)V0F+iMYXATM delta 228 zcmaDX@==8MGcPX}0}y2DTV=>@<+OFEk-`@}j8M!U(GOtKS?hzP~7#IOdl zssj}UvlX$YGXtd`D6`#SNy<;DoXpIa!^kzcfKf+P3aFr+;RY9Pv*!&tr3UW_!V^j+ zN?m4A0t&Tx76}2Ri&!W7GerT_uVV6);{^(Y6oDMzr^!~t4U*;o5g_p*zR7~jqV<9x zCP)QDw=j@(i_JMdFD)~@ND{~aDJ^CP5^W4Oq~yB!AtbBT@3)~E=<}0 literal 1094 zcmah|&ubGw6rS0g-E5MEsI?d(q#7x*qV-}yL_FAo)M5{=$H1^An{*-Bjc+!G+=QYQ zyu?HQ2>&4o1D-s^laPp4-KTo0;#ueZO~JsgxM55BHuN|HShLJCjL|{KCFov8koCAfP?Yr7L z$f5z6p9xgeA_)i4kuG;4)rNSO#cA@D`JVQqB#D%|B;_FOjQVunkn-Iq>@Ou%DZ4SK ztRE*)lA_pF8E9m~BuTT7Y$++hBR^YGDx%sl%rc1Ij5PGrjpR91hJ^?c-<= zs@>rjsNe=DR<(ujiA_tDlY?oa`TlUSImy1%zlkPXJ=a?)7e|)X{5ryFxdICWmP6+T6 zQUc0fQ5$d~yzEquF599qWCVA6g diff --git a/backend/app/crud.py b/backend/app/crud.py index c2fb5dc..deeb3e0 100644 --- a/backend/app/crud.py +++ b/backend/app/crud.py @@ -10,7 +10,7 @@ def get_items(db: Session, skip: int = 0, limit: int = 10): return db.query(models.Item).offset(skip).limit(limit).all() -def create_item(db: Session, item: schemas.ItemCreate): +def create_item(db: Session, item: schemas.Item): db_item = models.Item(**item.model_dump()) db.add(db_item) db.commit() diff --git a/backend/app/database.py b/backend/app/database.py index 22c96b1..4b658b2 100644 --- a/backend/app/database.py +++ b/backend/app/database.py @@ -1,10 +1,10 @@ from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker, declarative_base import os +from dotenv import load_dotenv -DATABASE_URL = os.getenv( - "DATABASE_URL", "postgresql://user:password@db:5432/mydatabase" -) +env = load_dotenv() +DATABASE_URL = os.getenv("DATABASE_URL", "") engine = create_engine(DATABASE_URL) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) diff --git a/backend/app/main.py b/backend/app/main.py index cbdb685..0937884 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -23,7 +23,7 @@ def health_check(): @app.post("/items/", response_model=schemas.Item) -def create_item(item: schemas.ItemCreate, db: Session = Depends(get_db)): +def create_item(item: schemas.Item, db: Session = Depends(get_db)): return crud.create_item(db, item) diff --git a/backend/app/schemas.py b/backend/app/schemas.py index c900f57..161b8f4 100644 --- a/backend/app/schemas.py +++ b/backend/app/schemas.py @@ -4,10 +4,7 @@ from pydantic import BaseModel class ItemBase(BaseModel): name: str description: str | None = None - - -class ItemCreate(ItemBase): - pass + body: str class Item(ItemBase): diff --git a/backend/docs/migration.md b/backend/docs/migration.md new file mode 100644 index 0000000..66e907d --- /dev/null +++ b/backend/docs/migration.md @@ -0,0 +1,13 @@ +# Database Migration with Alembic + +## Install Alembic + +## Init Alembic + +## Migration + +```bash + +alembic revision --autogenerate -m "add items table" +alembic upgrade head +``` diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 8ff85c1..83b6e76 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -5,6 +5,8 @@ description = "Add your description here" readme = "README.md" requires-python = ">=3.11" dependencies = [ + "alembic>=1.16.1", + "dotenv>=0.9.9", "fastapi[standard]>=0.115.12", "psycopg2-binary>=2.9.10", "sqlalchemy>=2.0.41", diff --git a/backend/uv.lock b/backend/uv.lock index f8fcb49..7c09e9c 100644 --- a/backend/uv.lock +++ b/backend/uv.lock @@ -2,6 +2,20 @@ version = 1 revision = 2 requires-python = ">=3.11" +[[package]] +name = "alembic" +version = "1.16.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mako" }, + { name = "sqlalchemy" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/20/89/bfb4fe86e3fc3972d35431af7bedbc60fa606e8b17196704a1747f7aa4c3/alembic-1.16.1.tar.gz", hash = "sha256:43d37ba24b3d17bc1eb1024fe0f51cd1dc95aeb5464594a02c6bb9ca9864bfa4", size = 1955006, upload-time = "2025-05-21T23:11:05.991Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/31/59/565286efff3692c5716c212202af61466480f6357c4ae3089d4453bff1f3/alembic-1.16.1-py3-none-any.whl", hash = "sha256:0cdd48acada30d93aa1035767d67dff25702f8de74d7c3919f2e8492c8db2e67", size = 242488, upload-time = "2025-05-21T23:11:07.783Z" }, +] + [[package]] name = "annotated-types" version = "0.7.0" @@ -64,6 +78,17 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/68/1b/e0a87d256e40e8c888847551b20a017a6b98139178505dc7ffb96f04e954/dnspython-2.7.0-py3-none-any.whl", hash = "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86", size = 313632, upload-time = "2024-10-05T20:14:57.687Z" }, ] +[[package]] +name = "dotenv" +version = "0.9.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "python-dotenv" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/b2/b7/545d2c10c1fc15e48653c91efde329a790f2eecfbbf2bd16003b5db2bab0/dotenv-0.9.9-py2.py3-none-any.whl", hash = "sha256:29cf74a087b31dafdb5a446b6d7e11cbce8ed2741540e2339c69fbef92c94ce9", size = 1892, upload-time = "2025-02-19T22:15:01.647Z" }, +] + [[package]] name = "email-validator" version = "2.2.0" @@ -250,6 +275,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, ] +[[package]] +name = "mako" +version = "1.3.10" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9e/38/bd5b78a920a64d708fe6bc8e0a2c075e1389d53bef8413725c63ba041535/mako-1.3.10.tar.gz", hash = "sha256:99579a6f39583fa7e5630a28c3c1f440e4e97a414b80372649c0ce338da2ea28", size = 392474, upload-time = "2025-04-10T12:44:31.16Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/fb/99f81ac72ae23375f22b7afdb7642aba97c00a713c217124420147681a2f/mako-1.3.10-py3-none-any.whl", hash = "sha256:baef24a52fc4fc514a0887ac600f9f1cff3d82c61d4d700a1fa84d597b88db59", size = 78509, upload-time = "2025-04-10T12:50:53.297Z" }, +] + [[package]] name = "markdown-it-py" version = "3.0.0" @@ -536,6 +573,8 @@ name = "server" version = "0.1.0" source = { virtual = "." } dependencies = [ + { name = "alembic" }, + { name = "dotenv" }, { name = "fastapi", extra = ["standard"] }, { name = "psycopg2-binary" }, { name = "sqlalchemy" }, @@ -544,6 +583,8 @@ dependencies = [ [package.metadata] requires-dist = [ + { name = "alembic", specifier = ">=1.16.1" }, + { name = "dotenv", specifier = ">=0.9.9" }, { name = "fastapi", extras = ["standard"], specifier = ">=0.115.12" }, { name = "psycopg2-binary", specifier = ">=2.9.10" }, { name = "sqlalchemy", specifier = ">=2.0.41" },