Skip to content

Commit

Permalink
doc: refactor example in dto factory, moving code to examples folder
Browse files Browse the repository at this point in the history
  • Loading branch information
Kumzy committed Apr 19, 2024
1 parent 36f5555 commit 2fcecff
Show file tree
Hide file tree
Showing 9 changed files with 104 additions and 98 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from dataclasses import dataclass
from typing import Generic, TypeVar

T = TypeVar("T")


@dataclass
class WithCount(Generic[T]):
count: int
data: List[T]
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from advanced_alchemy.dto import SQLAlchemyDTO
from litestar.dto import DTOConfig


class UserDTO(SQLAlchemyDTO[User]):
config = DTOConfig(exclude={"password", "created_at"})
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from litestar import get


@get("/users", dto=UserDTO, sync_to_thread=False)
def get_users() -> WithCount[User]:
return WithCount(
count=1,
data=[
User(
id=1,
name="Litestar User",
password="xyz",
created_at=datetime.now(),
),
],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
config = DTOConfig(
exclude={
"id",
"address.id",
"address.street",
"pets.0.id",
"pets.0.user_id",
}
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from advanced_alchemy.dto import SQLAlchemyDTO
from litestar.dto import DTOConfig


class UserDTO(SQLAlchemyDTO[User]):
config = DTOConfig(exclude={"password", "created_at"})
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from litestar import get
from litestar.pagination import ClassicPagination


@get("/users", dto=UserDTO, sync_to_thread=False)
def get_users() -> ClassicPagination[User]:
return ClassicPagination(
page_size=10,
total_pages=1,
current_page=1,
items=[
User(
id=1,
name="Litestar User",
password="xyz",
created_at=datetime.now(),
),
],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from advanced_alchemy.dto import SQLAlchemyDTO
from litestar.dto import DTOConfig


class UserDTO(SQLAlchemyDTO[User]):
config = DTOConfig(exclude={"password", "created_at"})
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from litestar import get, Response


@get("/users", dto=UserDTO, sync_to_thread=False)
def get_users() -> Response[User]:
return Response(
content=User(
id=1,
name="Litestar User",
password="xyz",
created_at=datetime.now(),
),
headers={"X-Total-Count": "1"},
)
116 changes: 18 additions & 98 deletions docs/usage/dto/1-abstract-dto.rst
Original file line number Diff line number Diff line change
Expand Up @@ -73,17 +73,10 @@ nested models.
Here, the config is created with the exclude parameter, which is a set of strings. Each string represents the path to a
field in the ``User`` object that should be excluded from the output DTO.

.. code-block:: python
config = DTOConfig(
exclude={
"id",
"address.id",
"address.street",
"pets.0.id",
"pets.0.user_id",
}
)
.. literalinclude:: /examples/data_transfer_objects/factory/excluding_fields_2.py
:caption: Excluding fields
:language: python


In this example, ``"id"`` represents the id field of the ``User`` object, ``"address.id"`` and ``"address.street"``
represent fields of the ``Address`` object nested inside the ``User`` object, and ``"pets.0.id"`` and
Expand Down Expand Up @@ -288,53 +281,22 @@ attributes you might need. In this example, we have a ``WithCount`` dataclass wh
The wrapper must be a python generic type with one or more type parameters, and at least one of those type parameters
should describe an instance attribute that will be populated with the data.

.. code-block:: python
from dataclasses import dataclass
from typing import Generic, TypeVar
T = TypeVar("T")
@dataclass
class WithCount(Generic[T]):
count: int
data: List[T]
.. literalinclude:: /examples/data_transfer_objects/factory/enveloping_return_data_1.py
:language: python


Now, create a DTO for your data object and configure it using ``DTOConfig``. In this example, we're excluding
``password`` and ``created_at`` from the final output.

.. code-block:: python
from advanced_alchemy.dto import SQLAlchemyDTO
from litestar.dto import DTOConfig
.. literalinclude:: /examples/data_transfer_objects/factory/enveloping_return_data_2.py
:language: python

class UserDTO(SQLAlchemyDTO[User]):
config = DTOConfig(exclude={"password", "created_at"})

Then, set up your route handler. This example sets up a ``/users`` endpoint, where a list of ``User`` objects is
returned, wrapped in the ``WithCount`` dataclass.

.. code-block:: python
from litestar import get
@get("/users", dto=UserDTO, sync_to_thread=False)
def get_users() -> WithCount[User]:
return WithCount(
count=1,
data=[
User(
id=1,
name="Litestar User",
password="xyz",
created_at=datetime.now(),
),
],
)
.. literalinclude:: /examples/data_transfer_objects/factory/enveloping_return_data_3.py
:language: python


This setup allows the DTO to manage the rendering of ``User`` objects into the response. The DTO Factory type will find
Expand All @@ -359,39 +321,16 @@ Litestar offers paginated response wrapper types, and DTO Factory types can hand
The DTO is defined and configured, in our example, we're excluding ``password`` and ``created_at`` fields from the final
representation of our users.

.. code-block:: python
from advanced_alchemy.dto import SQLAlchemyDTO
from litestar.dto import DTOConfig
.. literalinclude:: /examples/data_transfer_objects/factory/paginated_return_data_1.py
:language: python

class UserDTO(SQLAlchemyDTO[User]):
config = DTOConfig(exclude={"password", "created_at"})

The example sets up a ``/users`` endpoint, where a paginated list of ``User`` objects is returned, wrapped in
:class:`ClassicPagination <.pagination.ClassicPagination>`.

.. code-block:: python
from litestar import get
from litestar.pagination import ClassicPagination
.. literalinclude:: /examples/data_transfer_objects/factory/paginated_return_data_2.py
:language: python

@get("/users", dto=UserDTO, sync_to_thread=False)
def get_users() -> ClassicPagination[User]:
return ClassicPagination(
page_size=10,
total_pages=1,
current_page=1,
items=[
User(
id=1,
name="Litestar User",
password="xyz",
created_at=datetime.now(),
),
],
)

The :class:`ClassicPagination <.pagination.ClassicPagination>` class contains ``page_size`` (number of items per page),
``total_pages`` (total number of pages), ``current_page`` (current page number), and ``items`` (items for the current
Expand All @@ -413,34 +352,15 @@ Litestar's DTO (Data Transfer Object) Factory Types can handle data wrapped in a
We create a DTO for the ``User`` type and configure it using ``DTOConfig`` to exclude ``password`` and ``created_at``
from the serialized output.

.. code-block:: python
from advanced_alchemy.dto import SQLAlchemyDTO
from litestar.dto import DTOConfig
class UserDTO(SQLAlchemyDTO[User]):
config = DTOConfig(exclude={"password", "created_at"})
.. literalinclude:: /examples/data_transfer_objects/factory/response_return_data_1.py
:language: python


The example sets up a ``/users`` endpoint where a ``User`` object is returned wrapped in a ``Response`` type.

.. code-block:: python
from litestar import get, Response
.. literalinclude:: /examples/data_transfer_objects/factory/response_return_data_2.py
:language: python

@get("/users", dto=UserDTO, sync_to_thread=False)
def get_users() -> Response[User]:
return Response(
content=User(
id=1,
name="Litestar User",
password="xyz",
created_at=datetime.now(),
),
headers={"X-Total-Count": "1"},
)

The ``Response`` object encapsulates the ``User`` object in its ``content`` attribute and allows us to configure the
response received by the client. In this case, we add a custom header.

0 comments on commit 2fcecff

Please sign in to comment.