我正在使用 sqlalchemy,我想从用户那里获取以下数据并按照给定表的顺序应用这些操作:
keyword to filter the data with, column to order by, limit and page number
现在我有很多 table 。大多数“子表”——没有子表——工作。但是我有一张表,里面有很多各种各样的关系..双方一对多,一对一和多对多
为了实现上述操作,我事先加入了所有的表。过滤和排序工作正常但限制没有给我想要的结果
加入声明:
records = m.Activity.query.join(m.Event, m.Activity.events) \
.join(m.DateLocation, m.Activity.date_locations) \
.join(m.Goal, m.Activity.goals) \
.join(m.Type, m.Activity.type)
过滤和排序包含很多不必要的信息,基本上是这样的:
# filtering if column == event
records = records.filter(m.Event.name == keyword)
# ordering if column == type and desc was chosen
records = records.order_by(m.Type.name.desc())
最后是限制和分页:
records = records.limit(limit)
records = records.offset((page - 1) * limit)
让我解释一下限制行为与我想要的:
此代码中的限制工作正常。因为我加入了所有的表,它会返回我给它的加入行的数量。如果加入导致额外的 5 行,例如我要求限制 5,它会返回前 5,不管原始表 id
我要的是加入前的限制行为。我加入他们只是为了按他们过滤或订购。之后,当我说 limit (5) 时,我想返回具有不同 id 的前 5 个结果
我尝试了以下方法(一次一个)但没有成功:
records = records.distinct(m.Activity.id).limit(limit)
records = records.group_by(m.Activity.id).limit(limit)
records = records.from_self().limit(limit)
我尝试了提供的解决方案 here .它确实有效,但是它在加入之前限制了数据集。这对我来说不起作用,因为我需要限制过滤后的数据
编辑:模型:
EventsInActivities = db.Table(
'events_in_activities',
db.Column('activity_id', db.String, db.ForeignKey('activity.id')),
db.Column('event_id', db.Integer(), db.ForeignKey('event.id'))
)
class Event(db.Model, BaseMixin):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String)
class Type(db.Model, BaseMixin):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String, unique=True)
activities = db.relationship("Activity", backref="type", lazy='dynamic')
class Goal(db.Model, BaseMixin):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
activity_id = db.Column(db.String, db.ForeignKey('activity.id'), primary_key=True)
name = db.Column(db.String())
class DateLocation(db.Model, BaseMixin):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
activity_id = db.Column(db.String, db.ForeignKey('activity.id'), primary_key=True)
start_date = db.Column(db.DateTime)
end_date = db.Column(db.DateTime)
location = db.Column(db.String())
class Activity(db.Model, BaseMixin):
id = db.Column(db.String, primary_key=True)
name = db.Column(db.String())
type_id = db.Column(db.Integer, db.ForeignKey('type.id'))
date_locations = db.relationship("DateLocation", order_by='DateLocation.start_date', cascade="all, delete", backref="activity", lazy='dynamic')
goals = db.relationship("Goal", cascade="all, delete", backref="activity", lazy='dynamic')
events = db.relationship('Event', secondary=EventsInActivities, backref=db.backref('activities', lazy='dynamic'))
最佳答案
您至少可以用 EXISTS 替换过滤器的一些连接子查询表达式,或 semijoins在某种方式。这样您的查询就可以避免为单个事件生成多行。仍然可以加入 Type
,因为它是多对一的关系:
records = m.Activity.query.\
join(m.Activity.type).\
filter(m.Activity.events.any(name=keyword)).\
filter(m.Activity.goals.any(name=...)).\
filter(...).\
order_by(m.Type.name.desc()).\
limit(limit).\
offset((page - 1) * limit)
将关键字参数传递给 any()
是与 filter_by()
类似的简写.它也接受复杂的条件表达式作为位置参数。
distinct(m.Activity.id)
或 DISTINCT ON 应该也能正常工作,只要您随后将结果用作子查询,然后对其应用排序和限制:
records = m.Activity.query.\
join(m.Activity.events).\
join(m.Activity.date_locations).\
join(m.Activity.goals).\
filter(m.Event.name == keyword).\
filter(...).\
distinct(m.Activity.id).\
from_self().\
join(m.Activity.type).\
order_by(m.Type.name.desc()).\
limit(limit).\
offset((page - 1) * limit)
关于python - sqlalchemy - 连接表的限制就好像它们没有连接一样,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51346000/