1

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!

4
  • 1
    I'm not really following with the schema. Django's __exact filter indeed looks if it is None, and then transforms it to ... IS NULL. Another test is __isnull=True. Commented Dec 11, 2024 at 13:53
  • 1
    So perhaps you can use isnull as filter criteria, but again, the thing I am missing is how you convert the response to an InputsSchema. Commented Dec 11, 2024 at 13:54
  • oh god I think you are right with the __is_null..
    – Leemosh
    Commented Dec 11, 2024 at 13:56
  • Basically all the validation happens under the hood when the request comes to the endpoint and there the creation of "non_existing_key_in_input":None happens. But ofc I'm dumb and the is__null should work just fine. Thanks!
    – Leemosh
    Commented Dec 11, 2024 at 13:59

1 Answer 1

0

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.

Not the answer you're looking for? Browse other questions tagged or ask your own question.