Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pydantic V2 full support + massive refactoring #88

Open
wants to merge 78 commits into
base: dev-3.x
Choose a base branch
from

Conversation

mahenzon
Copy link
Member

@mahenzon mahenzon commented Jun 9, 2024

TODO:

  • HTTPMethod переименовать и / или унифицировать
  • обновить примеры
  • обновить линтеры: ruff, black
  • избавиться от poetry, оставить только Hatch
  • mypy
  • orjson instead of simplejson
  • hatch env test (copy from ri-sdk)
  • older python versions support

can_be_none = check_can_be_none(fields)

if value is None:
if can_be_none:
return getattr(model_column, operator)(value)

raise InvalidFilters(detail=f"The field `{schema_field.title}` can't be null")
raise InvalidFilters(detail=f"The field `{field_name}` can't be null")
Copy link
Collaborator

@CosmoV CosmoV Jun 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

В сообщении об ошибке имя поля должно быть то же, что и в сваггере. Там может быть alias, это оно?

value: Any,
schema_field: FieldInfo,
) -> Tuple[Optional[Any], List[str]]:
) -> tuple[Any | None, list[str]]:
result_value, errors = None, []

for type_to_cast in types:
try:
# Создаем экземпляр схемы для валидации значения
Copy link
Collaborator

@CosmoV CosmoV Jun 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Коммент соответствует действительности? Создание экземпляра схемы довольно дорогая операция, а в либе и так есть тормоза. Если нам нужно кастануть несериализованное значение в объект python надо найти другой способ это сделать


if SPLIT_REL in field:
value = {"field": SPLIT_REL.join(field.split(SPLIT_REL)[1:]), "order": self.sort_["order"]}
alias = aliased(self.related_model)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Поскольку планируем выпускать v3 версию либы есть некоторые мысли.

Я считаю, что использование рекурсивной логики и модели Node в данном репо антипаттерн. Если бы где-то строили AST и с ним работали еще имело бы смысл (не факт), а так код получается сложный, непонятный, и слабо расширяемый. Метод resolve становится точкой входа логику, которая будет бесконечно обрастать if условиями. Визуально может выглядеть привлекательно, но простота работы и производительность ниже чем конструирование фильтров через набор независимых функций.

В модуле fastapi_jsonapi/data_layers/filtering/sqlalchemy.py набор функций создан таким образом, чтобы они были максимально простыми и тестуруемыми.

Конкретно с алиасингом вида

alias = aliased(self.related_model)

уже натерпелись проблем когда создавались некорректные join конструкции.

Я предлагаю отказаться от этого класса и сделать следующее

  1. Определить интерфейсы для набора функций фильтрации и сортировки. Чтобы выглядели наподобие сингатуры build_filter_expressions
  2. Вынести эти интерфейсы в shared модули filtering/sorgting общие для различных DataLayer
  3. Вынести специфичные для sqla вещи в filterint/sorting модули в data_layers/sqlalchemy

На уровне DataLayer предлагаю делать вызовы как-то так. Псевдокод

from data_layers.shared.filteting import build_filter_expressions
from data_layers.shared.sorting import build_sort_expressions
from data_layers.sqla.filtering import specific_filter_handler
from data_layers.sqla.sorting import specific_sorting_getter

class SqlaDataLayer:
    async def build_filters(query_filter_conditions):
        return build_filter_expressions(
            dl_specific_callback=specific_filter_handler, 
            ... # other args
        )

    async def build_sorts(query_sort_conditions):
        return build_sorts_expressions(
            dl_specific_callback=specific_sorting_getter, 
            ... # other args
        )
        

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

# don't allow arbitrary types, we don't know their behaviour
return TypeAdapter(field_type).validate_python
except PydanticSchemaGenerationError:
return field_type
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Я бы добавил сюда log.error

return None


def validate_relationship_name(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Я бы назвал check_relationship_exists поскольку проверка тут именно на существование

target_schema: type[TypeSchema],
target_model: type[TypeModel],
relationships_info: dict[RelationshipPath, RelationshipFilteringInfo],
) -> BinaryExpression:
Copy link
Collaborator

@CosmoV CosmoV Jun 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Над аннотацией return значения надо подумать. Возврат BinaryExpression специфично для sqla, по-хорошему было бы обобщить эту функцию превратив в публичный интерфейс и вынести её в разделяемый различными DataLayer модуль

@mahenzon mahenzon mentioned this pull request Jun 25, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants