From c93a74b0f4874b59854fa924f6d30ff24dfac3c2 Mon Sep 17 00:00:00 2001 From: Klemek Date: Wed, 1 Jul 2026 11:43:10 +0200 Subject: [PATCH] feat: database with tortoise orm --- .editorconfig | 3 ++ Makefile | 18 +++++++ app/__main__.py | 2 + app/database.py | 33 ++++++++++++ app/migrations/0001_initial.py | 21 ++++++++ app/migrations/__init__.py | 0 app/models/__init__.py | 1 + app/models/base.py | 11 ++++ app/models/comment.py | 7 +++ app/params.py | 8 +++ compose.yml | 33 ++++++++++++ pyproject.toml | 6 ++- tortoise-dev.json | 12 +++++ uv.lock | 94 ++++++++++++++++++++++++++++++++++ 14 files changed, 248 insertions(+), 1 deletion(-) create mode 100644 app/database.py create mode 100644 app/migrations/0001_initial.py create mode 100644 app/migrations/__init__.py create mode 100644 app/models/__init__.py create mode 100644 app/models/base.py create mode 100644 app/models/comment.py create mode 100644 tortoise-dev.json diff --git a/.editorconfig b/.editorconfig index f48867c..f7495ba 100644 --- a/.editorconfig +++ b/.editorconfig @@ -11,6 +11,9 @@ charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true +[yaml] +indent_size = 2 + [Makefile] indent_style = tab indent_size = 2 diff --git a/Makefile b/Makefile index fd74ff2..c3b365b 100644 --- a/Makefile +++ b/Makefile @@ -23,6 +23,13 @@ endif DOCKER ?= podman +APP_EXEC ?= $(DOCKER) compose exec app +APP_RUN ?= $(DOCKER) compose run --rm -it app +TORTOISE ?= $(APP_RUN) uv run tortoise + +POSTGRES_EXEC ?= $(DOCKER) compose exec postgres +PSQL_EXEC_BIN ?= $(POSTGRES_EXEC) psql --username="tout-doux" --dbname="tout-doux" + OPEN ?= xdg-open # DOCS @@ -75,6 +82,9 @@ stop: docker-compose-down .PHONY: logs logs: docker-compose-logs +.PHONY: migrations +migrations: tortoise-makemigrations + # TOOLS .PHONY: uv-sync @@ -137,6 +147,14 @@ docker-compose-down-%: ## docker compose down (container) docker-compose-logs: ## docker compose logs $(DOCKER) compose logs -f || true +.PHONY: tortoise-% +tortoise-%: .venv ## tortoise (command) + $(TORTOISE) --config-file tortoise-dev.json $* + +.PHONY: psql +psql: ## psql + $(PSQL_EXEC_BIN) + .PHONY: release-% release-%: .venv ## release major/minor/patch $(UV) version --bump=$* diff --git a/app/__main__.py b/app/__main__.py index c6cc00c..2919d1c 100644 --- a/app/__main__.py +++ b/app/__main__.py @@ -2,6 +2,7 @@ import logging import sys from app import PKG_NAME, PKG_VERSION +from app.database import Database from app.logs import setup_logs from app.params import parse_parameters from app.server import Server @@ -11,5 +12,6 @@ def main() -> int: params = parse_parameters(sys.argv[1:]) setup_logs(params) logging.getLogger().info("%s %s", PKG_NAME, PKG_VERSION) + Database(params).init() Server(params).register_routes().start() return 0 diff --git a/app/database.py b/app/database.py new file mode 100644 index 0000000..af99fa6 --- /dev/null +++ b/app/database.py @@ -0,0 +1,33 @@ +import logging +import typing + +import tortoise +import tortoise.config +import tortoise.migrations.api + +if typing.TYPE_CHECKING: + from app.params import Parameters + + +class Database: + def __init__(self, params: Parameters) -> None: + self.env = params.env + self.logger = logging.getLogger(self.__class__.__name__) + self.config = tortoise.config.TortoiseConfig( + connections={"default": tortoise.config.DBUrlConfig(params.db_url)}, + apps={ + "models": tortoise.config.AppConfig( + models=["app.models"], + default_connection="default", + migrations="app.migrations", + ) + }, + ) + + def init(self) -> typing.Self: + self.logger.info("Initializing database") + tortoise.run_async(tortoise.Tortoise.init(config=self.config)) + self.logger.info("Migrating database") + tortoise.run_async(tortoise.migrations.api.migrate(config=self.config)) + self.logger.info("Database ready") + return self diff --git a/app/migrations/0001_initial.py b/app/migrations/0001_initial.py new file mode 100644 index 0000000..2e9a5e7 --- /dev/null +++ b/app/migrations/0001_initial.py @@ -0,0 +1,21 @@ +from tortoise import migrations +from tortoise.migrations import operations as ops +from uuid import uuid4 +from tortoise import fields + +class Migration(migrations.Migration): + initial = True + + operations = [ + ops.CreateModel( + name='Comment', + fields=[ + ('id', fields.UUIDField(primary_key=True, default=uuid4, unique=True, db_index=True)), + ('created_at', fields.DatetimeField(auto_now=False, auto_now_add=True)), + ('updated_at', fields.DatetimeField(auto_now=True, auto_now_add=False)), + ('content', fields.TextField(unique=False)), + ], + options={'table': 'comment', 'app': 'models', 'pk_attr': 'id'}, + bases=['AbstractModel'], + ), + ] diff --git a/app/migrations/__init__.py b/app/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/models/__init__.py b/app/models/__init__.py new file mode 100644 index 0000000..d2cdee0 --- /dev/null +++ b/app/models/__init__.py @@ -0,0 +1 @@ +from .comment import Comment # noqa: F401 diff --git a/app/models/base.py b/app/models/base.py new file mode 100644 index 0000000..80833f5 --- /dev/null +++ b/app/models/base.py @@ -0,0 +1,11 @@ +import tortoise +import tortoise.models + + +class AbstractModel(tortoise.models.Model): + id = tortoise.fields.UUIDField(primary_key=True) + created_at = tortoise.fields.DatetimeField(auto_now_add=True) + updated_at = tortoise.fields.DatetimeField(auto_now=True) + + class Meta: + abstract = True diff --git a/app/models/comment.py b/app/models/comment.py new file mode 100644 index 0000000..6e6df2c --- /dev/null +++ b/app/models/comment.py @@ -0,0 +1,7 @@ +import tortoise + +from app.models.base import AbstractModel + + +class Comment(AbstractModel): + content = tortoise.fields.TextField() diff --git a/app/params.py b/app/params.py index 182904f..4f92c0c 100644 --- a/app/params.py +++ b/app/params.py @@ -15,6 +15,7 @@ class Parameters: port: int = 5000 workers: int = 4 timeout: int = 120 + db_url: str = "postgres://postgres:5432/tout-doux" @classmethod def from_namespace(cls, args: argparse.Namespace) -> Parameters: @@ -112,5 +113,12 @@ def parse_parameters(args: typing.Sequence[str]) -> Parameters: default=default_values.timeout, help_txt="WSGI timeout", ) + __add_arg_str( + parser, + "--db-url", + env_var="DB_URL", + default=default_values.db_url, + help_txt="database connection url", + ) parsed_args = parser.parse_args(args) return Parameters.from_namespace(parsed_args) diff --git a/compose.yml b/compose.yml index 0187d35..cf39bc1 100644 --- a/compose.yml +++ b/compose.yml @@ -1,6 +1,9 @@ networks: backend: +volumes: + postgres-data: + services: app: image: astral/uv:python3.14-alpine @@ -11,9 +14,13 @@ services: - APP_ENV=dev - PORT=5000 - BIND=0.0.0.0 + - DB_URL=postgres://tout-doux:tout-doux@postgres:5432/tout-doux command: uv run tout-doux --debug networks: - backend + depends_on: + postgres: + condition: service_healthy vite: image: oven/bun:latest @@ -25,3 +32,29 @@ services: - "5173:5173" networks: - backend + + postgres: + image: postgres:17 + healthcheck: + test: + [ + "CMD-SHELL", + "pg_isready", + "-d", + "tout-doux", + "-U", + "tout-doux", + ] + interval: 10s + timeout: 60s + retries: 3 + start_period: 60s + environment: + - POSTGRES_DB=tout-doux + - POSTGRES_USER=tout-doux + - POSTGRES_PASSWORD=tout-doux + - PGDATA=/var/lib/postgresql/data/pgdata + volumes: + - "postgres-data:/var/lib/postgresql/data" + networks: + - backend diff --git a/pyproject.toml b/pyproject.toml index ff5489a..ccfaa8a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,6 +7,7 @@ requires-python = ">=3.14" dependencies = [ "flask>=3.1.3,<4.0.0", "gunicorn>=26.0.0,<27.0.0", + "tortoise-orm[asyncpg]>=1.1.7,<2.0.0", ] [project.scripts] @@ -31,12 +32,15 @@ add-bounds = "major" module-root = "" module-name = "app" +[tool.ruff] +exclude = ["app/migrations"] + [tool.ruff.lint] select = ["ALL"] ignore = ["D", "E501", "ANN401", "BLE001", "COM812", "S101", "PT", "S104"] [tool.ty.src] -include = ["app", "tests"] +include = ["app"] [tool.ty.environment] python = "./.venv/bin/python3" diff --git a/tortoise-dev.json b/tortoise-dev.json new file mode 100644 index 0000000..ba70d5c --- /dev/null +++ b/tortoise-dev.json @@ -0,0 +1,12 @@ +{ + "connections": { + "default": "postgres://tout-doux:tout-doux@postgres:5432/tout-doux" + }, + "apps": { + "models": { + "models": ["app.models"], + "default_connection": "default", + "migrations": "app.migrations" + } + } +} diff --git a/uv.lock b/uv.lock index 8feedb4..f172677 100644 --- a/uv.lock +++ b/uv.lock @@ -2,6 +2,51 @@ version = 1 revision = 3 requires-python = ">=3.14" +[[package]] +name = "aiosqlite" +version = "0.22.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/8a/64761f4005f17809769d23e518d915db74e6310474e733e3593cfc854ef1/aiosqlite-0.22.1.tar.gz", hash = "sha256:043e0bd78d32888c0a9ca90fc788b38796843360c855a7262a532813133a0650", size = 14821, upload-time = "2025-12-23T19:25:43.997Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/b7/e3bf5133d697a08128598c8d0abc5e16377b51465a33756de24fa7dee953/aiosqlite-0.22.1-py3-none-any.whl", hash = "sha256:21c002eb13823fad740196c5a2e9d8e62f6243bd9e7e4a1f87fb5e44ecb4fceb", size = 17405, upload-time = "2025-12-23T19:25:42.139Z" }, +] + +[[package]] +name = "anyio" +version = "4.14.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3b/72/5562aabb8dd7181e8e860622a38bea08d17842b99ecd4c91f84ac95251b0/anyio-4.14.1.tar.gz", hash = "sha256:8d648a3544c1a700e3ff78615cd679e4c5c3f149904287e73687b2596963629e", size = 254831, upload-time = "2026-06-24T20:56:06.017Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b0/7b/90df4a0a816d98d6ea26f559d87836d494a2cf1fcf063be67df50a7bcc30/anyio-4.14.1-py3-none-any.whl", hash = "sha256:4e5533c5b8ff0a24f5d7a176cbe6877129cd183893f66b537f8f227d10527d72", size = 124875, upload-time = "2026-06-24T20:56:04.413Z" }, +] + +[[package]] +name = "asyncpg" +version = "0.31.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fe/cc/d18065ce2380d80b1bcce927c24a2642efd38918e33fd724bc4bca904877/asyncpg-0.31.0.tar.gz", hash = "sha256:c989386c83940bfbd787180f2b1519415e2d3d6277a70d9d0f0145ac73500735", size = 993667, upload-time = "2025-11-24T23:27:00.812Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/36/e9450d62e84a13aea6580c83a47a437f26c7ca6fa0f0fd40b6670793ea30/asyncpg-0.31.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:f6b56b91bb0ffc328c4e3ed113136cddd9deefdf5f79ab448598b9772831df44", size = 660867, upload-time = "2025-11-24T23:26:17.631Z" }, + { url = "https://files.pythonhosted.org/packages/82/4b/1d0a2b33b3102d210439338e1beea616a6122267c0df459ff0265cd5807a/asyncpg-0.31.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:334dec28cf20d7f5bb9e45b39546ddf247f8042a690bff9b9573d00086e69cb5", size = 638349, upload-time = "2025-11-24T23:26:19.689Z" }, + { url = "https://files.pythonhosted.org/packages/41/aa/e7f7ac9a7974f08eff9183e392b2d62516f90412686532d27e196c0f0eeb/asyncpg-0.31.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:98cc158c53f46de7bb677fd20c417e264fc02b36d901cc2a43bd6cb0dc6dbfd2", size = 3410428, upload-time = "2025-11-24T23:26:21.275Z" }, + { url = "https://files.pythonhosted.org/packages/6f/de/bf1b60de3dede5c2731e6788617a512bc0ebd9693eac297ee74086f101d7/asyncpg-0.31.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9322b563e2661a52e3cdbc93eed3be7748b289f792e0011cb2720d278b366ce2", size = 3471678, upload-time = "2025-11-24T23:26:23.627Z" }, + { url = "https://files.pythonhosted.org/packages/46/78/fc3ade003e22d8bd53aaf8f75f4be48f0b460fa73738f0391b9c856a9147/asyncpg-0.31.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:19857a358fc811d82227449b7ca40afb46e75b33eb8897240c3839dd8b744218", size = 3313505, upload-time = "2025-11-24T23:26:25.235Z" }, + { url = "https://files.pythonhosted.org/packages/bf/e9/73eb8a6789e927816f4705291be21f2225687bfa97321e40cd23055e903a/asyncpg-0.31.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ba5f8886e850882ff2c2ace5732300e99193823e8107e2c53ef01c1ebfa1e85d", size = 3434744, upload-time = "2025-11-24T23:26:26.944Z" }, + { url = "https://files.pythonhosted.org/packages/08/4b/f10b880534413c65c5b5862f79b8e81553a8f364e5238832ad4c0af71b7f/asyncpg-0.31.0-cp314-cp314-win32.whl", hash = "sha256:cea3a0b2a14f95834cee29432e4ddc399b95700eb1d51bbc5bfee8f31fa07b2b", size = 532251, upload-time = "2025-11-24T23:26:28.404Z" }, + { url = "https://files.pythonhosted.org/packages/d3/2d/7aa40750b7a19efa5d66e67fc06008ca0f27ba1bd082e457ad82f59aba49/asyncpg-0.31.0-cp314-cp314-win_amd64.whl", hash = "sha256:04d19392716af6b029411a0264d92093b6e5e8285ae97a39957b9a9c14ea72be", size = 604901, upload-time = "2025-11-24T23:26:30.34Z" }, + { url = "https://files.pythonhosted.org/packages/ce/fe/b9dfe349b83b9dee28cc42360d2c86b2cdce4cb551a2c2d27e156bcac84d/asyncpg-0.31.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:bdb957706da132e982cc6856bb2f7b740603472b54c3ebc77fe60ea3e57e1bd2", size = 702280, upload-time = "2025-11-24T23:26:32Z" }, + { url = "https://files.pythonhosted.org/packages/6a/81/e6be6e37e560bd91e6c23ea8a6138a04fd057b08cf63d3c5055c98e81c1d/asyncpg-0.31.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6d11b198111a72f47154fa03b85799f9be63701e068b43f84ac25da0bda9cb31", size = 682931, upload-time = "2025-11-24T23:26:33.572Z" }, + { url = "https://files.pythonhosted.org/packages/a6/45/6009040da85a1648dd5bc75b3b0a062081c483e75a1a29041ae63a0bf0dc/asyncpg-0.31.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:18c83b03bc0d1b23e6230f5bf8d4f217dc9bc08644ce0502a9d91dc9e634a9c7", size = 3581608, upload-time = "2025-11-24T23:26:35.638Z" }, + { url = "https://files.pythonhosted.org/packages/7e/06/2e3d4d7608b0b2b3adbee0d0bd6a2d29ca0fc4d8a78f8277df04e2d1fd7b/asyncpg-0.31.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e009abc333464ff18b8f6fd146addffd9aaf63e79aa3bb40ab7a4c332d0c5e9e", size = 3498738, upload-time = "2025-11-24T23:26:37.275Z" }, + { url = "https://files.pythonhosted.org/packages/7d/aa/7d75ede780033141c51d83577ea23236ba7d3a23593929b32b49db8ed36e/asyncpg-0.31.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3b1fbcb0e396a5ca435a8826a87e5c2c2cc0c8c68eb6fadf82168056b0e53a8c", size = 3401026, upload-time = "2025-11-24T23:26:39.423Z" }, + { url = "https://files.pythonhosted.org/packages/ba/7a/15e37d45e7f7c94facc1e9148c0e455e8f33c08f0b8a0b1deb2c5171771b/asyncpg-0.31.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8df714dba348efcc162d2adf02d213e5fab1bd9f557e1305633e851a61814a7a", size = 3429426, upload-time = "2025-11-24T23:26:41.032Z" }, + { url = "https://files.pythonhosted.org/packages/13/d5/71437c5f6ae5f307828710efbe62163974e71237d5d46ebd2869ea052d10/asyncpg-0.31.0-cp314-cp314t-win32.whl", hash = "sha256:1b41f1afb1033f2b44f3234993b15096ddc9cd71b21a42dbd87fc6a57b43d65d", size = 614495, upload-time = "2025-11-24T23:26:42.659Z" }, + { url = "https://files.pythonhosted.org/packages/3c/d7/8fb3044eaef08a310acfe23dae9a8e2e07d305edc29a53497e52bc76eca7/asyncpg-0.31.0-cp314-cp314t-win_amd64.whl", hash = "sha256:bd4107bb7cdd0e9e65fae66a62afd3a249663b844fa34d479f6d5b3bef9c04c3", size = 706062, upload-time = "2025-11-24T23:26:44.086Z" }, +] + [[package]] name = "blinker" version = "1.9.0" @@ -100,6 +145,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e6/40/9c2384fc2be4ad25dd4a49decd5ad9ea5a3639814c11bd40ab77cb9f0a14/gunicorn-26.0.0-py3-none-any.whl", hash = "sha256:40233d26a5f0d1872916188c276e21641155111c2853f0c2cd55260aec0d24fc", size = 212009, upload-time = "2026-05-05T06:38:23.007Z" }, ] +[[package]] +name = "idna" +version = "3.18" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/63/9496c57188a2ee585e0f1db071d75089a11e98aa86eb99d9d7618fc1edce/idna-3.18.tar.gz", hash = "sha256:ffb385a7e039654cef1ab9ef32c6fafe283c0c0467bba1d9029738ce4a14a848", size = 196711, upload-time = "2026-06-02T14:34:07.794Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/5e/d4e9f1a599fb8e573b7b87160658329fbf28d19eac2718f51fc3def3aa5a/idna-3.18-py3-none-any.whl", hash = "sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2", size = 65455, upload-time = "2026-06-02T14:34:06.319Z" }, +] + [[package]] name = "iniconfig" version = "2.3.0" @@ -109,6 +163,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, ] +[[package]] +name = "iso8601" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b9/f3/ef59cee614d5e0accf6fd0cbba025b93b272e626ca89fb70a3e9187c5d15/iso8601-2.1.0.tar.gz", hash = "sha256:6b1d3829ee8921c4301998c909f7829fa9ed3cbdac0d3b16af2d743aed1ba8df", size = 6522, upload-time = "2023-10-03T00:25:39.317Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6c/0c/f37b6a241f0759b7653ffa7213889d89ad49a2b76eb2ddf3b57b2738c347/iso8601-2.1.0-py3-none-any.whl", hash = "sha256:aac4145c4dcb66ad8b648a02830f5e2ff6c24af20f4f482689be402db2429242", size = 7545, upload-time = "2023-10-03T00:25:32.304Z" }, +] + [[package]] name = "itsdangerous" version = "2.2.0" @@ -187,6 +250,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, ] +[[package]] +name = "pypika-tortoise" +version = "0.6.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c2/06/89fe5fff93c5a01dbdeb9f3d843a7e997dc6e3a87222a260a164ff91fb81/pypika_tortoise-0.6.5.tar.gz", hash = "sha256:64d96c9b88450f6360ad22a7063933b6a90961a7317f04b2b63c98fd5d705506", size = 81468, upload-time = "2026-03-13T20:44:54.439Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6c/b8/502910eb8b315f719d8f6a6509f13a38b6c4c05378f14ac151ff347bff0a/pypika_tortoise-0.6.5-py3-none-any.whl", hash = "sha256:9194ac6ce6ac9bdfc6e959c831c5788ef05ee1371e82ba281b0eb75f4a2bd4f1", size = 47936, upload-time = "2026-03-13T20:44:53.541Z" }, +] + [[package]] name = "pytest" version = "9.0.3" @@ -228,6 +300,26 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c0/98/6beb4b351e472e5f4c4613f7c35a5290b8be2497e183825310c4c3a3984b/ruff-0.15.12-py3-none-win_arm64.whl", hash = "sha256:a538f7a82d061cee7be55542aca1d86d1393d55d81d4fcc314370f4340930d4f", size = 11120821, upload-time = "2026-04-24T18:16:57.979Z" }, ] +[[package]] +name = "tortoise-orm" +version = "1.1.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiosqlite" }, + { name = "anyio" }, + { name = "iso8601", marker = "python_full_version < '4'" }, + { name = "pypika-tortoise" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1d/55/e75d3ae0dd2c96cf961bf068f465fb62ec481d802beb65f406620bfd40a0/tortoise_orm-1.1.7.tar.gz", hash = "sha256:a0f0f27f9c92547739f0a3f0862c257ab64d8baadbc0e548cbcfa2d70bcbe275", size = 387489, upload-time = "2026-03-21T20:03:29.087Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bf/9b/e7e5b4f4e9433b9d9c389afc62ea0ed2597626842dc0f76dd7b54eb32cdd/tortoise_orm-1.1.7-py3-none-any.whl", hash = "sha256:b640b786905891cc120aa16d1a7af18cd54b111a812f9b1c1d1c7b294d62949d", size = 272437, upload-time = "2026-03-21T20:03:27.539Z" }, +] + +[package.optional-dependencies] +asyncpg = [ + { name = "asyncpg" }, +] + [[package]] name = "tout-doux" version = "0.0.0" @@ -235,6 +327,7 @@ source = { editable = "." } dependencies = [ { name = "flask" }, { name = "gunicorn" }, + { name = "tortoise-orm", extra = ["asyncpg"] }, ] [package.dev-dependencies] @@ -249,6 +342,7 @@ dev = [ requires-dist = [ { name = "flask", specifier = ">=3.1.3,<4.0.0" }, { name = "gunicorn", specifier = ">=26.0.0,<27.0.0" }, + { name = "tortoise-orm", extras = ["asyncpg"], specifier = ">=1.1.7,<2.0.0" }, ] [package.metadata.requires-dev]