1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
|
Basic Use
=========
Here we demonstrate how to declare DTO types to your route handlers. For demonstration purposes, we assume that we
are working with a data model ``User``, and already have two DTO types created in our application, ``UserDTO``, and
``UserReturnDTO``.
DTO layer parameters
~~~~~~~~~~~~~~~~~~~~
On every :ref:`Layer <layered-architecture>` of the Litestar application there are two parameters that control the DTOs
that will take responsibility for the data received and returned from handlers:
- ``dto``: This parameter describes the DTO that will be used to parse inbound data to be injected as the ``data``
keyword argument for a handler. Additionally, if no ``return_dto`` is declared on the handler, this will also be used
to encode the return data for the handler.
- ``return_dto``: This parameter describes the DTO that will be used to encode data returned from the handler. If not
provided, the DTO described by the ``dto`` parameter is used.
The object provided to both of these parameters must be a class that conforms to the
:class:`AbstractDTO <litestar.dto.base_dto.AbstractDTO>` protocol.
Defining DTOs on handlers
~~~~~~~~~~~~~~~~~~~~~~~~~
The ``dto`` parameter
---------------------
.. literalinclude:: /examples/data_transfer_objects/the_dto_parameter.py
:caption: Using the ``dto`` Parameter
:language: python
In this example, ``UserDTO`` performs decoding of client data into the ``User`` type, and encoding of the returned
``User`` instance into a type that Litestar can encode into bytes.
The ``return_dto`` parameter
----------------------------
.. literalinclude:: /examples/data_transfer_objects/the_return_dto_parameter.py
:caption: Using the ``return_dto`` Parameter
:language: python
In this example, ``UserDTO`` performs decoding of client data into the ``User`` type, and ``UserReturnDTO`` is
responsible for converting the ``User`` instance into a type that Litestar can encode into bytes.
Overriding implicit ``return_dto``
----------------------------------
If a ``return_dto`` type is not declared for a handler, the type declared for the ``dto`` parameter is used for both
decoding and encoding request and response data. If this behavior is undesirable, it can be disabled by explicitly
setting the ``return_dto`` to ``None``.
.. literalinclude:: /examples/data_transfer_objects/overriding_implicit_return_dto.py
:caption: Disable implicit ``return_dto`` behavior
:language: python
In this example, we use ``UserDTO`` to decode request data, and convert it into the ``User`` type, but we want to manage
encoding the response data ourselves, and so we explicitly declare the ``return_dto`` as ``None``.
Defining DTOs on layers
~~~~~~~~~~~~~~~~~~~~~~~
DTOs can be defined on any :ref:`Layer <layered-architecture>` of the application. The DTO type applied is the one
defined in the ownership chain, closest to the handler in question.
.. literalinclude:: /examples/data_transfer_objects/defining_dtos_on_layers.py
:caption: Controller defined DTOs
:language: python
In this example, the ``User`` instance received by any handler that declares a ``data`` kwarg, is converted by the
``UserDTO`` type, and all handler return values are converted into an encodable type by ``UserReturnDTO`` (except for
the ``delete()`` route, which has the ``return_dto`` disabled).
DTOs can similarly be defined on :class:`Routers <litestar.router.Router>` and
:class:`The application <litestar.app.Litestar>` itself.
Improving performance with the codegen backend
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. note::
This feature was introduced in ``2.2.0`` and was hidden behind the ``DTO_CODEGEN``
feature flag. As of ``2.8.0`` it is considered stable and is enabled by default.
It can still be disabled selectively by using the
``DTOConfig(experimental_codegen_backend=False)`` override.
The DTO backend is the part that does the heavy lifting for all the DTO features. It
is responsible for the transforming, validation and parsing. Because of this,
it is also the part with the most significant performance impact. To reduce the overhead
introduced by the DTOs, the DTO codegen backend was introduced; A DTO backend that
increases efficiency by generating optimized Python code at runtime to perform all the
necessary operations.
Disabling the backend
---------------------
You can use ``experimental_codegen_backend=False``
to disable the codegen backend selectively:
.. code-block:: python
from dataclasses import dataclass
from litestar.dto import DTOConfig, DataclassDTO
@dataclass
class Foo:
name: str
class FooDTO(DataclassDTO[Foo]):
config = DTOConfig(experimental_codegen_backend=False)
Enabling the backend
--------------------
.. note:: This is a historical document meant for Litestar versions prior to 2.8.0
This backend was enabled by default since 2.8.0
.. warning:: ``ExperimentalFeatures.DTO_CODEGEN`` is deprecated and will be removed in 3.0.0
.. dropdown:: Enabling DTO codegen backend
:icon: git-pull-request-closed
You can enable this backend globally for all DTOs by passing the appropriate feature
flag to your Litestar application:
.. code-block:: python
from litestar import Litestar
from litestar.config.app import ExperimentalFeatures
app = Litestar(experimental_features=[ExperimentalFeatures.DTO_CODEGEN])
or selectively for individual DTOs:
.. code-block:: python
from dataclasses import dataclass
from litestar.dto import DTOConfig, DataclassDTO
@dataclass
class Foo:
name: str
class FooDTO(DataclassDTO[Foo]):
config = DTOConfig(experimental_codegen_backend=True)
The same flag can be used to disable the backend selectively:
.. code-block:: python
from dataclasses import dataclass
from litestar.dto import DTOConfig, DataclassDTO
@dataclass
class Foo:
name: str
class FooDTO(DataclassDTO[Foo]):
config = DTOConfig(experimental_codegen_backend=False)
Performance improvements
------------------------
These are some preliminary numbers showing the performance increase for certain
operations:
=================================== ===========
operation improvement
=================================== ===========
JSON to Python ~2.5x
JSON to Python (collection) ~3.5x
Python to Python ~2.5x
Python to Python (collection) ~5x
Python to JSON ~5.3x
Python to JSON (collection) ~5.4x
=================================== ===========
.. seealso::
If you are interested in technical details, check out
https://github.com/litestar-org/litestar/pull/2388
|