Flask SQLAlchemy - значение фильтра по умолчанию, если нет результатов для указанного фильтра - PullRequest
0 голосов
/ 04 ноября 2019

Использование Flask SQLAlchemy Я запрашиваю базу данных MySQL с таблицей с именем bar и ищу строки, которые соответствуют фильтру, состоящему из foo и country_code:

foo_filter = 'hello'
country_code_filter = 'ES'
result = Bar.filter_by(foo=foo_filter, country_code=country_code_filter).first()

Приведенный выше кодвернет первую строку, в которой foo = foo_filter и country_code = country_code_filter.

Однако возможно, что у нас не может быть строк для определенных кодов стран, которые соответствуют foo. В этих случаях (т. Е. В случаях, когда приведенный выше запрос возвращает 0 результатов), я хотел бы использовать фильтр страны по умолчанию «RoW», поскольку в нашем наборе данных всегда должно быть значение RoW для каждого возможного значения foo. В неожиданном случае, когда это также не возвращает никаких результатов, следует выдать ошибку. Вот код, который у меня есть для этого:

foo_filter = 'hello'
country_code_filter = 'ES'
result = Bar.filter_by(foo=foo_filter, country_code=country_code_filter).first()
if not result:
    result = Bar.filter_by(foo=foo_filter, country_code='RoW').first()
if not result:
    raise RuntimeException(f"No data for combination {foo_filter}, {country_code_filter} or {foo_filter}, RoW")

Этот подход многократного выполнения похожих запросов и проверки результата каждый раз, пока я не получаю строку, кажется очень грязным / неправильным, но я не смогнайдите более подходящие подходы, которые позволят вам установить «альтернативный» фильтр, когда ваш первоначальный запрос возвращает 0 строк в Flask SQLAlchemy

Есть ли более чистый подход к этому?

1 Ответ

1 голос
/ 04 ноября 2019

В этом решении используется обычная SQLAlchemy, но запуск его на Flask-SQLAlchemy происходит всего за пару изменений синтаксиса.

Идея состоит в том, что мы запрашиваем как нужный код страны, так и резервный, убедитесь, чторезультаты всегда сначала сортируются по нужному коду страны, а затем ограничиваются 1. Например:

result = (
    s.query(Bar)
    .filter(
        Bar.foo == foo_filter,
        Bar.country_code.in_([country_code_filter, "RoW"]),
    )
    .order_by(func.FIELD(Bar.country_code, country_code_filter, "RoW"))
    .limit(1)
    .first()
)

Функция FIELD в MySQL позволяет вам указать пользовательский порядок сортировки, в данном случае этогарантирует, что при наличии результата с нужным кодом страны он всегда будет возвращаться первым. Вы можете прочитать больше о FIELD здесь .

Вот полный тестовый код:

from sqlalchemy_app import Base, Session, engine  # you need to create these
from sqlalchemy import Column, Integer, String, func


class Bar(Base):
    id = Column(Integer, primary_key=True)
    foo = Column(String(32))
    country_code = Column(String(32))


if __name__ == "__main__":
    Base.metadata.create_all(engine)
    s = Session()
    s.add_all(
        [
            Bar(foo="hello", country_code="ES"),
            Bar(foo="hello", country_code="ZIM"),
            Bar(foo="hello", country_code="RoW"),
        ]
    )
    s.commit()
    for foo_filter, country_code_filter, exp_res in (
        ("hello", "ES", "ES"),
        ("hello", "ZIM", "ZIM"),
        ("hello", "AUS", "RoW"),
        ("goodbye", "GBR", None),
    ):
        result = (
            s.query(Bar)
            .filter(
                Bar.foo == foo_filter,
                Bar.country_code.in_([country_code_filter, "RoW"]),
            )
            .order_by(func.FIELD(Bar.country_code, country_code_filter, "RoW"))
            .limit(1)
            .first()
        )
        assert getattr(result, "country_code", None) == exp_res

Поскольку вы говорите, что хотите вызвать исключение в случаечто запрос возвращает None, вы можете поменять метод доступа .first() на .one(), так как запрос ограничивает набор результатов до 1 результата на стороне сервера, вы никогда не получите исключение для возврата более 1 результата ивызвать исключение NoResultFound, если ни одна строка не соответствует запросу.

...