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:
2026-07-15 23:43:31 -03:00
parent f8f6dad237
commit 6d1f73772e
22 changed files with 242 additions and 388 deletions
+47 -62
View File
@@ -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.