Pydantic/Django Ninja использует только существующие ключи (даже с None)

создание приложения на Django Ninja со схемами:

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"

, который я затем использую в конечной точке следующим образом:


@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)
    ...

Проблема:

Как видно из построения запроса, я исключаю все значения None, что вполне нормально в большинстве случаев, например:

{
  "major_version": {
    "exact": 3
  },
  "app_name": {
    "icontains": "google"
  }
}

это вернет схему

InputsSchema(major_version=NumericalFilterSchema(gt=None, lt=None, gte=None, lte=None, exact=3), app_name=StringFilterSchema(contains=None, icontains='google', exact=None))

что замечательно... но что, если мое входное значение - None?

например:

{
  "major_version": {
    "exact": null
  },
  "app_name": {
    "icontains": "google"
  }
}

здесь пара ключ-значение exact разрешится в "exact": None, которая будет такой же, как и другие ключи после проверки pydantic/ninja:

InputsSchema(major_version=NumericalFilterSchema(gt=None, lt=None, gte=None, lte=None, exact=None), app_name=StringFilterSchema(contains=None, icontains='google', exact=None))

, что «отстой» для меня, потому что я использую exclude_none=True, который отфильтровывает все Nones - даже то значение, которое я дал.

Есть ли способ избежать создания несуществующих ключей в созданной модели? Итак, после валидации запроса у меня будет что-то вроде:

InputsSchema(major_version=NumericalFilterSchema(exact=None), app_name=StringFilterSchema(icontains='google'))

так что мне не нужно использовать exclude_none=True и передавать None в запрос?

Спасибо!

Джанго __exact lookup [Django-doc] действительно смотрит, является ли объект NULL, и таким образом __exact=None преобразует его в … IS NULL условие.

Но вы также можете фильтровать с помощью __isnull lookup [Django-doc], если вы установите его в __isnull=True, то это преобразует его в … IS NULL условие, а для __isnull=False вы таким образом используете … IS NOT NULL.

Таким образом, вы, вероятно, можете добавить isnull в качестве условия фильтрации, которое, если True/true, заставит значение быть NULL/None.

Вернуться на верх