Как выполнить запрос без учета регистра для атрибутов sqlalchemy association_proxy?

#python #sqlalchemy

#python #sqlalchemy

Вопрос:

У меня есть два занятия, трек и Альбом. Чтобы иметь легкий доступ к полям альбома из трека, я создал прокси-сервер ассоциации.

 class Track(MusicItem, Base):
    __tablename__ = "tracks"

    _id = Column(Integer, primary_key=True)
    _album_id = Column(Integer, ForeignKey("albums._id"))
    artist = Column(String, nullable=False, default="")
    path = Column(_PathType, nullable=False, unique=True)
    title = Column(String, nullable=False, default="")

    _album_obj = relationship("Album", back_populates="tracks")

    album = association_proxy('_album_obj', 'title')
  
 class Album(MusicItem, Base):
    __tablename__ = "albums"

    _id = Column(Integer, primary_key=True)
    artist = Column(String, nullable=False, default="")
    title = Column(String, nullable=False, default="")

    tracks = relationship("Track", back_populates="_album_obj", cascade="all, delete")
  

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

При создании фильтра запроса следующее отлично работает для обычных атрибутов дорожки (не связанных с прокси-сервером).

 attr = getattr(Track, field)
attr = sqlalchemy.func.lower(attr)
  

Но, когда attr является атрибутом прокси-сервера ассоциации, возникает следующая ошибка:

 moe/core/query.py:187: in query
    items = session.query(query_cls).filter(*query_filters).all()
.venv/lib/python3.8/site-packages/sqlalchemy/orm/query.py:3346: in all
    return list(self)
.venv/lib/python3.8/site-packages/sqlalchemy/orm/query.py:3508: in __iter__
    return self._execute_and_instances(context)
.venv/lib/python3.8/site-packages/sqlalchemy/orm/query.py:3533: in _execute_and_instances
    result = conn.execute(querycontext.statement, self._params)
.venv/lib/python3.8/site-packages/sqlalchemy/engine/base.py:1011: in execute
    return meth(self, multiparams, params)
.venv/lib/python3.8/site-packages/sqlalchemy/sql/elements.py:298: in _execute_on_connection
    return connection._execute_clauseelement(self, multiparams, params)
.venv/lib/python3.8/site-packages/sqlalchemy/engine/base.py:1124: in _execute_clauseelement
    ret = self._execute_context(
.venv/lib/python3.8/site-packages/sqlalchemy/engine/base.py:1316: in _execute_context
    self._handle_dbapi_exception(
.venv/lib/python3.8/site-packages/sqlalchemy/engine/base.py:1510: in _handle_dbapi_exception
    util.raise_(
.venv/lib/python3.8/site-packages/sqlalchemy/util/compat.py:182: in raise_
    raise exception
.venv/lib/python3.8/site-packages/sqlalchemy/engine/base.py:1276: in _execute_context
    self.dialect.do_execute(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <sqlalchemy.dialects.sqlite.pysqlite.SQLiteDialect_pysqlite object at 0x7f54a0692430>
cursor = <sqlite3.Cursor object at 0x7f54a0638b90>
statement = 'SELECT tracks._id AS tracks__id, tracks._album_id AS tracks__album_id, tracks.artist AS tracks_artist, tracks.path AS tracks_path, tracks.title AS tracks_title nFROM tracks nWHERE lower(?) = ?'
parameters = (ColumnAssociationProxyInstance(AssociationProxy('_album_obj', 'title')), 'tmp')
context = <sqlalchemy.dialects.sqlite.base.SQLiteExecutionContext object at 0x7f54a05b7fa0>

    def do_execute(self, cursor, statement, parameters, context=None):
>       cursor.execute(statement, parameters)
E       sqlalchemy.exc.InterfaceError: (sqlite3.InterfaceError) Error binding parameter 0 - probably unsupported type.
E       [SQL: SELECT tracks._id AS tracks__id, tracks._album_id AS tracks__album_id, tracks.artist AS tracks_artist, tracks.path AS tracks_path, tracks.title AS tracks_title 
E       FROM tracks 
E       WHERE lower(?) = ?]
E       [parameters: (ColumnAssociationProxyInstance(AssociationProxy('_album_obj', 'title')), 'tmp')]
E       (Background on this error at: http://sqlalche.me/e/13/rvf5)

.venv/lib/python3.8/site-packages/sqlalchemy/engine/default.py:593: InterfaceError
  

Не уверен, что это полезно, но вот еще несколько упрощенных кодов для запроса. Я могу подробнее остановиться на этом, если необходимо.

 query_filters = [attr == query_value]
items = session.query(Track).filter(*query_filters).all()
  

Я также видел этот соответствующий фрагмент в документации по созданию пользовательского компаратора, но я не мог понять, как применить это к моему коду. Я добавил его вместе с классами треков / альбомов, но это ничему не помогло, и возникает та же ошибка.

Ответ №1:

Начиная с версии 1.3.19, sqlalchemy не позволяет применять функции SQL к атрибутам прокси-сервера ассоциации. Вот проблема, которая отслеживает это.

Для конкретного случая выполнения поиска без учета регистра вы можете использовать ilike()

 session.query(Track).filter(somecolumn.ilike("value"))
  

ilike() работает как для атрибутов прокси-сервера ассоциации, так и для обычных атрибутов.