Extending Pydantic use cases with Django — Filtering

IT racer
4 min readSep 22, 2021

--

Pydantic use-case with Django ORM

Some time ago I came across an article of a developer who was sharing his ideas about hype around usage of Pydantic library in Django projects. To cut the long story short, he was against it, because he did not see many applications for this lib in Django projects. And compared it to DRF functionality, for example.

Myself being an active Pydantic user, I was somewhat disappointed that someone could be so unhappy with it. But then I started thinking if it was possible to do something about it. So I took one single task I was working on and decided to apply Pyndantic to it. So, it is more of an exercise, than a fully tested applied instrument.

What we will do here is provide an interface for a Pydantic schema to filter a Django Model by values, which were sent to us in a GET query parameters, along with fancy Pydantic style validation.

Step 1

First of all we shall have a Django model with some fields in it. To make the case interesting it shall have datetime and foreign keys in it. The related models shall not be shown here, for they are plain and simple and can be reversed from the provided code.

Sample Django model

Step 2

Now we need to prepare a special mixin for our Pydantic schema. We shall call it DjangoFilterMixin, and it shall have 2 class variables:

__model__ — which holds the reference to the model class;

__fields__ — which will inherit from PydanticModel __fields__.

DjangoFilterMixin class

The filtering logic shall be implemented in “filter” method, which basically. iterates over Pydantic fields, retrieves the filters and constructs a query from django Q object by reducing the list of necessary filters with the operator. In order to reduce the filters we shall use built-in operator module, which provides functional api to basic logical operators, such as “and” or “|”.

And that’s it. There are no other external dependencies other than Django and Pydantic, everything is based on build-in modules.

Step 3

Next, we shall set up a schema, based on Pydantic BaseModel. And here we shall have some things to note:

  1. We use “extra” options in Fields description, which accepts any valid dict, and stores some extra info to describe field schema. There we put 2 parameters (if both needed). “filters” — it describes a list of field filters to be used upon querying a django model in Q object. These filters as you know, can accept __in, __lte, __gte etc. “filter_type” — has a string type of concatenation logic which should be used with these filters. It can either be “AND” or “OR” (or any other meaningful value).
  2. We add an additional class parameter __model__, where we refer the Django model class, which we created in the previous step.
  3. We add the DjangoFilteriMixin mixin, which was created on the previous step.

That’s it. All the rest is self explanatory: we have an alias generator function, which will convert incoming query parameters from camesCase to snake_case so that the populate the schema instance. And a shared validator, which effectively not validates, but rather pre-formats the incoming values to support __in filtering, by splitting the query field comma separated values.

Mixin in use

The last thing left is to use this schema in a view. Let’s set up a simple APIView, which shall respond with a queryset’s count. And do not forget to attach it to a url.

Out upgraded Pydantic Schema in use

That’s it. And again, wanted to reiterate, that this mixin was made purely as an exercise and might need improving for some edge cases.

--

--