Запрос с необязательными аргументами с использованием PonyORM - PullRequest
2 голосов
/ 26 мая 2019

Я создаю API для библиотеки командообразующих игр. Игры имеют определенные свойства, такие как тип, размер и длина, которые я храню как отношения «многие ко многим». Модель выглядит так:

class Game(db.Entity):
    game_type = Set('Type')
    game_length = Set('Length')
    group_size = Set('GroupSize')
    ...

class Type(db.Entity):  # similar for Length, GroupSize
    game = Set(Game)
    short = Required(str)
    full = Required(str)

Они заполняются значениями разных типов / длин / размеров, а затем присваиваются различным играм. Это отлично работает.

Мне было трудно понять, как разрешить пользователям запроса к базе данных, например, два из них с третьим не дано. Например, я хочу все игры с type=race AND size=medium, но length=None.

Я построил это раньше в SQL с подзапросами и пустыми строками. Это была моя первая попытка работы с PonyORM:

def get_all_games(**kwargs):
    game_type = kwargs.get('game_type', None)
    group_size = kwargs.get('group_size', None)
    game_length = kwargs.get('game_length', None)

    query = select((g, gt, gs, gl) for g in Game
                                     for gt in g.game_type
                                       for gs in g.group_size
                                         for gl in g.game_length)

    if game_type:
        query = query.filter(lambda g, gt, gs, gl: gt.short == game_type)
    if group_size:
        query = query.filter(lambda g, gt, gs, gl: gs.short == group_size)
    if game_length:
        query = query.filter(lambda g, gt, gs, gl: gl.short == game_length)

    query = select(g for (g, gt, gs, gl) in query)

    result = []

    for g in query:
        this_game = get_game(g)
        result.append(this_game)

    return result

Мне кажется, это слишком сложно. Есть ли способ сделать это без упаковки и распаковки кортежей? Может быть, с использованием переменных сразу в запросе без операторов if?

1 Ответ

1 голос
/ 27 мая 2019

Вы можете использовать exists или in в filter. Также вы можете использовать атрибут поднятие для упрощения сложных объединений:

query = Game.select()

if game_length:
    # example with exists
    query = query.filter(lambda g: exists(
        gl for gl in g.game_length
        if gl.min <= game_length and gl.max >= game_length))

if group_size:
    # example with in
    query = query.filter(lambda g: group_size in (
        gs.value for gs in g.group_sizes))

if game_type:
    # example with attribute lifting
    query = query.filter(lambda g: game_type in g.game_types.short)
...