Reusable code
=============

One of the worst things about the current generation of
Python REST frameworks is code re-usability.

- ``django-rest-framework`` is very flexible, but all the flexibility
  comes from importing fully qualified object's path
  strings taken from app's settings.
  It is very hard to properly type a code base like this.
  Using it is also really hard, because you can't easily
  navigate in your source code.
- ``fastapi`` does not even offer a way to write reusable code,
  because it is based on functions, which are really hard to reuse and modify.
  That's why you have to copy paste lots of code just to,
  for example, use the most common things such as JWT auth.

What does ``django-modern-rest`` offer instead?


.. _reusable-controllers:

Reusable controllers
--------------------

We offer a concept of a "reusable controllers".

To make a reusable controller, you need
to provide :class:`typing.TypeVar` instead of a
real :class:`~dmr.serializer.BaseSerializer` type.

Here's an example:

.. literalinclude:: /examples/reusable_code/reusable_controller.py
  :caption: views.py
  :linenos:
  :language: python

This code can work with both ``pydantic`` and ``msgspec`` as serializers.
Let's try to create two exact controllers with exact serializers:

.. tabs::

    .. tab:: msgspec

      .. literalinclude:: /examples/reusable_code/msgspec_controller.py
        :caption: views.py
        :linenos:
        :language: python

    .. tab:: pydantic

      .. literalinclude:: /examples/reusable_code/pydantic_controller.py
        :caption: views.py
        :linenos:
        :language: python

Basically - we just specify what kind of serializer to use. And that's it.
But, this is just the first step. We can do much more!


Generic parsing and response models
-----------------------------------

Next, let's define a reusable controller that will have:

- customizable serializer
- customizable request model
- customizable response body

The process will look exactly the same:

.. literalinclude:: /examples/reusable_code/reusable_parsing.py
  :caption: views.py
  :linenos:
  :language: python

Here we use 3 type variables. One of each of the parts we want to customize.

Important part here is that we defined our own abstract ``convert`` method
to convert unknown request model into an unknown response body.

We would need to implement this method in all of our concrete controllers.

.. tabs::

    .. tab:: msgspec

      .. literalinclude:: /examples/reusable_code/parsing_msgspec.py
        :caption: views.py
        :linenos:
        :language: python

    .. tab:: pydantic

      .. literalinclude:: /examples/reusable_code/parsing_pydantic.py
        :caption: views.py
        :linenos:
        :language: python

Note that ``msgspec`` and ``pydantic`` controllers in this
case have completely different request and response bodies
and completely different OpenAPI schemas.

We can completely customize each controller and all parsing components
and return type validation.

.. important::

  All schema generation and validation rules work
  the same way for concrete controllers.

  We infer the passed values during import time and use real types.
