SQLAlchemy在过滤器中使用like on关系

时间:2019-02-06 21:12:31

标签: python sqlalchemy flask-sqlalchemy

编辑:这与SqlAlchemy - Filtering by Relationship Attribute非常相似,因为我们都试图过滤关系属性。但是,它们在匹配精确值时进行过滤,而我正在通过使用like / contains进行过滤。因此,正如评论中所指出的那样,我的解决方案需要执行额外的步骤,而该步骤在其他文章中并不明显。

让我以此开头:我还是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的所有警报,或获取其alert_url中带有X的所有警报。但是,令我感到困扰的是,我希望能够在相关事件名称中使用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

我了解我可以通过警报的相关事件名称来获得过滤后的警报列表,这些事件可以通过以下方式用options和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()

但是当添加到过滤器集中时,我还没有类似的东西可以工作。

0 个答案:

没有答案