Excluding from collections of nested models#

In Python, generic types can accept one or more type parameters (types that are enclosed in square brackets). This pattern is often seen when representing a collection of some type, such as List[Person], where List is a generic container type, and Person specializes the contents of the collection to contain only instances of the Person class.

Given a generic type, with an arbitrary number of type parameters (e.g., GenericType[Type0, Type1, ..., TypeN]), we use the index of the type parameter to indicate which type the exclusion should refer to. For example, a.0.b, excludes the b field from the first type parameter of a, a.1.b excludes the b field from the second type parameter of a, and so on.

To demonstrate, lets add a self-referencing children relationship to our Person model:

 1from __future__ import annotations
 2
 3from dataclasses import dataclass
 4from typing import List
 5
 6from litestar import Litestar, get
 7from litestar.dto import DataclassDTO, DTOConfig
 8
 9
10@dataclass
11class Address:
12    street: str
13    city: str
14    country: str
15
16
17@dataclass
18class Person:
19    name: str
20    age: int
21    email: str
22    address: Address
23    children: List[Person]
24
25
26class ReadDTO(DataclassDTO[Person]):
27    config = DTOConfig(exclude={"email", "address.street", "children.0.email", "children.0.address"})
28
29
30@get("/person/{name:str}", return_dto=ReadDTO, sync_to_thread=False)
31def get_person(name: str) -> Person:
32    # Your logic to retrieve the person goes here
33    # For demonstration purposes, a placeholder Person instance is returned
34    address = Address(street="123 Main St", city="Cityville", country="Countryland")
35    child1 = Person(name="Child1", age=10, email="child1@example.com", address=address, children=[])
36    child2 = Person(name="Child2", age=8, email="child2@example.com", address=address, children=[])
37    return Person(
38        name=name,
39        age=30,
40        email=f"email_of_{name}@example.com",
41        address=address,
42        children=[child1, child2],
43    )
44
45
46app = Litestar(route_handlers=[get_person])
 1from __future__ import annotations
 2
 3from dataclasses import dataclass
 4
 5from litestar import Litestar, get
 6from litestar.dto import DataclassDTO, DTOConfig
 7
 8
 9@dataclass
10class Address:
11    street: str
12    city: str
13    country: str
14
15
16@dataclass
17class Person:
18    name: str
19    age: int
20    email: str
21    address: Address
22    children: list[Person]
23
24
25class ReadDTO(DataclassDTO[Person]):
26    config = DTOConfig(exclude={"email", "address.street", "children.0.email", "children.0.address"})
27
28
29@get("/person/{name:str}", return_dto=ReadDTO, sync_to_thread=False)
30def get_person(name: str) -> Person:
31    # Your logic to retrieve the person goes here
32    # For demonstration purposes, a placeholder Person instance is returned
33    address = Address(street="123 Main St", city="Cityville", country="Countryland")
34    child1 = Person(name="Child1", age=10, email="child1@example.com", address=address, children=[])
35    child2 = Person(name="Child2", age=8, email="child2@example.com", address=address, children=[])
36    return Person(
37        name=name,
38        age=30,
39        email=f"email_of_{name}@example.com",
40        address=address,
41        children=[child1, child2],
42    )
43
44
45app = Litestar(route_handlers=[get_person])

Now, a Person can have one or many children, and each child can have one or many children, and so on.

We have explicitly excluded the email and address fields of all represented children ("children.0.email", "children.0.address").

In our handler we add children to the Person, and each child has no children of their own.

Here’s the output:

../../_images/nested_collection_exclude.png

Fantastic! Our children are now represented in the output, and their emails and addresses are excluded. However, astute readers may have noticed that we didn’t exclude the children field of Person.children (e.g., children.0.children), yet that field is not represented in the output. To understand why, we’ll next look at the max_nested_depth configuration option.