having an app in Django Ninja with schemas:
class NumericalFilterSchema(Schema):
gt: Optional[int] = None
lt: Optional[int] = None
gte: Optional[int] = None
lte: Optional[int] = None
exact: Optional[int] = None
class Config(Schema.Config):
extra = "forbid"
class StringFilterSchema(Schema):
contains: Optional[str] = None
icontains: Optional[str] = None
exact: Optional[str] = None
class Config(Schema.Config):
extra = "forbid"
class InputsSchema(Schema):
major_version: Optional[NumericalFilterSchema] = None
app_name: Optional[StringFilterSchema] = None
class Config(Schema.Config):
extra = "forbid"
class InputSchema(Schema):
filters: InputsSchema
class Config(Schema.Config):
extra = "forbid"
which I then use in the endpoint like this:
@router_v1.post(
"/apps",
tags=["..."],
auth=AuthBearer(),
)
def dynamic_filter(request: HttpRequest, filters: InputsSchema):
query = Q()
# import ipdb
# ipdb.set_trace()
for key, value in filters.dict(exclude_none=True).items():
# key = translate_field(key) # just abstraction between endpoint keys to db keys
if isinstance(value, dict):
for k, v in value.items():
if v is not None:
query &= Q(**{f"{key}__{k}": v})
else:
query &= Q(**{key: value})
results = Apps.objects.filter(query)
...
Problem:
As you can see in the query building I am excluding all the None values which is fine in the most cases for example:
{
"major_version": {
"exact": 3
},
"app_name": {
"icontains": "google"
}
}
this will return schema
InputsSchema(major_version=NumericalFilterSchema(gt=None, lt=None, gte=None, lte=None, exact=3), app_name=StringFilterSchema(contains=None, icontains='google', exact=None))
which is great... but what if my input value is None?
for example:
{
"major_version": {
"exact": null
},
"app_name": {
"icontains": "google"
}
}
here the exact
key value pair will resolve to "exact": None
which will be the same as other keys after pydantic/ninja validation:
InputsSchema(major_version=NumericalFilterSchema(gt=None, lt=None, gte=None, lte=None, exact=None), app_name=StringFilterSchema(contains=None, icontains='google', exact=None))
which "sucks" for me bcs I use exclude_none=True
which filters out all Nones - even the value I gave.
is there a way to avoid creating the non existing keys in created model? So after the request validation I will have something like:
InputsSchema(major_version=NumericalFilterSchema(exact=None), app_name=StringFilterSchema(icontains='google'))
so I don't have to use the exclude_none=True
and pass the None
to the query?
Thanks!
Django's __exact
lookup [Django-doc] indeed looks if the object is NULL
, and thus __exact=None
will convert it to an … IS NULL
condition.
But you can also filter with the __isnull
lookup [Django-doc], if you set it to __isnull=True
, then this will convert it to … IS NULL
condition, and for __isnull=False
, you thus use … IS NOT NULL
.
You thus probably can add isnull
as filter condition, which will, if True
/true
thus enforce the value to be NULL
/None
.