Фильтр Flask SQLAlchemy Для объекта списка Postgres JSON На основе одной строки

#postgresql #flask #sqlalchemy #flask-sqlalchemy

Вопрос:

У меня есть модель под названием Тестирование. Вызываемое поле alias является полем JSON (на самом деле списком) и имеет такие значения, как ["a", "b"] или ["d", "e"] и так далее.

 class Testing(db.Model):
    __tablename__ = 'testing'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(25))
    alias = db.Column(JSON)

    def __init__(self, name, alias):
        self.name = name
        self.alias = alias
 

В моем представлении flask я выбираю параметр url, который я хочу использовать для фильтрации тестирования, чтобы получить все объекты тестирования, в которых значение параметра находится в списке alias json. Так, например, если url_param_value="a" я хочу, чтобы все объекты тестирования находились там, где "a" есть alias . Таким alias образом, значение ["a", "b"] будет хитом в этом примере.

Вот мой подход, но он не работает, и я предполагаю, что это связано с сериализацией.

 Testing.query.filter(Testing.alias.contains(url_param_val)).all()
 

Я получаю следующую ошибку

 NotImplementedError: Operator 'contains' is not supported on this expression
 

Ответ №1:

name Поле является типом JSON, а не типом массива. У столбцов JSON нет contains метода, даже если вы храните данные массива (как об этом узнает база данных?)

В Postgres вы можете использовать json_array_elements для расширения массива JSON до набора значений JSON; это вернет по одной строке на элемент:

 select id, json_array_elements(alias) as val from testing;

   id    |         val
--------- --------------------
 1       | "a"
 2       | "b"
 

Вы можете использовать это в качестве подзапроса для выбора записей, содержащих соответствующее значение:

 select t.id, t.name, t.alias, cast(q.val as varchar)
from testing t, (
    select id, json_array_elements(alias) as val
    from testing
) q
where q.id=t.id and cast(q.val as varchar) = '"a"';

 

В синтаксисе SQLAlchemy:

 subq = session.query(
    Testing.id,
    func.json_array_elements(Testing.alias).label("val")
).subquery()
q = session.query(Testing).filter(
    cast(subq.c.val, sa.Unicode) == '"a"',
    subq.c.id == Testing.id)
 

Предупреждение: это будет очень неэффективно для больших таблиц; вам, вероятно, лучше исправить типы, соответствующие вашим данным, а затем создать соответствующие индексы.