SQLAlchemy, используя как на отношения внутри фильтра - PullRequest
0 голосов
/ 07 февраля 2019

РЕДАКТИРОВАТЬ: Это очень похоже на SqlAlchemy - фильтрация по атрибуту отношения в том, что мы оба пытаемся фильтровать по атрибутам отношения.Тем не менее, они фильтруют на соответствие точному значению, в то время как я фильтрую с использованием подобно / содержит.Из-за этого, как указано в комментариях, мое решение требует дополнительного шага, который не был очевиден в другом посте.

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

У меня есть Flask API, который определил «оповещения» и «события».Предупреждение может принадлежать только одному событию, а событие может иметь несколько предупреждений.Схема для предупреждений следующая:

class Alert(PaginatedAPIMixin, db.Model):
    __tablename__ = 'alert'

    id = db.Column(db.Integer, primary_key=True, nullable=False)
    type = db.relationship('AlertType')
    type_id = db.Column(db.Integer, db.ForeignKey('alert_type.id'), nullable=False)
    url = db.Column(db.String(512), unique=True, nullable=False)
    event_id = db.Column(db.Integer, db.ForeignKey('event.id'), nullable=False)
    event = db.relationship('Event')

    def __str__(self):
        return str(self.url)

    def to_dict(self):
        return {'id': self.id,
                'event': self.event.name,
                'type': self.type.value,
                'url': self.url}

Одна из конечных точек API позволяет получить список предупреждений на основе различных критериев фильтрации.Например, получить все оповещения о alert_type X или все оповещения с X внутри своего alert_url.Однако единственное, что меня озадачивает, - это то, что я хочу иметь возможность получать все оповещения с символом X в названии связанных с ними событий.

Вот функция конечной точки API (бит закомментированных событий является моей первоначальной «наивностью»«подход, который не работает из-за того, что событие является связью), но вы можете понять, что я пытаюсь сделать с фильтрацией.

def read_alerts():
    """ Gets a list of all the alerts. """

    filters = set()

    # Event filter
    #if 'event' in request.args:
    #    filters.add(Alert.event.name.like('%{}%'.format(request.args.get('event'))))

    # URL filter
    if 'url' in request.args:
        filters.add(Alert.url.like('%{}%'.format(request.args.get('url'))))

    # Type filter
    if 'type' in request.args:
        type_ = AlertType.query.filter_by(value=request.args.get('type')).first()
        if type_:
            type_id = type_.id
        else:
            type_id = -1
        filters.add(Alert.type_id == type_id)

    data = Alert.to_collection_dict(Alert.query.filter(*filters), 'api.read_alerts', **request.args)
    return jsonify(data)

Набор фильтров, который создается, подается вФункция to_collection_dict (), которая по существу возвращает разбитый на страницы список запроса со всеми фильтрами.

def to_collection_dict(query, endpoint, **kwargs):
    """ Returns a paginated dictionary of a query. """

    # Create a copy of the request arguments so that we can modify them.
    args = kwargs.copy()

    # Read the page and per_page values or use the defaults.
    page = int(args.get('page', 1))
    per_page = min(int(args.get('per_page', 10)), 100)

    # Now that we have the page and per_page values, remove them
    # from the arguments so that the url_for function does not
    # receive duplicates of them.
    try:
        del args['page']
    except KeyError:
        pass
    try:
        del args['per_page']
    except KeyError:
        pass

    # Paginate the query.
    resources = query.paginate(page, per_page, False)

    # Generate the response dictionary.
    data = {
        'items': [item.to_dict() for item in resources.items],
        '_meta': {
            'page': page,
            'per_page': per_page,
            'total_pages': resources.pages,
            'total_items': resources.total
        },
        '_links': {
            'self': url_for(endpoint, page=page, per_page=per_page, **args),
            'next': url_for(endpoint, page=page + 1, per_page=per_page, **args) if resources.has_next else None,
            'prev': url_for(endpoint, page=page - 1, per_page=per_page, **args) if resources.has_prev else None
        }
    }
    return data

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

alerts = db.session.query(Alert).join(Alert.event).options(contains_eager(Alert.event)).filter(Event.name.like('%{}%'.format(request.args.get('event')))).all()

Но я не получил что-то подобное, чтобы работать при добавлении в набор фильтров.

...