diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c76da29..220fc31 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,6 +21,7 @@ jobs: # All of these options are optional, so you can remove them if you are happy with the defaults concurrent_skipping: 'same_content' skip_after_successful_duplicate: 'true' + paths_ignore: '["**/README.md"]' pytest: needs: pre_job if: ${{ needs.pre_job.outputs.should_skip != 'true' }} diff --git a/README.md b/README.md index 3b58385..cdd5bb5 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ python3 -m fastapi_template One of the coolest features is that this project is extremely small and handy. You can choose between different databases and even ORMs. -Currently SQLAlchemy1.4 and TortoiseORM are supported. +Currently SQLAlchemy1.4, TortoiseORM and Ormar are supported. TUI and CLI and excellent code documentation. @@ -56,7 +56,7 @@ $ python -m fastapi_template --help usage: FastAPI template [-h] [--version] [--name PROJECT_NAME] [--description PROJECT_DESCRIPTION] [--db {none,sqlite,mysql,postgresql}] - [--orm {sqlalchemy,tortoise}] + [--orm {ormar,sqlalchemy,tortoise}] [--ci {none,gitlab,github}] [--redis] [--migrations] [--kube] [--dummy] [--routers] [--swagger] [--force] @@ -68,7 +68,7 @@ optional arguments: Project description --db {none,sqlite,mysql,postgresql} Database - --orm {sqlalchemy,tortoise} + --orm {ormar,sqlalchemy,tortoise} ORM --ci {none,gitlab,github} Choose CI support diff --git a/fastapi_template/cli.py b/fastapi_template/cli.py index c0f0fde..b0933e1 100644 --- a/fastapi_template/cli.py +++ b/fastapi_template/cli.py @@ -56,7 +56,7 @@ def parse_args(): "--orm", help="ORM", type=str, - choices=list(map(attrgetter("value"), ORM)), + choices=[orm.value for orm in ORM if orm != ORM.none], default=None, dest="orm", ) @@ -65,7 +65,7 @@ def parse_args(): help="Choose CI support", default=None, type=str, - choices=list(map(attrgetter("value"), CIType)), + choices=[ci.value for ci in CIType], dest="ci_type", ) parser.add_argument( @@ -192,7 +192,7 @@ def read_user_input(current_context: BuilderContext) -> BuilderContext: current_context.orm = radiolist_dialog( "ORM", text="Which ORM do you want?", - values=[(orm, orm.value) for orm in list(ORM)], + values=[(orm, orm.value) for orm in list(ORM) if orm != ORM.none], ).run() if current_context.orm is None: raise KeyboardInterrupt() diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/deploy/kube/app.yml b/fastapi_template/template/{{cookiecutter.project_name}}/deploy/kube/app.yml index f0d843b..1046b78 100644 --- a/fastapi_template/template/{{cookiecutter.project_name}}/deploy/kube/app.yml +++ b/fastapi_template/template/{{cookiecutter.project_name}}/deploy/kube/app.yml @@ -28,11 +28,13 @@ spec: args: - -c - >- + {%- if cookiecutter.enable_migrations == "True" %} {%- if cookiecutter.orm in ['sqlalchemy', 'ormar'] %} alembic upgrade head && {%- elif cookiecutter.orm == 'tortoise' %} aerich upgrade && {%- endif %} + {%- endif %} python -m {{cookiecutter.project_name }} {%- endif %} env: @@ -40,12 +42,10 @@ spec: value: "0.0.0.0" - name: {{cookiecutter.project_name | upper }}_WORKERS_COUNT value: "10" - {%- if cookiecutter.db_info.name != "none" %} - {%- if cookiecutter.db_info.name != "sqlite" %} + {%- if cookiecutter.db_info.name not in ["none", "sqlite"] %} - name: {{cookiecutter.project_name | upper }}_DB_HOST value: "{{cookiecutter.kube_name}}-db-service" {%- endif %} - {%- endif %} {%- if cookiecutter.enable_redis == 'True' %} - name: {{cookiecutter.project_name | upper }}_REDIS_HOST value: "{{cookiecutter.kube_name}}-redis-service" diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/deploy/kube/db.yml b/fastapi_template/template/{{cookiecutter.project_name}}/deploy/kube/db.yml index 32af3d7..4bd8efe 100644 --- a/fastapi_template/template/{{cookiecutter.project_name}}/deploy/kube/db.yml +++ b/fastapi_template/template/{{cookiecutter.project_name}}/deploy/kube/db.yml @@ -53,6 +53,7 @@ spec: - port: {{cookiecutter.db_info.port}} targetPort: {{cookiecutter.db_info.port}} --- +{%- if cookiecutter.enable_migrations == "True" %} apiVersion: batch/v1 kind: Job metadata: @@ -87,5 +88,5 @@ spec: command: ["./wait-for-it.sh", "-t", "60", "{{cookiecutter.kube_name}}-db-service:{{cookiecutter.db_info.port}}"] restartPolicy: Never - --- +{%- endif %} diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db_tortoise/config.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db_tortoise/config.py index e7e3737..791eedb 100644 --- a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db_tortoise/config.py +++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db_tortoise/config.py @@ -9,7 +9,7 @@ }, "apps": { "models": { - "models": ["aerich.models"] + MODELS_MODULES, + "models": MODELS_MODULES {%- if cookiecutter.enable_migrations == "True" %} + ["aerich.models"] {%- endif %} , "default_connection": "default", }, }, diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/application.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/application.py index cc5e51b..9a176ff 100644 --- a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/application.py +++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/application.py @@ -57,7 +57,14 @@ def get_app() -> FastAPI: {% endif %} {%- if cookiecutter.orm == 'tortoise' %} - register_tortoise(app, config=TORTOISE_CONFIG, add_exception_handlers=True) + register_tortoise( + app, + config=TORTOISE_CONFIG, + add_exception_handlers=True, + {%- if cookiecutter.enable_migrations == "False" %} + generate_schemas=True, + {%- endif %} + ) {%- endif %} return app diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/lifetime.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/lifetime.py index 13a189f..958c8e8 100644 --- a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/lifetime.py +++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/lifetime.py @@ -10,6 +10,11 @@ {%- if cookiecutter.orm == "ormar" %} from {{cookiecutter.project_name}}.db.config import database +{%- if cookiecutter.db_info.name != "none" and cookiecutter.enable_migrations == "False" %} +from sqlalchemy.engine import create_engine +from {{cookiecutter.project_name}}.db.meta import meta +from {{cookiecutter.project_name}}.db.models import load_all_models +{%- endif %} {%- endif %} {%- if cookiecutter.orm == "sqlalchemy" %} @@ -21,6 +26,11 @@ ) from sqlalchemy.orm import sessionmaker +{%- if cookiecutter.db_info.name != "none" and cookiecutter.enable_migrations == "False" %} +from {{cookiecutter.project_name}}.db.meta import meta +from {{cookiecutter.project_name}}.db.models import load_all_models +{%- endif %} + def _setup_db(app: FastAPI) -> None: """ @@ -47,11 +57,34 @@ def _setup_db(app: FastAPI) -> None: {% if cookiecutter.enable_redis == "True" %} def _setup_redis(app: FastAPI) -> None: + """ + Initialize redis connection. + + :param app: current FastAPI app. + """ app.state.redis_pool = aioredis.ConnectionPool.from_url( str(settings.redis_url), ) {%- endif %} +{%- if cookiecutter.db_info.name != "none" and cookiecutter.enable_migrations == "False" %} +{%- if cookiecutter.orm in ["ormar", "sqlalchemy"] %} +async def _create_tables() -> None: + """Populates tables in the database.""" + load_all_models() + {%- if cookiecutter.orm == "ormar" %} + engine = create_engine(str(settings.db_url)) + with engine.connect() as connection: + meta.create_all(connection) + engine.dispose() + {%- elif cookiecutter.orm == "sqlalchemy" %} + engine = create_async_engine(str(settings.db_url)) + async with engine.begin() as connection: + await connection.run_sync(meta.create_all) + await engine.dispose() + {%- endif %} +{%- endif %} +{%- endif %} def startup(app: FastAPI) -> Callable[[], Awaitable[None]]: """ @@ -67,9 +100,14 @@ def startup(app: FastAPI) -> Callable[[], Awaitable[None]]: async def _startup() -> None: # noqa: WPS430 {%- if cookiecutter.orm == "sqlalchemy" %} _setup_db(app) - {% elif cookiecutter.orm == "ormar" %} + {%- elif cookiecutter.orm == "ormar" %} await database.connect() {%- endif %} + {%- if cookiecutter.db_info.name != "none" and cookiecutter.enable_migrations == "False" %} + {%- if cookiecutter.orm in ["ormar", "sqlalchemy"] %} + await _create_tables() + {%- endif %} + {%- endif %} {%- if cookiecutter.enable_redis == "True" %} _setup_redis(app) {%- endif %} diff --git a/pyproject.toml b/pyproject.toml index 180b297..b6d1898 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "fastapi_template" -version = "3.2.0" +version = "3.2.1" description = "Feature-rich robust FastAPI template" authors = ["Pavel Kirilin "] packages = [{ include = "fastapi_template" }]