This repository has been archived by the owner on Sep 12, 2023. It is now read-only.
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
docs: restructures and adds to documentation
- Loading branch information
1 parent
bec1a52
commit 2f8875c
Showing
6 changed files
with
173 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
# Configuring the application | ||
|
||
Configuration is via environment. Here's an example `.env`: | ||
|
||
```dotenv title="Example .env" | ||
--8<-- ".env.example" | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
# DTOs | ||
|
||
- pydantic models generated from SQLAlchemy declarative models. | ||
- DTOs have a purpose, read or write. | ||
- Model attributes can have a mode, read-only or private. | ||
|
||
## What are DTOs? | ||
|
||
DTO stands for "Data Transfer Object". They are the filter through which data is accepted into, and | ||
output from the application. | ||
|
||
## DTO Factory | ||
|
||
`starlite-saqlalchemy` includes | ||
[`dto.factory()`](../reference/starlite_saqlalchemy/dto/#starlite_saqlalchemy.dto.factory) | ||
which automatically creates [pydantic](https://pydantic-docs.helpmanual.io/) models from | ||
[SQLAlchemy 2.0 ORM](https://docs.sqlalchemy.org/en/20/orm/) models. | ||
|
||
### Creating a SQLAlchemy ORM model | ||
|
||
If you are new to SQLAlchemy, I cannot recommend their docs enough. Start at | ||
[the beginning](https://docs.sqlalchemy.org/en/20/orm/quickstart.html), and follow along until you | ||
are comfortable. I won't even try to compete with the quality and depth of information that can be | ||
found there - a credit to everyone who has contributed to that project over the years. | ||
|
||
### Configuring generated DTOs | ||
|
||
#### DTO Purpose | ||
|
||
The `dto.Purpose` enum tells the factory if the purpose of the DTO is parse data submitted by the | ||
client for updating or "writing" to a resource, or if it is to serialize data to be transmitted back | ||
to, or "read" by the client. | ||
|
||
For example, the DTO objects generated by the factory may differ due to the intended purpose of the | ||
DTO: | ||
|
||
```python | ||
from starlite_saqlalchemy import dto | ||
|
||
from domain.users import User | ||
|
||
|
||
ReadDTO = dto.factory("UserReadDTO", model=User, purpose=dto.Purpose.READ) | ||
WriteDTO = dto.factory("UserWriteDTO", model=User, purpose=dto.Purpose.WRITE) | ||
``` | ||
|
||
#### DTO Mode | ||
|
||
We use the `info` parameter to `mapped_column()` to guide `dto.factory()`. | ||
|
||
The [dto.Mode](../reference/starlite_saqlalchemy/dto/#Mode) enumeration is used to indicate on the | ||
SQLAlchemy ORM model, whether properties should always be private, or read-only. | ||
|
||
Take this model, for example: | ||
|
||
```python | ||
from datetime import datetime | ||
|
||
from sqlalchemy.orm import mapped_column | ||
from starlite_saqlalchemy import dto, orm | ||
|
||
|
||
class User(orm.Base): | ||
name: str | ||
password_hash: str = mapped_column(info={"dto": dto.Mode.PRIVATE}) | ||
updated_at: datetime = mapped_column(info={"dto": dto.Mode.READ_ONLY}) | ||
|
||
|
||
ReadDTO = dto.factory("UserReadDTO", model=User, purpose=dto.Purpose.READ) | ||
WriteDTO = dto.factory("UserWriteDTO", model=User, purpose=dto.Purpose.WRITE) | ||
``` | ||
|
||
Both `ReadDTO` and `WriteDTO` are pydantic models that have a `name` attribute. | ||
|
||
Neither `ReadDTO` or `WriteDTO` have a `password_hash` attribute - this is the side effect of | ||
marking the column with `dto.Mode.PRIVATE`. Columns that are marked private will never be included | ||
in any generated DTO model, meaning that in the context of the application, they are unable to be | ||
read or modified by the client. | ||
|
||
`ReadDTO` has an `updated_at` field, while `WriteDTO` does not. This is the side effect of marking | ||
the column with `dto.Mode.READ_ONLY` - these fields will only be included in DTOs generated for | ||
`dto.Purpose.READ` and make sense for fields that have internally generated values. | ||
|
||
The following class is pretty much the same as | ||
[`orm.Base`](../reference/starlite_saqlalchemy/orm/#starlite_saqlalchemy.orm.Base) - the bundled | ||
SQLAlchemy base class that comes with `starlite-saqlalchemy`. | ||
|
||
```python | ||
from datetime import datetime | ||
from uuid import UUID, uuid4 | ||
|
||
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column | ||
|
||
from starlite_saqlalchemy import dto | ||
|
||
|
||
class Base(DeclarativeBase): | ||
id: Mapped[UUID] = mapped_column( | ||
default=uuid4, primary_key=True, info={"dto": dto.Mode.READ_ONLY} | ||
) | ||
"""Primary key column.""" | ||
created: Mapped[datetime] = mapped_column( | ||
default=datetime.now, info={"dto": dto.Mode.READ_ONLY} | ||
) | ||
"""Date/time of instance creation.""" | ||
updated: Mapped[datetime] = mapped_column( | ||
default=datetime.now, info={"dto": dto.Mode.READ_ONLY} | ||
) | ||
``` | ||
|
||
Notice that all these fields are marked as `dto.Mode.READ_ONLY`. This means that they are unable to | ||
be modified by clients, even if they include values for them in the payloads to `POST`/`PUT`/`PATCH` | ||
routes. | ||
|
||
You can inherit from `orm.Base` to create your SQLAlchemy models, but you don't have to. You can | ||
choose to subclass `orm.Base` or roll your own base class altogether. `dto.factory()` will still | ||
work as advertised. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters