From 44eec3045ccb1630aba66a31cacf4683a0760dec Mon Sep 17 00:00:00 2001 From: Pavel Kirilin Date: Tue, 5 May 2026 16:17:57 +0200 Subject: [PATCH] Fixed post_gen and fastapi-users integration. --- .../template/hooks/post_gen_project.py | 7 ++- .../conditional_files.toml | 3 +- .../pyproject.toml | 1 + .../db_sa/migrations/script.py.mako | 3 ++ .../versions/2021-08-16-16-55_2b7380507a71.py | 2 +- .../versions/2026-05-05-14-37_8caca4abd7b4.py | 52 +++++++++++++++++++ .../db_sa/models/users.py | 8 +-- 7 files changed, 68 insertions(+), 8 deletions(-) create mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db_sa/migrations/versions/2026-05-05-14-37_8caca4abd7b4.py diff --git a/fastapi_template/template/hooks/post_gen_project.py b/fastapi_template/template/hooks/post_gen_project.py index bbe6aaaf..14870efa 100644 --- a/fastapi_template/template/hooks/post_gen_project.py +++ b/fastapi_template/template/hooks/post_gen_project.py @@ -81,7 +81,7 @@ def run_cmd(cmd: str, ignore_error: bool = False): cprint(out.stdout.decode(errors="replace"), "red") if out.stderr: cprint(out.stderr.decode(errors="replace"), "red") - exit(1) + raise ValueError() def init_repo(): @@ -102,4 +102,7 @@ def init_repo(): if __name__ == "__main__": delete_resources_for_disabled_features() replace_resources() - init_repo() + try: + init_repo() + except ValueError: + pass diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/conditional_files.toml b/fastapi_template/template/{{cookiecutter.project_name}}/conditional_files.toml index b02e0ae8..8d07d9fb 100644 --- a/fastapi_template/template/{{cookiecutter.project_name}}/conditional_files.toml +++ b/fastapi_template/template/{{cookiecutter.project_name}}/conditional_files.toml @@ -123,7 +123,8 @@ name = "User models" enabled = "{{cookiecutter.add_users}}" resources = [ "{{cookiecutter.project_name}}/web/api/users", - "{{cookiecutter.project_name}}/db_sa/models/users.py" + "{{cookiecutter.project_name}}/db_sa/models/users.py", + "{{cookiecutter.project_name}}/db_sa/migrations/versions/2026-05-05-14-37_8caca4abd7b4.py", ] [[features]] diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/pyproject.toml b/fastapi_template/template/{{cookiecutter.project_name}}/pyproject.toml index ec73e56b..7a8d03f0 100644 --- a/fastapi_template/template/{{cookiecutter.project_name}}/pyproject.toml +++ b/fastapi_template/template/{{cookiecutter.project_name}}/pyproject.toml @@ -290,6 +290,7 @@ lint.ignore = [ "ANN401", # typing.Any are disallowed in `**kwargs "PLR0913", # Too many arguments for function call "D106", # Missing docstring in public nested class + "UP043", # Unnecessary default argument specified (conflicts with mypy). ] exclude = [ {%- if cookiecutter.orm in ["ormar", "sqlalchemy", "piccolo", "tortoise"] %} diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db_sa/migrations/script.py.mako b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db_sa/migrations/script.py.mako index 395e017f..2c133564 100644 --- a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db_sa/migrations/script.py.mako +++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db_sa/migrations/script.py.mako @@ -7,8 +7,11 @@ Create Date: ${create_date} """ from alembic import op import sqlalchemy as sa +import fastapi_users_db_sqlalchemy.generics +import fastapi_users_db_sqlalchemy ${imports if imports else ""} + # revision identifiers, used by Alembic. revision = ${repr(up_revision)} down_revision = ${repr(down_revision)} diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db_sa/migrations/versions/2021-08-16-16-55_2b7380507a71.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db_sa/migrations/versions/2021-08-16-16-55_2b7380507a71.py index f531a432..dc96cf71 100644 --- a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db_sa/migrations/versions/2021-08-16-16-55_2b7380507a71.py +++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db_sa/migrations/versions/2021-08-16-16-55_2b7380507a71.py @@ -20,7 +20,7 @@ def upgrade() -> None: op.create_table( "dummy_model", sa.Column("id", sa.Integer(), autoincrement=True, nullable=False), - sa.Column("name", sa.String(length=200), nullable=True), + sa.Column("name", sa.String(length=200), nullable=False), sa.PrimaryKeyConstraint("id"), ) # ### end Alembic commands ### diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db_sa/migrations/versions/2026-05-05-14-37_8caca4abd7b4.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db_sa/migrations/versions/2026-05-05-14-37_8caca4abd7b4.py new file mode 100644 index 00000000..86471a58 --- /dev/null +++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db_sa/migrations/versions/2026-05-05-14-37_8caca4abd7b4.py @@ -0,0 +1,52 @@ +"""Added users models. + +Revision ID: 8caca4abd7b4 +{%- if cookiecutter.add_dummy == 'True' %} +Revises: 2b7380507a71 +{%- else %} +Revises: 819cbf6e030b +{%- endif %} +Create Date: 2026-05-05 14:37:30.629187 + +""" + +import sqlalchemy as sa +from alembic import op +import fastapi_users_db_sqlalchemy.generics +import fastapi_users_db_sqlalchemy + +# revision identifiers, used by Alembic. +revision = "8caca4abd7b4" +down_revision = "2b7380507a71" +{%- if cookiecutter.add_dummy == 'True' %} +down_revision = "2b7380507a71" +{%- else %} +down_revision = "819cbf6e030b" +{%- endif %} +branch_labels = None +depends_on = None + + +def upgrade() -> None: + """Run the migration.""" + # ### commands auto generated by Alembic - please adjust! ### + op.create_table( + "user", + sa.Column("id", fastapi_users_db_sqlalchemy.generics.GUID(), nullable=False), + sa.Column("email", sa.String(length=320), nullable=False), + sa.Column("hashed_password", sa.String(length=1024), nullable=False), + sa.Column("is_active", sa.Boolean(), nullable=False), + sa.Column("is_superuser", sa.Boolean(), nullable=False), + sa.Column("is_verified", sa.Boolean(), nullable=False), + sa.PrimaryKeyConstraint("id"), + ) + op.create_index(op.f("ix_user_email"), "user", ["email"], unique=True) + # ### end Alembic commands ### + + +def downgrade() -> None: + """Undo the migration.""" + # ### commands auto generated by Alembic - please adjust! ### + op.drop_index(op.f("ix_user_email"), table_name="user") + op.drop_table("user") + # ### end Alembic commands ### diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db_sa/models/users.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db_sa/models/users.py index 783b5862..9a5c38be 100644 --- a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db_sa/models/users.py +++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db_sa/models/users.py @@ -1,4 +1,4 @@ -# type: ignore +from typing import AsyncGenerator import uuid from fastapi import Depends @@ -40,7 +40,7 @@ class UserManager(UUIDIDMixin, BaseUserManager[User, uuid.UUID]): verification_token_secret = settings.users_secret -async def get_user_db(session: AsyncSession = Depends(get_db_session)) -> SQLAlchemyUserDatabase: +async def get_user_db(session: AsyncSession = Depends(get_db_session)) -> AsyncGenerator[SQLAlchemyUserDatabase[User, uuid.UUID], None]: """ Yield a SQLAlchemyUserDatabase instance. @@ -50,7 +50,7 @@ async def get_user_db(session: AsyncSession = Depends(get_db_session)) -> SQLAlc yield SQLAlchemyUserDatabase(session, User) -async def get_user_manager(user_db: SQLAlchemyUserDatabase = Depends(get_user_db)) -> UserManager: +async def get_user_manager(user_db: SQLAlchemyUserDatabase[User, uuid.UUID] = Depends(get_user_db)) -> AsyncGenerator[UserManager, None]: """ Yield a UserManager instance. @@ -60,7 +60,7 @@ async def get_user_manager(user_db: SQLAlchemyUserDatabase = Depends(get_user_db yield UserManager(user_db) -def get_jwt_strategy() -> JWTStrategy: +def get_jwt_strategy() -> JWTStrategy[User, uuid.UUID]: """ Return a JWTStrategy in order to instantiate it dynamically.