Skip to content
This repository has been archived by the owner on Sep 28, 2023. It is now read-only.

updated fastapi to 0.65.2 (bug fix), also updated more used libraries… #7

Merged
merged 2 commits into from
Jun 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
{
"python.formatting.provider": "black",
"python.analysis.extraPaths": ["./fastapi_plan/template/{{cookiecutter.project_name}}"]
"python.analysis.extraPaths": [
"./fastapi_plan/template/{{cookiecutter.project_name}}"
],
"python.pythonPath": ".venv\\Scripts\\python.exe"
}
21 changes: 9 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
![PyPI - Python Version](https://img.shields.io/pypi/pyversions/fastapi-plan)
![tests](https://github.com/rafsaf/fastapi-plan/actions/workflows/tests.yml/badge.svg)

dead simple but powerful template manager for FastAPI applications.
Dead simple but powerful template manager for FastAPI applications.

- [About](#about)
- [Quickstart](#quickstart)
Expand All @@ -17,7 +17,8 @@ dead simple but powerful template manager for FastAPI applications.

## About

features:
Features:

- postgresql database with [Tortoise ORM](https://tortoise-orm.readthedocs.io/en/latest/index.html) as ORM
- well organised, rock solid project structure (see section [Project structure](#project-structure))
- ready-to-use user model, authentiaction system (JWT), hashing with Bcrypt
Expand All @@ -29,12 +30,12 @@ features:
- poetry or pip
- deployment ready docker-compose.prod.yml file with poetry, you will only need own domain

furthermore:
Furthermore:

- full [project structure schema](#project-structure)
- [high level overview](#high-level-overview) how this project is organised and why, questions like where do the settings live or what every variable in `.env` file
- [high level overview](#high-level-overview) how this project is organised and why, questions like where do the settings live or what every variable in `.env` file is used for
- step by step explanation [how to add new endpoint](#how-to-add-new-endpoint), from creating new model, adding schemas and routes to migrating database and writting tests (it's always better to have it and optionally adopt it, than wasting time trying to figure out the best dev path)


## Quickstart

NOTE: you will need [docker](https://www.docker.com/get-started) and optional but recommended [poetry](https://python-poetry.org/docs/) installed!
Expand Down Expand Up @@ -66,7 +67,6 @@ pip install -r requirements.txt

### 1. DEVELOPMENT


since we wanna use uvicorn in development, create only postgres container using docker-compose.yml file like that:

```bash
Expand Down Expand Up @@ -100,7 +100,6 @@ The diffrence between development approach is that web server automatically runs
2. `FIRST_SUPER_USER_EMAIL` - first account email
3. `DEBUG` - when it's false, the `POSTGRES_SERVER` is set to `localhost` for development, so change it to `DEBUG=true` to use `db` postgres server.


### 3. PRODUCTION (https, own domain)

To make it available from https://your_domain.com on VM run
Expand Down Expand Up @@ -164,15 +163,15 @@ Plesae also note that to get no-test certificate, you should comment line `"--ce

## High level overview

This project strucutre is mostly based on the [official template](#https://github.com/tiangolo/full-stack-fastapi-postgresql) (but not only) which is really great but unfortunatly does not support Tortoise ORM and is... (too?) complicated. All the security or problematic stuff (`app/core/security.py` with `verify_password` function, login and token routes, JWT token schemas) are just copied from there, so you can be preety sure it will work as expected.
This project strucutre is mostly based on the [official template](#https://github.com/tiangolo/full-stack-fastapi-postgresql) (but not only) which is really great but unfortunatly does not support Tortoise ORM and is... (too?) complicated. All the security or problematic stuff (`app/core/security.py` with `verify_password` function, login and token routes, JWT token schemas) are just copied from there, so you can be pretty sure it will work as expected.

The main thougts are:

- There two sorts of settings, first one located in `.env` file for the ENTIRE project, and python-specific settings which lives in `app/core/config.py`, the file is based on pydantic solution (using dotenv lib). Why? Well, that's simple, this is due to [12factor methodology](https://12factor.net/), python-specific settings inherit from `.env` file, so this is the only place where you actually change something. If you have any problems understanding mentioned `config.py` file, just refer to [pydantic - settings management](https://pydantic-docs.helpmanual.io/usage/settings/), it's preety clear.
- There two sorts of settings, first one located in `.env` file for the ENTIRE project, and python-specific settings which lives in `app/core/config.py`, the file is based on pydantic solution (using dotenv lib). Why? Well, that's simple, this is due to [12factor methodology](https://12factor.net/), python-specific settings inherit from `.env` file, so this is the only place where you actually change something. If you have any problems understanding mentioned `config.py` file, just refer to [pydantic - settings management](https://pydantic-docs.helpmanual.io/usage/settings/), it's pretty clear.

- Models, crud, schemas, api routes, tests... it might be confusing how to actually ADD SOMETHING NEW here, but after following next section (learn by doing, step by step), it should be pretty easy

- Database-related stuff is very convinient, taken mostly from [Tortoise ORM](https://tortoise-orm.readthedocs.io/en/latest/index.html) docs and just *working*. There is `register_tortoise` function in `main.py`, `TORTOISE_ORM` variable in `app/core/config.py`. Please, be aware that if you don't run `initial_data.py` SOMEHOW (in development- you have to do it yourself, in debug/production it is handled by shell script `initial.sh`, which also runs tests and migrations), you won't be able to connect to database. `initial_data.py` is hearbly based on the same named file in **official template** mentioned earlier. It has two responsibilities, first is running `init` function from Tortoise to initialize connection, and the second - creating first superuser (defined in `.env`) if one doesn't yet exists.
- Database-related stuff is very convinient, taken mostly from [Tortoise ORM](https://tortoise-orm.readthedocs.io/en/latest/index.html) docs and just _working_. There is `register_tortoise` function in `main.py`, `TORTOISE_ORM` variable in `app/core/config.py`. Please, be aware that if you don't run `initial_data.py` SOMEHOW (in development- you have to do it yourself, in debug/production it is handled by shell script `initial.sh`, which also runs tests and migrations), you won't be able to connect to database. `initial_data.py` is hearbly based on the same named file in **official template** mentioned earlier. It has two responsibilities, first is running `init` function from Tortoise to initialize connection, and the second - creating first superuser (defined in `.env`) if one doesn't yet exists.

- Migrations are also provided by Tortiose (the tool is aerich), docs can be found [here in aerich repo](https://github.com/tortoise/aerich). The default migration (default user model) file is already included. After changes in models (e.g. new model `Cars`), just run `aerich migrate`, `aerich upgrade` and you are good to go.

Expand Down Expand Up @@ -287,7 +286,6 @@ dog = CRUDDog(Dog)
from .crud_dog import dog # type: ignore
```


8. Create `dogs.py` with endpoints in `app/api/routers` folder

```python
Expand Down Expand Up @@ -494,4 +492,3 @@ def test_remove_all_user_dogs(event_loop: EventLoop, normal_user: models.User):
```

13. And then `test_dogs.py` for endpoints in `app/tests/api` folder

Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

from app.api.routers import login, users


api_router = APIRouter()
api_router.include_router(login.router, prefix="/login", tags=["login"])
api_router.include_router(users.router, prefix="/users", tags=["users"])
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
from app.core import security
from app.core.config import settings


reusable_oauth2 = OAuth2PasswordBearer(
tokenUrl=f"{settings.API_STR}/login/access-token"
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
from app.core import security
from app.core.config import settings


router = APIRouter()


Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from fastapi import APIRouter, Depends, HTTPException, status

from app import crud, models, schemas
from app.api import deps

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from typing import Dict, List, Optional, Union
from pydantic import AnyHttpUrl, BaseSettings, AnyUrl, validator, EmailStr
from pathlib import Path
from typing import Dict, List, Optional, Union

from pydantic import AnyHttpUrl, AnyUrl, BaseSettings, EmailStr, validator

PROJECT_DIR = Path(__file__).parent.parent.parent

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from typing import Any, Dict, Generic, Optional, Type, TypeVar, Union
from tortoise.models import Model

from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel
from tortoise.queryset import QuerySet
from tortoise.exceptions import DoesNotExist
from tortoise.models import Model
from tortoise.queryset import QuerySet

ModelType = TypeVar("ModelType", bound=Model)
CreateSchemaType = TypeVar("CreateSchemaType", bound=BaseModel)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from typing import Optional

from tortoise.exceptions import DoesNotExist

from app import models, schemas
from app.core.security import get_password_hash, verify_password
from app.crud.base import CRUDBase
from app import models, schemas
from tortoise.exceptions import DoesNotExist


class CRUDUser(CRUDBase[models.User, schemas.UserCreateMe, schemas.UserUpdateMe]):
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
import logging
from tortoise import run_async
from tenacity import retry, after_log, before_log, retry, wait_fixed, stop_after_attempt
from tortoise import Tortoise

from tenacity import after_log, before_log, retry, stop_after_attempt, wait_fixed
from tortoise import Tortoise, run_async

try:
import app
except ModuleNotFoundError:
import sys
import os
import pathlib
import sys

app = pathlib.Path(os.path.dirname(__file__)).parent
sys.path.append(str(app))
from app import crud, schemas
from app.core.config import settings
from app.core.config import TORTOISE_ORM
from app.core.config import TORTOISE_ORM, settings

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,4 @@ def create_app() -> FastAPI:
db_url=settings.TORTOISE_DATABASE_URI,
modules={"models": ["app.models"]},
add_exception_handlers=True,
)
)
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from typing import Optional

import tortoise.fields.data as fields
from tortoise.models import Model

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from .token import Token, TokenPayload # noqa
from .user import (
UserCreateBySuperuser,
UserCreateMe,
UserPydantic,
UserPydanticList,
UserCreateMe,
UserCreateBySuperuser,
UserUpdateBySuperuser,
UserUpdateMe,
)
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from typing import Optional

from pydantic import BaseModel, EmailStr
from tortoise.contrib.pydantic.creator import (
pydantic_model_creator,
pydantic_queryset_creator,
)
from pydantic import BaseModel, EmailStr

from app.models import User

UserPydantic = pydantic_model_creator(User)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
from asyncio import AbstractEventLoop as EventLoop
from typing import Dict

from fastapi.testclient import TestClient
from app.tests.conftest import default_user, default_superuser
from asyncio import AbstractEventLoop as EventLoop

from app.core.config import settings
from app.tests.conftest import default_superuser, default_user


def test_get_access_token(
Expand All @@ -25,4 +27,4 @@ def test_use_access_token(
)
result = r.json()
assert r.status_code == 200
assert "email" in result
assert "email" in result
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
from app import crud
from typing import Dict
from app.tests.conftest import default_user, default_superuser
from app.core.config import settings
from asyncio import AbstractEventLoop as EventLoop
from typing import Dict

from fastapi.testclient import TestClient

from app import crud
from app.core.config import settings
from app.tests.conftest import default_superuser, default_user
from app.tests.utils.user import get_random_user_by_superuser, get_random_user_me
from app.tests.utils.utils import random_lower_string
from app.tests.utils.user import get_random_user_me, get_random_user_by_superuser


def test_read_users_superuser(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import pytest
from asyncio import AbstractEventLoop as EventLoop
from typing import Generator

import pytest
from fastapi.testclient import TestClient
from tortoise.contrib.test import finalizer, initializer

from app import crud, models, schemas
from app.main import create_app
from app import models, schemas, crud
from app.tests.utils.utils import user_authentication_headers

app = create_app()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import pytest
from app.schemas.user import UserUpdateBySuperuser, UserUpdateMe
import logging
from asyncio import AbstractEventLoop as EventLoop

import pytest
from fastapi.testclient import TestClient
from app.models import User

from app import crud
from app.schemas import UserCreateMe, UserCreateBySuperuser
from app.tests.utils.utils import random_email, random_lower_string
from app.core.security import verify_password
import logging

from app.models import User
from app.schemas import UserCreateBySuperuser, UserCreateMe
from app.schemas.user import UserUpdateBySuperuser, UserUpdateMe
from app.tests.utils.utils import random_email, random_lower_string

logger = logging.getLogger(__name__)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from app.schemas import UserCreateMe, UserCreateBySuperuser
from app.schemas import UserCreateBySuperuser, UserCreateMe
from app.tests.utils.utils import random_email, random_lower_string


Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
from app.core.config import settings
import random
import string
from asyncio import AbstractEventLoop as EventLoop
from typing import Dict
from app import crud

from fastapi.testclient import TestClient

from app import crud
from app.core.config import settings
from app.schemas import UserCreateBySuperuser


Expand Down
Loading