Как удалить строку из набора результатов запроса в SQLAlchemy? - PullRequest
0 голосов
/ 08 мая 2020

Мне нужна функция, которая делает что-то вроде:

query = Member.query.filter(Member.age > 3)
query = filter_query(query)

def filter_query(query):
    for q in query:
        if not q.someAttr == "someValue":
            query.remove(q) # <-- this doesn't work. is there anything similar?
    return query

Пожалуйста, не спрашивайте, зачем мне это нужно. Это долгая история)

ОБНОВЛЕНИЕ

Я решил использовать, может быть, не самое красивое, но все же решение, которое пока работает.

query = Member.query.filter(Member.age > 3)
query = filter_query(query)

def filter_query(query):
    id_list = []
    for q in query:
        if q.someAttr == "someValue":
            id_list.append(q.id)
    return Member.query.filter(Member.id.in_(id_list))

1 Ответ

1 голос
/ 09 мая 2020

Проблема в том, что в Sqlite должен выполняться запрос, который включает фильтр ILIKE для текста, отличного от ASCII, но сборка по умолчанию Sqlite не поддерживает сопоставление без учета регистра для такого текста .

Одним из возможных способов обхода проблемы является хранение нормализованной по регистру копии текста в другом столбце. Это предполагает, что удвоение объема памяти для текста не является проблемой.

Простой способ сделать это будет выглядеть так:

class Likeable(Base):
    __tablename__ = 'likeable'

    id = sa.Column(sa.Integer, primary_key=True)
    name = sa.Column(sa.String(32))
    lowercase_name = sa.Column(sa.String(32))

Экземпляры могут быть созданы следующим образом:

likeable = Likeable(name='Россия', lowercase_name='россия') 

и запрашивается следующим образом

session.query(Likeable).filter(Likeable.lowercase_name.like('р%'))

Это нормально, требуется, чтобы name и lowercase_name всегда обновлялись одновременно. Мы можем обойти это, замаскировав lowercase_name с помощью гибридного свойства и перехватив присвоения name с помощью прослушивателя атрибутов . Слушатель обнаруживает изменение name и передает новое значение установщику lowercase_name.

import sqlalchemy as sa
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm import Session


Base = declarative_base()


class Likeable(Base):
    __tablename__ = 'likeable'

    id = sa.Column(sa.Integer, primary_key=True)
    name = sa.Column(sa.String(32))
    _lowercase_name = sa.Column(sa.String(32))

    def __repr__(self):
        return f"Likeable(name='{self.name}', lowercase_name='{self.lowercase_name}')"

    @hybrid_property
    def lowercase_name(self):
        return self._lowercase_name

    @lowercase_name.setter
    def lowercase_name(self, value):
        self._lowercase_name = value.lower()


@sa.event.listens_for(Likeable.name, 'set')
def receive_set(target, value, oldvalue, initiator):
    target.lowercase_name = value

Запуск этого кода:

engine = sa.create_engine('sqlite:///', echo=True)
Base.metadata.create_all(bind=engine)

session = Session(bind=engine)
likeables = [Likeable(name=n) for n in ['Россия', 'Русский', 'Французский']]
session.add_all(likeables)
session.commit()
session.close()

session = Session(bind=engine)
q = session.query(Likeable).filter(Likeable.lowercase_name.like('р%'))
for r in q:
    print(r)
session.close()

Производит следующий вывод:

Likeable(name='Россия', lowercase_name='россия')
Likeable(name='Русский', lowercase_name='русский')

Это демонстрационный код. Для производства вы можете добавить проверки, чтобы гарантировать, что name и lowercase_name не могут рассинхронизироваться.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...