编辑:这与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()
但是当添加到过滤器集中时,我还没有类似的东西可以工作。