Template
mirror of
https://github.com/Nekidev/uv-fastapi-tortoise.git
synced 2026-09-12 19:57:25 +00:00
Add versioning support by default, replace _Project.md with _Summary.md and _Description.md, upgrade to the latest Tortoise ORM version
This commit is contained in:
@@ -15,13 +15,15 @@ This project is divided into 3 sub-modules, `project.api`, `project.db`, and `pr
|
||||
### API
|
||||
|
||||
The `project.api` module contains all the `FastAPI` code of your project. Routers and
|
||||
schemas all go in here.
|
||||
schemas all go in here. The module is divided into `v*` sub-modules to version the API
|
||||
by default.
|
||||
|
||||
#### Handler Groups
|
||||
|
||||
The API module is divided in sub-groups. Each of them usually represents a single
|
||||
OpenAPI tag, and a group of related handlers. For example, to operate on a `Book`
|
||||
resource you may have a `project.api.books` module with its respective sub-modules.
|
||||
Each API version module is divided in sub-groups. Each of them usually represents a
|
||||
single OpenAPI tag, and a group of related handlers. For example, to operate on a
|
||||
`Book` resource you may have a `project.api.v1.books` module with its respective
|
||||
sub-modules.
|
||||
|
||||
Sub-modules inside those tag modules usually include:
|
||||
|
||||
@@ -33,7 +35,7 @@ For example, a minimal `router.py` file could look like this:
|
||||
```py
|
||||
from fastapi import APIRouter
|
||||
|
||||
from project.api.example.schemas import MessageSchema
|
||||
from project.api.v1.example.schemas import MessageSchema
|
||||
|
||||
|
||||
router = APIRouter(tags=["Example"])
|
||||
@@ -47,10 +49,10 @@ async def hello_world() -> MessageSchema:
|
||||
```
|
||||
|
||||
That example was taken from the example API module bundled by default with this
|
||||
template. Check it out at `project/api/example/router.py`.
|
||||
template. Check it out at `project/api/v1/example/router.py`.
|
||||
|
||||
The code snippet above references `project.api.example.schemas`, the `schemas.py` file
|
||||
mentioned above. The example file looks like this:
|
||||
The code snippet above references `project.api.v1.example.schemas`, the `schemas.py`
|
||||
file mentioned above. The example file looks like this:
|
||||
|
||||
```py
|
||||
from pydantic import BaseModel
|
||||
@@ -63,7 +65,7 @@ class MessageSchema(BaseModel):
|
||||
For more information about FastAPI return types and Pydantic models, check [FastAPI's
|
||||
tutorial on response types](https://fastapi.tiangolo.com/tutorial/response-model/).
|
||||
|
||||
To create new handler groups, create a new directory module under `project/api/` and
|
||||
To create new handler groups, create a new directory module under `project/api/v1` and
|
||||
name it to your group. Inside it, create an empty `__init__.py` file and a `router.py`
|
||||
file with the following base code:
|
||||
|
||||
@@ -74,7 +76,7 @@ from fastapi import APIRouter
|
||||
router = APIRouter()
|
||||
```
|
||||
|
||||
Next, go to `project/api/__init__.py` and import the router aliasing it to
|
||||
Next, go to `project/api/v1/__init__.py` and import the router aliasing it to
|
||||
`{module}_router`, e.g. `books_router`, `auth_router`, etc.
|
||||
|
||||
Last but not least, at the bottom of the file, add the following line:
|
||||
@@ -88,12 +90,12 @@ example.
|
||||
|
||||
#### API-Wide Schemas
|
||||
|
||||
Sometimes, you have API-wide schemas. That may be the response base schema, pagination
|
||||
schemas, error schemas, or any other schemas the whole API uses. In those cases, you
|
||||
can use the `project/api/schemas.py` module and include them there.
|
||||
Sometimes, you have version-wide schemas. That may be the response base schema,
|
||||
pagination schemas, error schemas, or any other schemas the whole API uses. In those
|
||||
cases, you can use the `project/api/v*/schemas.py` module and include them there.
|
||||
|
||||
For example, given that you want a base response schema where data is always in a
|
||||
`data` property in an object (JSON), your `project/api/schemas.py` would look like
|
||||
`data` property in an object (JSON), your `project/api/v1/schemas.py` would look like
|
||||
this:
|
||||
|
||||
```py
|
||||
@@ -104,7 +106,7 @@ class ResponseSchema[T](BaseModel):
|
||||
data: T
|
||||
```
|
||||
|
||||
The included `project/api/schemas.py` file includes a `NanoID` type by default. It
|
||||
The included `project/api/v1/schemas.py` file includes a `NanoID` type by default. It
|
||||
represents NanoID fields, as the name conveys, and it's useful when using nanoids for
|
||||
your models instead of numeric IDs, UUIDs, or any other ID type. You can use it like
|
||||
follows:
|
||||
@@ -112,7 +114,7 @@ follows:
|
||||
```py
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from project.api.schemas import NanoID
|
||||
from project.api.v1.schemas import NanoID
|
||||
|
||||
|
||||
class ExampleSchema(BaseModel):
|
||||
@@ -125,7 +127,7 @@ It can also be used as path parameters, query parameters, and anywhere that take
|
||||
pydantic model in FastAPI. For example:
|
||||
|
||||
```py
|
||||
from project.api.schemas import NanoID
|
||||
from project.api.v1.schemas import NanoID
|
||||
|
||||
|
||||
@router.get("/example/{example_id}")
|
||||
@@ -181,9 +183,9 @@ Your handler code would then look like:
|
||||
|
||||
```py
|
||||
from project.db import Book
|
||||
from project.api.errors import NotFoundError
|
||||
from project.api.schemas import ErrorSchema, NanoID
|
||||
from project.api.books.schemas import BookSchema
|
||||
from project.api.v1.errors import NotFoundError
|
||||
from project.api.v1.schemas import ErrorSchema, NanoID
|
||||
from project.api.v1.books.schemas import BookSchema
|
||||
|
||||
|
||||
@router.get("/books/{book_id}", responses={
|
||||
@@ -212,8 +214,8 @@ The `project/api/errors.py` file contains a few pre-defined error types you can
|
||||
right away from your API handlers to return errors. For example:
|
||||
|
||||
```py
|
||||
from project.api.errors import NotFoundError
|
||||
from project.api.schemas import ErrorSchema
|
||||
from project.api.v1.errors import NotFoundError
|
||||
from project.api.v1.schemas import ErrorSchema
|
||||
|
||||
|
||||
@router.get("/not-found", status_code=404)
|
||||
@@ -248,8 +250,8 @@ class ImATeapotError(ErrorInitMixin, BaseError):
|
||||
Your handler will then look something like:
|
||||
|
||||
```py
|
||||
from project.api.errors import ImATeapotError
|
||||
from project.api.schemas import ErrorSchema
|
||||
from project.api.v1.errors import ImATeapotError
|
||||
from project.api.v1.schemas import ErrorSchema
|
||||
|
||||
|
||||
@router.get("/coffee", status_code=418)
|
||||
@@ -292,6 +294,7 @@ the naming. The modules you'll most commonly edit are the following:
|
||||
- `project.db.models`: Contains concern-specific submodules with database model
|
||||
definitions. For example, `users.py` for `User` models, `books.py` for `Book` and
|
||||
`Author` models (e.g. in a books-related application).
|
||||
- `project.db.migrations`: Contains migrations, autogenerated by the `tortoise` CLI.
|
||||
- `project.db.fields`: Custom DB fields and field aliases. It contains a
|
||||
`NanoIDField` function which aliases to a nanoid `CharField`. Add any custom DB
|
||||
fields here.
|
||||
@@ -337,9 +340,9 @@ CONFIG = {
|
||||
"models": {
|
||||
"models": [
|
||||
"project.db.models.example",
|
||||
"aerich.models", # Keep this one for migrations.
|
||||
],
|
||||
"default_connection": "default",
|
||||
"migrations": "project.db.migrations",
|
||||
}
|
||||
},
|
||||
"use_tz": True,
|
||||
@@ -362,35 +365,13 @@ To add a new model, just add it to the imports list.
|
||||
|
||||
#### Migrations
|
||||
|
||||
This template uses [aerich](https://github.com/tortoise/aerich) to handle migrations.
|
||||
Migrations are automatically-generated and stored under `project/db/migrations/models`.
|
||||
This template uses the Tortoise ORM CLI to handle migrations. Migrations are
|
||||
automatically-generated and stored under `project/db/migrations`.
|
||||
|
||||
Migrations are database-specific, meaning that your SQLite migrations won't work on
|
||||
PostgreSQL, and neither will any other DB combination that doesn't mirror the SQL
|
||||
language implementation perfectly.
|
||||
To create a new migration after you make an update, use `tortoise migrate`.
|
||||
|
||||
The migrations folder is initialized automatically when you start the server if the
|
||||
`project/db/migrations` folder is missing. If you wish to initalize the directory
|
||||
manaully without running the server, run the following in your terminal:
|
||||
|
||||
```sh
|
||||
$ uv run aerich init-migrations
|
||||
```
|
||||
|
||||
That'll automatically create the migrations folder in the proper location and a first
|
||||
migration file.
|
||||
|
||||
To create a new migration after you make an update, use `aerich migrate`.
|
||||
|
||||
The server automatically creates the `project/db/migrations/` folder if missing and
|
||||
applies any pending migrations on startup.
|
||||
|
||||
Since migrations are database-specific, you'll need to delete the
|
||||
`project/db/migrations/` folder completely when switching database management systems.
|
||||
Note that migrations keep a record of the migrations applied, so deleting the folder
|
||||
means you won't be able to keep making changes on a database following the now-deleted
|
||||
migrations unless you kept a backup of them somewhere and move those back to the
|
||||
`project/db/migrations/` folder back.
|
||||
The server automatically applies any pending migrations on startup. If you wish to
|
||||
apply such new migrations manually, run `tortoise upgrade`.
|
||||
|
||||
### Lib and Business Logic
|
||||
|
||||
@@ -409,6 +390,10 @@ other constants go there.
|
||||
|
||||
This template comes with support for `.env` files by default.
|
||||
|
||||
It's a good practice to prefix your project's environment variables with `PROJECT_` or
|
||||
your project's name. It prevents conflicts when running multiple services in the same
|
||||
environment.
|
||||
|
||||
## Getting Started
|
||||
|
||||
To start with, delete the example models and API router (you can keep it if you want a
|
||||
@@ -416,12 +401,12 @@ base to work on).
|
||||
|
||||
To do that:
|
||||
|
||||
1. Delete the `project/api/example` directory.
|
||||
2. Delete the `docs/Example.md` file.
|
||||
3. Open `project/api/__init__.py` and remove the inclusion of the example handler.
|
||||
1. Delete the `project/api/v1/example` directory.
|
||||
2. Delete the `docs/v1/Example.md` file.
|
||||
3. Open `project/api/v1/__init__.py` and remove the inclusion of the example handler.
|
||||
4. Delete the `project/db/models/example.py` file.
|
||||
5. Open `project/db/setup.py` and remove `project.db.models.example` from the list of
|
||||
models.
|
||||
5. Open `project/db/settings.py` and remove `project.db.models.example` from the list
|
||||
of models.
|
||||
6. Open `project/db/__init__.py` and remove the re-export of the `Book` example model.
|
||||
|
||||
### Template Defaults Cleanup
|
||||
@@ -437,7 +422,7 @@ To do that, do the following:
|
||||
with your project's name.
|
||||
3. Rename the `project/` directory to your new import name.
|
||||
4. Rename your project in `pyproject.toml`.
|
||||
5. Update the `[tool.aerich]` section in your `pyproject.toml` file to point to the
|
||||
5. Update the `[tool.tortoise]` section in your `pyproject.toml` file to point to the
|
||||
new directory and root module name.
|
||||
|
||||
The following steps mention deletion, but you can always just update those files
|
||||
@@ -446,9 +431,9 @@ instead if you want to keep them as a base for starting. It also still mentions
|
||||
your now-renamed source code folder.
|
||||
|
||||
6. Empty the `docs/` directory.
|
||||
7. Delete the `project/api/example/` directory.
|
||||
8. Remove `project.api.example` imports and router inclusion from
|
||||
`project/api/__init__.py`.
|
||||
7. Delete the `project/api/v1/example/` directory.
|
||||
8. Remove `project.api.v1.example` imports and router inclusion from
|
||||
`project/api/v1/__init__.py`.
|
||||
9. Delete the `project/db/models/example.py` file.
|
||||
10. Remove the `project.db.models.example.*` re-exports from `project/db/__init__.py`.
|
||||
11. Remove `"project.db.models.example"` from the `DATABASE` object in
|
||||
@@ -460,7 +445,7 @@ your now-renamed source code folder.
|
||||
To start the server, run:
|
||||
|
||||
```sh
|
||||
$ uv run fastapi run project
|
||||
$ uv run uvicorn project:app --reload
|
||||
```
|
||||
|
||||
`project` is your import name.
|
||||
|
||||
Reference in New Issue
Block a user