/main/sqlalchemy-hybrid-p-order

SQLAlchemy и order_by() для hybrid_property

Привет, Мир!

Много от кого слышал очень РАЗНЫХ мнений относительно SQLAlchemy

Лично я ловил очень много кринжатины, но абсолютным оверкилом оказалось сделать ORDER BY с помощью SQLAlchemy.

Имеем:

  • SQLAlchemy
  • postgresql
  • гибридные поля
  • какие-то обычные поля в БД

Кринжатина:

  • SQLAlchemy не соблюдает дефолтное NULLS FIRST(DESC)/NULLS LAST(ASC) в постгресе, а зачем-то ВСЕГДА лепит NULLS FIRST
  • есть огромная разница использования select(User).order_by(User.fullname.desc()) и использованием select(User).order_by(desc(User.fullname))
  • гибридное поле должно возвращать ЗАПРОС, а не SQLColumnExpression

Если не будут соблюдены все эти условия ваше упорядочевание на гибридных свойствах или не будет работать, или будет возвращать неправильные результаты.

Вот, что заработало в моём случае, код гибридного свойства:

@related_clients_count.inplace.expression
@classmethod
def _related_clients_count_expression(cls) -> Select[Any]:
    stmt: Select = select(func.count(RelatedClient.id)).filter(  # pylint: disable=not-callable
        RelatedClient.equipment_id == cls.id,
        RelatedClient.is_operator == false(),
        RelatedClient.is_active == true(),
    )

    return stmt

Код репозитория:

def _apply_ordering(self, statement: Select, ordering: pydantic.BaseModel | None = None) -> Select:
    if ordering is None or not ordering.ordering:  # type: ignore[attr-defined]
        return self._default_ordering(statement=statement)

    for enum_value in ordering.ordering:  # type: ignore[attr-defined]
        field: str = enum_value.value
        order: UnaryExpression[Any]
        nulls_order: UnaryExpression[Any]

        if field.startswith('-'):
            field = field[1:]
            order = desc
            nulls_order = nulls_first
        else:
            order = asc
            nulls_order = nulls_last

        try:
            field_attr: InstrumentedAttribute = getattr(self.model, field)
            statement = statement.order_by(nulls_order(order(field_attr)))
        except AttributeError:
            log.warning(f'Не удалось добавить сортировку для поля {field}, у модели отсутствует данный атрибут')

    return statement



07.08.2025
/main/sqlalchemy-hybrid-p-order