Read only fields¶
Sometimes, fields should never be able to be specified by the client. For example, upon creation of a new resource
instance, the id field of a model should be generated by the server and not specified by the client.
1from __future__ import annotations
2
3from dataclasses import dataclass
4
5from litestar import Litestar, post
6from litestar.dto import DataclassDTO, DTOConfig
7
8
9@dataclass
10class Person:
11 name: str
12 age: int
13 email: str
14 id: int
15
16
17class ReadDTO(DataclassDTO[Person]):
18 config = DTOConfig(exclude={"email"})
19
20
21class WriteDTO(DataclassDTO[Person]):
22 config = DTOConfig(exclude={"id"})
23
24
25@post("/person", dto=WriteDTO, return_dto=ReadDTO, sync_to_thread=False)
26def create_person(data: Person) -> Person:
27 # Logic for persisting the person goes here
28 return data
29
30
31app = Litestar(route_handlers=[create_person])
In this adjustment, we add an id field to the Person model. We also create a new class, WriteDTO that is
instructed to ignore the id attribute.
The WriteDTO class is assigned to the handler via the dto kwarg (dto=WriteDTO) meaning that the id field
will be ignored from any data received from the client when creating a new Person instance.
When we try to create a new Person instance with an id field specified, we get an error:
What’s happening? The DTO is trying to construct an instance of the Person model but we have excluded the id
field from the accepted client data. The id field is required by the Person model, so the model constructor
raises an error.
There is more than one way to address this issue, for example we could assign a default value to the id field and
overwrite the default in the handler, or we could create an entirely separate model that has no id field, and
transfer the data from that to the Person model in the handler. However, Litestar has a built-in solution for this:
DTOData.