From 78fa3076b943a25eeff68cbe5fae3e22537fa5c1 Mon Sep 17 00:00:00 2001 From: h7x4 Date: Sat, 6 May 2023 17:48:27 +0200 Subject: [PATCH] Set up alembic, and more - Setup alembic and generate the initial migration - Add poethepoet to dev dependencies in order to put hard-to-remember commands beneath the poetry namespace - Add psycopg2-binary dependency, for use with postgresql - Remove dotenv dependency, as it is no longer required - Add `config.toml` to gitignore. This is because alembic has no argument parser that will let you specify the config file to use. Developers are encouraged to use `config.toml`, which will be automatically recognized, unless they want to implement the argument parser functionality for alembic. --- .gitignore | 4 +- alembic.ini | 110 +++++++++++ poetry.lock | 118 +++++++++++- pyproject.toml | 19 +- worblehat/models/migrations/env.py | 56 ++++++ worblehat/models/migrations/script.py.mako | 24 +++ ...-06T1746_d51c7172d2f2_initial_migration.py | 175 ++++++++++++++++++ 7 files changed, 495 insertions(+), 11 deletions(-) create mode 100644 alembic.ini create mode 100644 worblehat/models/migrations/env.py create mode 100644 worblehat/models/migrations/script.py.mako create mode 100644 worblehat/models/migrations/versions/2023-05-06T1746_d51c7172d2f2_initial_migration.py diff --git a/.gitignore b/.gitignore index 4ea3da9..157d6b0 100644 --- a/.gitignore +++ b/.gitignore @@ -20,4 +20,6 @@ venv.bak/ dist/ -result \ No newline at end of file +result + +config.toml \ No newline at end of file diff --git a/alembic.ini b/alembic.ini new file mode 100644 index 0000000..1a85016 --- /dev/null +++ b/alembic.ini @@ -0,0 +1,110 @@ +# A generic, single database configuration. + +[alembic] +# path to migration scripts +script_location = worblehat/models/migrations + +# 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).2dT%%(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. +prepend_sys_path = . + +# timezone to use when rendering the date within the migration file +# as well as the filename. +# If specified, requires the python-dateutil library that can be +# installed by adding `alembic[tz]` to the pip requirements +# 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 = true + +# 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 worblehat/models/migrations/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 "version_path_separator" below. +# version_locations = %(here)s/bar:%(here)s/bat:worblehat/models/migrations/versions + +# version path separator; As mentioned above, this is the character used to split +# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep. +# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas. +# Valid values for version_path_separator are: +# +# version_path_separator = : +# version_path_separator = ; +# version_path_separator = space +version_path_separator = os # Use os.pathsep. Default configuration used for new projects. + +# 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 + +# sqlalchemy.url = driver://user:pass@localhost/dbname + + +[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 + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARN +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARN +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/poetry.lock b/poetry.lock index 1035bee..67e5878 100644 --- a/poetry.lock +++ b/poetry.lock @@ -315,19 +315,107 @@ files = [ ] [[package]] -name = "python-dotenv" -version = "1.0.0" -description = "Read key-value pairs from a .env file and set them as environment variables" -category = "main" +name = "pastel" +version = "0.2.1" +description = "Bring colors to your terminal." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "pastel-0.2.1-py2.py3-none-any.whl", hash = "sha256:4349225fcdf6c2bb34d483e523475de5bb04a5c10ef711263452cb37d7dd4364"}, + {file = "pastel-0.2.1.tar.gz", hash = "sha256:e6581ac04e973cac858828c6202c1e1e81fee1dc7de7683f3e1ffe0bfd8a573d"}, +] + +[[package]] +name = "poethepoet" +version = "0.20.0" +description = "A task runner that works well with poetry." +category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "python-dotenv-1.0.0.tar.gz", hash = "sha256:a8df96034aae6d2d50a4ebe8216326c61c3eb64836776504fcca410e5937a3ba"}, - {file = "python_dotenv-1.0.0-py3-none-any.whl", hash = "sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a"}, + {file = "poethepoet-0.20.0-py3-none-any.whl", hash = "sha256:cb37be15f3895ccc65ddf188c2e3d8fb79e26cc9d469a6098cb1c6f994659f6f"}, + {file = "poethepoet-0.20.0.tar.gz", hash = "sha256:ca5a2a955f52dfb0a53fad3c989ef0b69ce3d5ec0f6bfa9b1da1f9e32d262e20"}, ] +[package.dependencies] +pastel = ">=0.2.1,<0.3.0" +tomli = ">=1.2.2" + [package.extras] -cli = ["click (>=5.0)"] +poetry-plugin = ["poetry (>=1.0,<2.0)"] + +[[package]] +name = "psycopg2-binary" +version = "2.9.6" +description = "psycopg2 - Python-PostgreSQL Database Adapter" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "psycopg2-binary-2.9.6.tar.gz", hash = "sha256:1f64dcfb8f6e0c014c7f55e51c9759f024f70ea572fbdef123f85318c297947c"}, + {file = "psycopg2_binary-2.9.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d26e0342183c762de3276cca7a530d574d4e25121ca7d6e4a98e4f05cb8e4df7"}, + {file = "psycopg2_binary-2.9.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c48d8f2db17f27d41fb0e2ecd703ea41984ee19362cbce52c097963b3a1b4365"}, + {file = "psycopg2_binary-2.9.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffe9dc0a884a8848075e576c1de0290d85a533a9f6e9c4e564f19adf8f6e54a7"}, + {file = "psycopg2_binary-2.9.6-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8a76e027f87753f9bd1ab5f7c9cb8c7628d1077ef927f5e2446477153a602f2c"}, + {file = "psycopg2_binary-2.9.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6460c7a99fc939b849431f1e73e013d54aa54293f30f1109019c56a0b2b2ec2f"}, + {file = "psycopg2_binary-2.9.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae102a98c547ee2288637af07393dd33f440c25e5cd79556b04e3fca13325e5f"}, + {file = "psycopg2_binary-2.9.6-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9972aad21f965599ed0106f65334230ce826e5ae69fda7cbd688d24fa922415e"}, + {file = "psycopg2_binary-2.9.6-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7a40c00dbe17c0af5bdd55aafd6ff6679f94a9be9513a4c7e071baf3d7d22a70"}, + {file = "psycopg2_binary-2.9.6-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:cacbdc5839bdff804dfebc058fe25684cae322987f7a38b0168bc1b2df703fb1"}, + {file = "psycopg2_binary-2.9.6-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7f0438fa20fb6c7e202863e0d5ab02c246d35efb1d164e052f2f3bfe2b152bd0"}, + {file = "psycopg2_binary-2.9.6-cp310-cp310-win32.whl", hash = "sha256:b6c8288bb8a84b47e07013bb4850f50538aa913d487579e1921724631d02ea1b"}, + {file = "psycopg2_binary-2.9.6-cp310-cp310-win_amd64.whl", hash = "sha256:61b047a0537bbc3afae10f134dc6393823882eb263088c271331602b672e52e9"}, + {file = "psycopg2_binary-2.9.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:964b4dfb7c1c1965ac4c1978b0f755cc4bd698e8aa2b7667c575fb5f04ebe06b"}, + {file = "psycopg2_binary-2.9.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:afe64e9b8ea66866a771996f6ff14447e8082ea26e675a295ad3bdbffdd72afb"}, + {file = "psycopg2_binary-2.9.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15e2ee79e7cf29582ef770de7dab3d286431b01c3bb598f8e05e09601b890081"}, + {file = "psycopg2_binary-2.9.6-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dfa74c903a3c1f0d9b1c7e7b53ed2d929a4910e272add6700c38f365a6002820"}, + {file = "psycopg2_binary-2.9.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b83456c2d4979e08ff56180a76429263ea254c3f6552cd14ada95cff1dec9bb8"}, + {file = "psycopg2_binary-2.9.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0645376d399bfd64da57148694d78e1f431b1e1ee1054872a5713125681cf1be"}, + {file = "psycopg2_binary-2.9.6-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e99e34c82309dd78959ba3c1590975b5d3c862d6f279f843d47d26ff89d7d7e1"}, + {file = "psycopg2_binary-2.9.6-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4ea29fc3ad9d91162c52b578f211ff1c931d8a38e1f58e684c45aa470adf19e2"}, + {file = "psycopg2_binary-2.9.6-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:4ac30da8b4f57187dbf449294d23b808f8f53cad6b1fc3623fa8a6c11d176dd0"}, + {file = "psycopg2_binary-2.9.6-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e78e6e2a00c223e164c417628572a90093c031ed724492c763721c2e0bc2a8df"}, + {file = "psycopg2_binary-2.9.6-cp311-cp311-win32.whl", hash = "sha256:1876843d8e31c89c399e31b97d4b9725a3575bb9c2af92038464231ec40f9edb"}, + {file = "psycopg2_binary-2.9.6-cp311-cp311-win_amd64.whl", hash = "sha256:b4b24f75d16a89cc6b4cdff0eb6a910a966ecd476d1e73f7ce5985ff1328e9a6"}, + {file = "psycopg2_binary-2.9.6-cp36-cp36m-win32.whl", hash = "sha256:498807b927ca2510baea1b05cc91d7da4718a0f53cb766c154c417a39f1820a0"}, + {file = "psycopg2_binary-2.9.6-cp36-cp36m-win_amd64.whl", hash = "sha256:0d236c2825fa656a2d98bbb0e52370a2e852e5a0ec45fc4f402977313329174d"}, + {file = "psycopg2_binary-2.9.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:34b9ccdf210cbbb1303c7c4db2905fa0319391bd5904d32689e6dd5c963d2ea8"}, + {file = "psycopg2_binary-2.9.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84d2222e61f313c4848ff05353653bf5f5cf6ce34df540e4274516880d9c3763"}, + {file = "psycopg2_binary-2.9.6-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30637a20623e2a2eacc420059be11527f4458ef54352d870b8181a4c3020ae6b"}, + {file = "psycopg2_binary-2.9.6-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8122cfc7cae0da9a3077216528b8bb3629c43b25053284cc868744bfe71eb141"}, + {file = "psycopg2_binary-2.9.6-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38601cbbfe600362c43714482f43b7c110b20cb0f8172422c616b09b85a750c5"}, + {file = "psycopg2_binary-2.9.6-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c7e62ab8b332147a7593a385d4f368874d5fe4ad4e341770d4983442d89603e3"}, + {file = "psycopg2_binary-2.9.6-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2ab652e729ff4ad76d400df2624d223d6e265ef81bb8aa17fbd63607878ecbee"}, + {file = "psycopg2_binary-2.9.6-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:c83a74b68270028dc8ee74d38ecfaf9c90eed23c8959fca95bd703d25b82c88e"}, + {file = "psycopg2_binary-2.9.6-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d4e6036decf4b72d6425d5b29bbd3e8f0ff1059cda7ac7b96d6ac5ed34ffbacd"}, + {file = "psycopg2_binary-2.9.6-cp37-cp37m-win32.whl", hash = "sha256:a8c28fd40a4226b4a84bdf2d2b5b37d2c7bd49486b5adcc200e8c7ec991dfa7e"}, + {file = "psycopg2_binary-2.9.6-cp37-cp37m-win_amd64.whl", hash = "sha256:51537e3d299be0db9137b321dfb6a5022caaab275775680e0c3d281feefaca6b"}, + {file = "psycopg2_binary-2.9.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cf4499e0a83b7b7edcb8dabecbd8501d0d3a5ef66457200f77bde3d210d5debb"}, + {file = "psycopg2_binary-2.9.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7e13a5a2c01151f1208d5207e42f33ba86d561b7a89fca67c700b9486a06d0e2"}, + {file = "psycopg2_binary-2.9.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e0f754d27fddcfd74006455b6e04e6705d6c31a612ec69ddc040a5468e44b4e"}, + {file = "psycopg2_binary-2.9.6-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d57c3fd55d9058645d26ae37d76e61156a27722097229d32a9e73ed54819982a"}, + {file = "psycopg2_binary-2.9.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:71f14375d6f73b62800530b581aed3ada394039877818b2d5f7fc77e3bb6894d"}, + {file = "psycopg2_binary-2.9.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:441cc2f8869a4f0f4bb408475e5ae0ee1f3b55b33f350406150277f7f35384fc"}, + {file = "psycopg2_binary-2.9.6-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:65bee1e49fa6f9cf327ce0e01c4c10f39165ee76d35c846ade7cb0ec6683e303"}, + {file = "psycopg2_binary-2.9.6-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:af335bac6b666cc6aea16f11d486c3b794029d9df029967f9938a4bed59b6a19"}, + {file = "psycopg2_binary-2.9.6-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:cfec476887aa231b8548ece2e06d28edc87c1397ebd83922299af2e051cf2827"}, + {file = "psycopg2_binary-2.9.6-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:65c07febd1936d63bfde78948b76cd4c2a411572a44ac50719ead41947d0f26b"}, + {file = "psycopg2_binary-2.9.6-cp38-cp38-win32.whl", hash = "sha256:4dfb4be774c4436a4526d0c554af0cc2e02082c38303852a36f6456ece7b3503"}, + {file = "psycopg2_binary-2.9.6-cp38-cp38-win_amd64.whl", hash = "sha256:02c6e3cf3439e213e4ee930308dc122d6fb4d4bea9aef4a12535fbd605d1a2fe"}, + {file = "psycopg2_binary-2.9.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e9182eb20f41417ea1dd8e8f7888c4d7c6e805f8a7c98c1081778a3da2bee3e4"}, + {file = "psycopg2_binary-2.9.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8a6979cf527e2603d349a91060f428bcb135aea2be3201dff794813256c274f1"}, + {file = "psycopg2_binary-2.9.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8338a271cb71d8da40b023a35d9c1e919eba6cbd8fa20a54b748a332c355d896"}, + {file = "psycopg2_binary-2.9.6-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e3ed340d2b858d6e6fb5083f87c09996506af483227735de6964a6100b4e6a54"}, + {file = "psycopg2_binary-2.9.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f81e65376e52f03422e1fb475c9514185669943798ed019ac50410fb4c4df232"}, + {file = "psycopg2_binary-2.9.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfb13af3c5dd3a9588000910178de17010ebcccd37b4f9794b00595e3a8ddad3"}, + {file = "psycopg2_binary-2.9.6-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4c727b597c6444a16e9119386b59388f8a424223302d0c06c676ec8b4bc1f963"}, + {file = "psycopg2_binary-2.9.6-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:4d67fbdaf177da06374473ef6f7ed8cc0a9dc640b01abfe9e8a2ccb1b1402c1f"}, + {file = "psycopg2_binary-2.9.6-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:0892ef645c2fabb0c75ec32d79f4252542d0caec1d5d949630e7d242ca4681a3"}, + {file = "psycopg2_binary-2.9.6-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:02c0f3757a4300cf379eb49f543fb7ac527fb00144d39246ee40e1df684ab514"}, + {file = "psycopg2_binary-2.9.6-cp39-cp39-win32.whl", hash = "sha256:c3dba7dab16709a33a847e5cd756767271697041fbe3fe97c215b1fc1f5c9848"}, + {file = "psycopg2_binary-2.9.6-cp39-cp39-win_amd64.whl", hash = "sha256:f6a88f384335bb27812293fdb11ac6aee2ca3f51d3c7820fe03de0a304ab6249"}, +] [[package]] name = "sqlalchemy" @@ -407,6 +495,18 @@ postgresql-psycopg2cffi = ["psycopg2cffi"] pymysql = ["pymysql"] sqlcipher = ["sqlcipher3-binary"] +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] + [[package]] name = "typing-extensions" version = "4.5.0" @@ -457,5 +557,5 @@ email = ["email-validator"] [metadata] lock-version = "2.0" -python-versions = "^3.10" -content-hash = "5d3f98b7404ad05269ae8b89d054613fcde49608e826d90f38b93f8d76a763d5" +python-versions = "^3.11" +content-hash = "123df985006374b7c4ede4587a2facef89306039e35af84ddc9c516eecd46c89" diff --git a/pyproject.toml b/pyproject.toml index 1a50fa2..57a527d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,16 +14,33 @@ flask-admin = "^1.6.1" flask-sqlalchemy = "^3.0.3" isbnlib = "^3.10.14" python = "^3.11" -python-dotenv = "^1.0.0" sqlalchemy = "^2.0.8" +psycopg2-binary = "^2.9.6" [tool.poetry.group.dev.dependencies] werkzeug = "^2.3.3" +poethepoet = "^0.20.0" [tool.poetry.scripts] cli = "worblehat.cli.main:main" dev = "worblehat.flaskapp.wsgi_dev:main" +[tool.poe.tasks] +clean = """ + rm -rf + ./**/__pycache__ + ./**/worblehat.sqlite +""" + +# Migration related +genmigration = "alembic revision --autogenerate -m" +migrate = "alembic upgrade head" +downmigrate = "alembic downgrade -1" +# Be careful with cleanmigrations. If you run migrate a database and then +# delete the migration file with this, there will be no easy way of downgrading +cleanmigrations = "git clean -f worblehat/models/migrations/versions" + + [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" diff --git a/worblehat/models/migrations/env.py b/worblehat/models/migrations/env.py new file mode 100644 index 0000000..662a6b6 --- /dev/null +++ b/worblehat/models/migrations/env.py @@ -0,0 +1,56 @@ +from alembic import context +from flask import current_app +from logging.config import fileConfig +from sqlalchemy import engine_from_config +from sqlalchemy import pool + +from worblehat.models import Base +from worblehat.services.config import Config + +config = context.config + +if config.config_file_name is not None: + fileConfig(config.config_file_name) + +Config.load_configuration({}) + +config.set_main_option('sqlalchemy.url', Config.db_string()) + +# This will make sure alembic doesn't generate empty migrations +# https://stackoverflow.com/questions/70203927/how-to-prevent-alembic-revision-autogenerate-from-making-revision-file-if-it-h +def _process_revision_directives(context, revision, directives): + if config.cmd_opts.autogenerate: + script = directives[0] + if script.upgrade_ops.is_empty(): + directives[:] = [] + print('No changes in schema detected. Not generating migration.') + +def run_migrations_online() -> None: + 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=Base.metadata, + + # Extended type checking with alembic when generating migrations + # https://alembic.sqlalchemy.org/en/latest/autogenerate.html#what-does-autogenerate-detect-and-what-does-it-not-detect + compare_type=True, + + # This is required for ALTER TABLE to work with sqlite. + # It should have no effect on postgreSQL + # https://alembic.sqlalchemy.org/en/latest/batch.html + render_as_batch=True, + process_revision_directives=_process_revision_directives, + ) + + with context.begin_transaction(): + context.run_migrations() + +# We don't have any good reasons to generate raw sql migrations, +# so the `run_migrations_offline` has been removed +run_migrations_online() diff --git a/worblehat/models/migrations/script.py.mako b/worblehat/models/migrations/script.py.mako new file mode 100644 index 0000000..55df286 --- /dev/null +++ b/worblehat/models/migrations/script.py.mako @@ -0,0 +1,24 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision = ${repr(up_revision)} +down_revision = ${repr(down_revision)} +branch_labels = ${repr(branch_labels)} +depends_on = ${repr(depends_on)} + + +def upgrade() -> None: + ${upgrades if upgrades else "pass"} + + +def downgrade() -> None: + ${downgrades if downgrades else "pass"} diff --git a/worblehat/models/migrations/versions/2023-05-06T1746_d51c7172d2f2_initial_migration.py b/worblehat/models/migrations/versions/2023-05-06T1746_d51c7172d2f2_initial_migration.py new file mode 100644 index 0000000..ff78269 --- /dev/null +++ b/worblehat/models/migrations/versions/2023-05-06T1746_d51c7172d2f2_initial_migration.py @@ -0,0 +1,175 @@ +"""initial_migration + +Revision ID: d51c7172d2f2 +Revises: +Create Date: 2023-05-06 17:46:39.230122 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'd51c7172d2f2' +down_revision = None +branch_labels = None +depends_on = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('Author', + sa.Column('uid', sa.Integer(), nullable=False), + sa.Column('name', sa.Text(), nullable=False), + sa.PrimaryKeyConstraint('uid', name=op.f('pk_Author')) + ) + with op.batch_alter_table('Author', schema=None) as batch_op: + batch_op.create_index(batch_op.f('ix_Author_name'), ['name'], unique=True) + + op.create_table('Bookcase', + sa.Column('description', sa.Text(), nullable=True), + sa.Column('uid', sa.Integer(), nullable=False), + sa.Column('name', sa.Text(), nullable=False), + sa.PrimaryKeyConstraint('uid', name=op.f('pk_Bookcase')) + ) + with op.batch_alter_table('Bookcase', schema=None) as batch_op: + batch_op.create_index(batch_op.f('ix_Bookcase_name'), ['name'], unique=True) + + op.create_table('Category', + sa.Column('description', sa.Text(), nullable=True), + sa.Column('uid', sa.Integer(), nullable=False), + sa.Column('name', sa.Text(), nullable=False), + sa.PrimaryKeyConstraint('uid', name=op.f('pk_Category')) + ) + with op.batch_alter_table('Category', schema=None) as batch_op: + batch_op.create_index(batch_op.f('ix_Category_name'), ['name'], unique=True) + + op.create_table('Language', + sa.Column('iso639_1_code', sa.String(length=2), nullable=False), + sa.Column('uid', sa.Integer(), nullable=False), + sa.Column('name', sa.Text(), nullable=False), + sa.PrimaryKeyConstraint('uid', name=op.f('pk_Language')) + ) + with op.batch_alter_table('Language', schema=None) as batch_op: + batch_op.create_index(batch_op.f('ix_Language_iso639_1_code'), ['iso639_1_code'], unique=True) + batch_op.create_index(batch_op.f('ix_Language_name'), ['name'], unique=True) + + op.create_table('MediaType', + sa.Column('description', sa.Text(), nullable=True), + sa.Column('uid', sa.Integer(), nullable=False), + sa.Column('name', sa.Text(), nullable=False), + sa.PrimaryKeyConstraint('uid', name=op.f('pk_MediaType')) + ) + with op.batch_alter_table('MediaType', schema=None) as batch_op: + batch_op.create_index(batch_op.f('ix_MediaType_name'), ['name'], unique=True) + + op.create_table('BookcaseShelf', + sa.Column('description', sa.Text(), nullable=True), + sa.Column('row', sa.SmallInteger(), nullable=False), + sa.Column('column', sa.SmallInteger(), nullable=False), + sa.Column('fk_bookcase_uid', sa.Integer(), nullable=False), + sa.Column('uid', sa.Integer(), nullable=False), + sa.ForeignKeyConstraint(['fk_bookcase_uid'], ['Bookcase.uid'], name=op.f('fk_BookcaseShelf_fk_bookcase_uid_Bookcase')), + sa.PrimaryKeyConstraint('uid', name=op.f('pk_BookcaseShelf')), + sa.UniqueConstraint('column', 'fk_bookcase_uid', 'row', name=op.f('uq_BookcaseShelf_column')) + ) + op.create_table('BookcaseItem', + sa.Column('isbn', sa.String(), nullable=False), + sa.Column('owner', sa.String(), nullable=False), + sa.Column('amount', sa.SmallInteger(), nullable=False), + sa.Column('fk_media_type_uid', sa.Integer(), nullable=False), + sa.Column('fk_bookcase_shelf_uid', sa.Integer(), nullable=True), + sa.Column('fk_language_uid', sa.Integer(), nullable=True), + sa.Column('uid', sa.Integer(), nullable=False), + sa.Column('name', sa.Text(), nullable=False), + sa.ForeignKeyConstraint(['fk_bookcase_shelf_uid'], ['BookcaseShelf.uid'], name=op.f('fk_BookcaseItem_fk_bookcase_shelf_uid_BookcaseShelf')), + sa.ForeignKeyConstraint(['fk_language_uid'], ['Language.uid'], name=op.f('fk_BookcaseItem_fk_language_uid_Language')), + sa.ForeignKeyConstraint(['fk_media_type_uid'], ['MediaType.uid'], name=op.f('fk_BookcaseItem_fk_media_type_uid_MediaType')), + sa.PrimaryKeyConstraint('uid', name=op.f('pk_BookcaseItem')) + ) + with op.batch_alter_table('BookcaseItem', schema=None) as batch_op: + batch_op.create_index(batch_op.f('ix_BookcaseItem_isbn'), ['isbn'], unique=True) + batch_op.create_index(batch_op.f('ix_BookcaseItem_name'), ['name'], unique=True) + + op.create_table('BookcaseItemBorrowing', + sa.Column('username', sa.String(), nullable=False), + sa.Column('start_time', sa.DateTime(), nullable=False), + sa.Column('end_time', sa.DateTime(), nullable=False), + sa.Column('delivered', sa.Boolean(), nullable=False), + sa.Column('fk_bookcase_item_uid', sa.Integer(), nullable=False), + sa.Column('uid', sa.Integer(), nullable=False), + sa.ForeignKeyConstraint(['fk_bookcase_item_uid'], ['BookcaseItem.uid'], name=op.f('fk_BookcaseItemBorrowing_fk_bookcase_item_uid_BookcaseItem')), + sa.PrimaryKeyConstraint('uid', name=op.f('pk_BookcaseItemBorrowing')) + ) + with op.batch_alter_table('BookcaseItemBorrowing', schema=None) as batch_op: + batch_op.create_index(batch_op.f('ix_BookcaseItemBorrowing_fk_bookcase_item_uid'), ['fk_bookcase_item_uid'], unique=False) + + op.create_table('BookcaseItemBorrowingQueue', + sa.Column('username', sa.String(), nullable=False), + sa.Column('entered_queue_time', sa.DateTime(), nullable=True), + sa.Column('should_notify_user', sa.Boolean(), nullable=True), + sa.Column('fk_bookcase_item_uid', sa.Integer(), nullable=False), + sa.Column('uid', sa.Integer(), nullable=False), + sa.ForeignKeyConstraint(['fk_bookcase_item_uid'], ['BookcaseItem.uid'], name=op.f('fk_BookcaseItemBorrowingQueue_fk_bookcase_item_uid_BookcaseItem')), + sa.PrimaryKeyConstraint('uid', name=op.f('pk_BookcaseItemBorrowingQueue')) + ) + with op.batch_alter_table('BookcaseItemBorrowingQueue', schema=None) as batch_op: + batch_op.create_index(batch_op.f('ix_BookcaseItemBorrowingQueue_fk_bookcase_item_uid'), ['fk_bookcase_item_uid'], unique=False) + + op.create_table('Item_Author', + sa.Column('fk_item_uid', sa.Integer(), nullable=False), + sa.Column('fk_author_uid', sa.Integer(), nullable=False), + sa.ForeignKeyConstraint(['fk_author_uid'], ['Author.uid'], name=op.f('fk_Item_Author_fk_author_uid_Author')), + sa.ForeignKeyConstraint(['fk_item_uid'], ['BookcaseItem.uid'], name=op.f('fk_Item_Author_fk_item_uid_BookcaseItem')), + sa.PrimaryKeyConstraint('fk_item_uid', 'fk_author_uid', name=op.f('pk_Item_Author')) + ) + op.create_table('Item_Category', + sa.Column('fk_item_uid', sa.Integer(), nullable=False), + sa.Column('fk_category_uid', sa.Integer(), nullable=False), + sa.ForeignKeyConstraint(['fk_category_uid'], ['Category.uid'], name=op.f('fk_Item_Category_fk_category_uid_Category')), + sa.ForeignKeyConstraint(['fk_item_uid'], ['BookcaseItem.uid'], name=op.f('fk_Item_Category_fk_item_uid_BookcaseItem')), + sa.PrimaryKeyConstraint('fk_item_uid', 'fk_category_uid', name=op.f('pk_Item_Category')) + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('Item_Category') + op.drop_table('Item_Author') + with op.batch_alter_table('BookcaseItemBorrowingQueue', schema=None) as batch_op: + batch_op.drop_index(batch_op.f('ix_BookcaseItemBorrowingQueue_fk_bookcase_item_uid')) + + op.drop_table('BookcaseItemBorrowingQueue') + with op.batch_alter_table('BookcaseItemBorrowing', schema=None) as batch_op: + batch_op.drop_index(batch_op.f('ix_BookcaseItemBorrowing_fk_bookcase_item_uid')) + + op.drop_table('BookcaseItemBorrowing') + with op.batch_alter_table('BookcaseItem', schema=None) as batch_op: + batch_op.drop_index(batch_op.f('ix_BookcaseItem_name')) + batch_op.drop_index(batch_op.f('ix_BookcaseItem_isbn')) + + op.drop_table('BookcaseItem') + op.drop_table('BookcaseShelf') + with op.batch_alter_table('MediaType', schema=None) as batch_op: + batch_op.drop_index(batch_op.f('ix_MediaType_name')) + + op.drop_table('MediaType') + with op.batch_alter_table('Language', schema=None) as batch_op: + batch_op.drop_index(batch_op.f('ix_Language_name')) + batch_op.drop_index(batch_op.f('ix_Language_iso639_1_code')) + + op.drop_table('Language') + with op.batch_alter_table('Category', schema=None) as batch_op: + batch_op.drop_index(batch_op.f('ix_Category_name')) + + op.drop_table('Category') + with op.batch_alter_table('Bookcase', schema=None) as batch_op: + batch_op.drop_index(batch_op.f('ix_Bookcase_name')) + + op.drop_table('Bookcase') + with op.batch_alter_table('Author', schema=None) as batch_op: + batch_op.drop_index(batch_op.f('ix_Author_name')) + + op.drop_table('Author') + # ### end Alembic commands ###