├── tests ├── __init__.py ├── data │ ├── __init__.py │ ├── models.py │ └── people.py ├── endpoints │ ├── __init__.py │ ├── test_logout.py │ ├── test_confirm.py │ ├── test_login.py │ └── test_register.py ├── models │ ├── __init__.py │ ├── test_mixins.py │ ├── test_events.py │ ├── test_types.py │ └── test_models.py ├── conftest.py ├── test_db_registry.py ├── test_utils.py ├── test_middleware.py ├── test_tz.py ├── test_types.py ├── test_crud.py └── test_auth.py ├── fastapi_sqlalchemy ├── __init__.py ├── endpoints │ ├── templates │ │ ├── confirmation_email.txt │ │ ├── confirmation_email.html │ │ ├── failed_email_confirmation.html │ │ ├── send_confirmation.html │ │ ├── login.html │ │ └── register.html │ ├── __init__.py │ ├── logout.py │ ├── confirm.py │ ├── login.py │ └── register.py ├── models │ ├── __init__.py │ ├── groups.py │ ├── permissions.py │ ├── base.py │ ├── users.py │ ├── mixins.py │ ├── types.py │ ├── associations.py │ └── events.py ├── types.py ├── db_registry.py ├── tz.py ├── utils.py ├── middleware.py ├── auth.py └── crud.py ├── pytest.ini ├── assets ├── logo-128x128.png └── logo.svg ├── .gitignore ├── .flake8 ├── Makefile ├── setup.py ├── README.md └── .pylintrc /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/data/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/endpoints/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/models/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /fastapi_sqlalchemy/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | env = 3 | D:DATABASE_URL=sqlite:///sqlite.db?check_same_thread=False 4 | -------------------------------------------------------------------------------- /assets/logo-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zuarbase/fastapi-sqlalchemy/HEAD/assets/logo-128x128.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.db 2 | *.egg-info 3 | *.pyc 4 | __pycache__ 5 | /build 6 | /dist 7 | /pyenv 8 | /venv 9 | /.idea 10 | /.cache 11 | /.coverage 12 | -------------------------------------------------------------------------------- /fastapi_sqlalchemy/endpoints/templates/confirmation_email.txt: -------------------------------------------------------------------------------- 1 | Welcome! Thanks for signing up. Please open the link to activate your account: 2 | 3 | ${confirm_url} 4 | 5 | Thank You! -------------------------------------------------------------------------------- /fastapi_sqlalchemy/endpoints/__init__.py: -------------------------------------------------------------------------------- 1 | """ All built-in endpoints """ 2 | from .login import LoginEndpoint 3 | from .logout import LogoutEndpoint 4 | from .register import RegisterEndpoint 5 | from .confirm import ConfirmEndpoint 6 | -------------------------------------------------------------------------------- /fastapi_sqlalchemy/endpoints/templates/confirmation_email.html: -------------------------------------------------------------------------------- 1 |
Welcome! Thanks for signing up. Please follow this link to activate your account:
2 | 3 |Thank you!
-------------------------------------------------------------------------------- /tests/data/models.py: -------------------------------------------------------------------------------- 1 | from fastapi_sqlalchemy import models 2 | 3 | 4 | class User(models.User): 5 | pass 6 | 7 | 8 | class Group(models.Group): 9 | pass 10 | 11 | 12 | class Permission(models.Permission): 13 | pass 14 | -------------------------------------------------------------------------------- /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 80 3 | inline-quotes = " 4 | exclude = .git,node_modules,__init__.py,DEBIAN 5 | ignore = 6 | # too many leading '#' for block comment 7 | E266, 8 | # comparison to None should be 'if cond is not None:' 9 | E711 10 | -------------------------------------------------------------------------------- /tests/endpoints/test_logout.py: -------------------------------------------------------------------------------- 1 | from fastapi_sqlalchemy import endpoints 2 | 3 | 4 | def test_logout_post(engine, session, app, client): 5 | endpoint = endpoints.LogoutEndpoint() 6 | 7 | @app.post("/logout") 8 | async def _post(): 9 | return await endpoint.on_post() 10 | 11 | res = client.post("/logout") 12 | assert res.status_code == 303 13 | assert res.headers.get("location") == "/login" 14 | -------------------------------------------------------------------------------- /fastapi_sqlalchemy/models/__init__.py: -------------------------------------------------------------------------------- 1 | """ The SQLAlchemy model """ 2 | from .base import BASE, Session 3 | 4 | from .types import GUID, JSONEncodedDict, JSON_TYPE 5 | from .mixins import GuidMixin, TimestampMixin, DictMixin 6 | 7 | from .users import User 8 | from .groups import Group 9 | from .permissions import Permission 10 | 11 | from .associations import ( 12 | create_group_membership_table, 13 | create_user_permissions_table, 14 | create_group_permissions_table 15 | ) 16 | 17 | from . import events 18 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: flake8 pylint test 2 | 3 | flake8: flake8_pkg flake8_tests 4 | .PHONY: flake8 5 | 6 | flake8_pkg: 7 | flake8 fastapi_sqlalchemy 8 | .PHONY: flake8_pkg 9 | 10 | flake8_tests: 11 | flake8 tests 12 | .PHONY: flake8_tests 13 | 14 | pylint: pylint_pkg pylint_tests 15 | .PHONY: pylint 16 | 17 | pylint_pkg: 18 | pylint fastapi_sqlalchemy 19 | .PHONY: pylint_pkg 20 | 21 | pylint_tests: 22 | pylint tests --disable=missing-docstring,unused-argument,too-many-ancestors,unexpected-keyword-arg 23 | .PHONY: pylint_tests 24 | 25 | test: 26 | pytest -xvv tests 27 | .PHONY: test 28 | 29 | coverage: 30 | pytest --cov=fastapi_sqlalchemy --cov-report=term-missing --cov-fail-under=100 tests/ 31 | .PHONY: coverage 32 | 33 | pyenv: 34 | virtualenv -p python3 pyenv 35 | pyenv/bin/pip install -e .[dev,prod] 36 | .PHONY: pyenv 37 | -------------------------------------------------------------------------------- /fastapi_sqlalchemy/models/groups.py: -------------------------------------------------------------------------------- 1 | """ Group model """ 2 | import sqlalchemy 3 | from sqlalchemy import event 4 | 5 | from .base import BASE, Session, MODEL_MAPPING 6 | from . import mixins 7 | 8 | 9 | class Group(BASE, mixins.GuidMixin, mixins.TimestampMixin): 10 | """ The groups table """ 11 | __tablename__ = "groups" 12 | __abstract__ = True 13 | 14 | name = sqlalchemy.Column( 15 | sqlalchemy.String(255), 16 | nullable=False, 17 | ) 18 | 19 | @classmethod 20 | def get_by_name( 21 | cls, 22 | session: Session, 23 | name: str 24 | ): 25 | """ Lookup a group by name 26 | """ 27 | return session.query(cls).filter(cls.name == name).first() 28 | 29 | 30 | @event.listens_for(Group, "mapper_configured", propagate=True) 31 | def _mapper_configured(_mapper, cls): 32 | MODEL_MAPPING["Group"] = cls 33 | -------------------------------------------------------------------------------- /fastapi_sqlalchemy/models/permissions.py: -------------------------------------------------------------------------------- 1 | """ Permission model """ 2 | import sqlalchemy 3 | from sqlalchemy import event 4 | 5 | from .base import BASE, Session, MODEL_MAPPING 6 | from . import mixins 7 | 8 | 9 | class Permission(BASE, mixins.GuidMixin, mixins.TimestampMixin): 10 | """ The permissions table """ 11 | __tablename__ = "permissions" 12 | __abstract__ = True 13 | 14 | name = sqlalchemy.Column( 15 | sqlalchemy.String(255), 16 | nullable=False, 17 | ) 18 | 19 | @classmethod 20 | def get_by_name( 21 | cls, 22 | session: Session, 23 | name: str 24 | ): 25 | """ Lookup a group by name 26 | """ 27 | return session.query(cls).filter(cls.name == name).first() 28 | 29 | 30 | @event.listens_for(Permission, "mapper_configured", propagate=True) 31 | def _mapper_configured(_mapper, cls): 32 | MODEL_MAPPING["Permission"] = cls 33 | -------------------------------------------------------------------------------- /fastapi_sqlalchemy/endpoints/templates/failed_email_confirmation.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |Please fill out the registration form again.
16 |Please check your spam folder if you don't receive an email in the next several minutes.
16 |