sqlalchemy-限制联接表,就好像它们未联接

时间:2018-07-15 06:51:33

标签: python sql postgresql sqlalchemy

我正在使用sqlalchemy,我想从用户那里获取以下数据,并按照给表的顺序应用这些操作:

  

用于过滤数据的关键字,按顺序排序的列,限制和页面   数字

现在我有很多桌子。大多数“儿童”表(没有孩子的表)都可以工作。但是我有一张桌子,桌子上有很多各种各样的关系..双方一对一,一对一,多对多

为实现上述操作,我预先加入了所有表。过滤和排序工作正常,但限制不能给我想要的结果

加入声明:

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)

让我解释一下极限行为与我想要的东西

此代码中的

limit工作正常。由于我联接了所有表,因此它将返回我给它的联接行的数目..如果联接导致额外的5行,并且我要求限制5,例如,它将返回前5个而不管原始表的ID

我想要的是加入之前的极限行为。我只加入了他们以按他们筛选或订购。之后,当我说限制(5)时,我想返回前5个具有不同ID的结果

我尝试了以下操作(一次一次),但是没有用:

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'))

1 个答案:

答案 0 :(得分:1)

您可以使用EXISTS子查询表达式或semijoins替换过滤器的至少一些连接。这样,您的查询可以避免为单个活动生成多个行。仍然可以加入model.getContent(),因为这是多对一的关系:

Type

将关键字参数传递给any()filter_by()类似。它也接受复杂的条件表达式和位置参数。

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) (或DISTINCT ON)应该也可以正常工作,只要您随后将结果用作子查询,然后对其应用排序和限制:

distinct(m.Activity.id)