Как сделать запрос по нескольким полям, используя sqlalchemy hybrid_property? - PullRequest
1 голос
/ 13 июня 2019

У меня есть модель, определенная следующим образом:

class Patch(Base):
    id = Column(Integer, primary_key=True)
    major = Column(Integer, nullable=False)
    minor = Column(Integer, nullable=False)

    @hybrid_property
    def patch(self) -> str:
        return f'{self.major}.{self.minor}'

    @patch.expression
    def patch(self):
        return func.concat(self.major, '.', self.minor)

Я бы хотел сделать такие запросы:

Patch.query.order_by(Patch.patch)

Что должно быть эквивалентно следующим командам SQL:

SELECT * FROM patch ORDER BY major DESC, minor DESC

Я пытался использовать hybrid_property.expression

    @patch.expression
    def patch(self):
        return self.major, self.minor

но получил исключение:

sqlalchemy.exc.InvalidRequestError: SQL expression, column, or mapped entity expected - got '(<sqlalchemy.orm.attributes.InstrumentedAttribute object at 0x7f1a5cd5fc50>, <sqlalchemy.orm.attributes.InstrumentedAttribute object at 0x7f1a5cd5fd00>)

Я знаю, что можно использовать custrom компаратор , но я не нашел, как это сделать с двумя полями (т.е. major, minor).

Есть идеи?

P.S. оно не должно быть hybrid_property, в любом случае хорошо

1 Ответ

1 голос
/ 13 июня 2019

Ниже приведен порядок с двумя отдельными полями. Стоит отметить, что реализация Python patch по-прежнему является строкой, поэтому порядок будет различаться между объектами Python и строками SQL, если вы не измените patch, чтобы вернуть кортеж (major, minor). Протестировано с Python 3.6.5, SQLAlchemy 1.1.15, psycopg2 2.7.7

from sqlalchemy import create_engine, Column, Integer, func
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm import sessionmaker
from sqlalchemy.sql.functions import concat

engine = create_engine('postgresql://localhost:5432/postgres', echo=True)
Base = declarative_base(bind=engine)


class Patch(Base):

    __tablename__ = "patch"

    id = Column(Integer, primary_key=True)
    major = Column(Integer, nullable=False)
    minor = Column(Integer, nullable=False)

    @hybrid_property
    def patch(self) -> str:
        return f'{self.major}.{self.minor}'

    @patch.expression
    def patch(self):
        return self.major.op(",")(self.minor)

    def __repr__(self):
        return f"Patch object {self.id} {self.patch}"


Base.metadata.create_all()
Session = sessionmaker(bind=engine)
session = Session()

if __name__ == "__main__":
    if session.query(Patch).count() == 0:
        example_patches = [Patch(major=major, minor=minor) for major in range(3) for minor in range(3)]
        session.add_all(example_patches)
        session.commit()

    patches = session.query(Patch).order_by(Patch.patch).all()
    print(patches)
2019-06-13 14:44:27,245 INFO sqlalchemy.engine.base.Engine SELECT patch.id AS patch_id, patch.major AS patch_major, patch.minor AS patch_minor 
  FROM patch ORDER BY patch.major , patch.minor
2019-06-13 14:44:27,245 INFO sqlalchemy.engine.base.Engine {}
[Patch object 2740 0.0, Patch object 2741 0.1, Patch object 2742 0.2, Patch object 2743 0.3, Patch object 2744 0.4, Patch object 2745 0.5, Patch object 2746 0.6, Patch object 2747 0.7, Patch object 2748 0.8, Patch object 2749 0.9, Patch object 2750 0.10, ...
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...