在SQLAlchemy中进行分组

时间:2017-04-02 15:29:09

标签: python sqlalchemy flask-sqlalchemy

我正在使用Flask和SQLAlchemy开发类似学习管理系统的东西。我有用户,课程,作业和提交。用户可以参加几门课程。每个作业只属于一门课程。用户可以为与课程相关的每项作业提交多份提交。我正在尝试编写一个功能,该功能将显示已提交所有提交的用户可用的所有分配。输出页面的结构如下:

  • 课程1
    • 作业1
      • 提交1(01-04-2017 01:02:03)
      • 提交2(01-04-2017 01:03:05)
    • 作业2
      • 尚未提交
  • 课程2
    • 作业2
      • 提交1(01-04-2017 02:03:10)

因此,对于固定用户,我必须查询用户所属的所有课程,每个课程查询所有作业以及每个作业查询属于特定用户的所有提交。

我可以使用一些Python代码来完成它,但我相信这可以通过我要求的明智的SQLAlchemy查询来完成。详情如下。

我的模特定义基本上是这样的:

class Course(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    assignments = db.relationship('Assignment', backref='course',
                                  lazy='dynamic')
class User(db.Model, UserMixin):
    id = db.Column(db.Integer, primary_key=True)
    courses = db.relationship('Course', secondary=courses_users,
                              backref=db.backref(
                                  'users', lazy='dynamic'))
    submissions = db.relationship('Submission', backref=db.backref('user'),
                                  lazy='dynamic')
class Assignment(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    course_id = db.Column(db.Integer, db.ForeignKey('course.id'))
    submissions = db.relationship('Submission', backref='assignment',
                                  lazy='dynamic')
class Submission(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    assignment_id = db.Column(db.Integer, db.ForeignKey('assignment.id'))
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))

目前,我通过以下方式为list_assignments视图准备数据:

@app.route("/list/assignments")
@login_required
def list_assignments():
    submissions = current_user.submissions.all()
    courses = current_user.courses
    mycourses = []
    for course in courses:
        mycourse={'id': course.id, 'assignments': []}
        for assignment in course.assignments:
            mycourse['assignments'].append(
                (assignment,
                 [s for s in submissions if s.assignment == assignment]))
        mycourses.append(mycourse)
    return render_template("list_assignments.html",
                           mycourses=mycourses)

所以我有一个课程列表,每个课程都包含一个分配记录列表,每个分配记录是一对,第一个元素是一个赋值本身,第二个元素是属于这个赋值的提交列表用户。根本不是很优雅。

用SQLAlchemy查询替换此逻辑的最佳方法是什么?加入? GROUPBY'?的

1 个答案:

答案 0 :(得分:1)

您目前所拥有的是" ORM方式"做事 - 没有错。您处理实例及其关系,就好像它们是包含其他对象集合的纯Python对象一样。实际上,因为您已将关系配置为lazy='dynamic',所以对关系Query对象的每次迭代都会针对数据库触发SQL查询。

如果您要将关系配置为充当集合(懒惰或渴望),或者将其他关系添加到模型中,则可以在单个查询中获取整个图形:

# Lazy collections that by default issue SELECTs
Course.assignments_collection = db.relationship('Assignment')
Assignment.submissions_collection = db.relationship('Submission') 

由于您希望将Submission仅限制为当前用户拥有的那些,我们需要一个别名:

submissions = db.aliased(
    Submission,
    db.session.query(Submission).
        filter(Submission.user == current_user).
        subquery()
)

然后,我们可以形成提取加载查询,该查询提取属于Course的{​​{1}}及其current_userAssignment

Submission

然后,您将使用我们定义的新关系属性来访问急切加载的集合。例如,如果我们要:

courses = db.session.query(Course).\
    outerjoin(Course.assignments_collection).\
    outerjoin(submissions, Assignment.submissions_collection).\
    options(db.contains_eager(Course.assignments_collection).
            contains_eager(Assignment.submissions_collection,
                           alias=submissions)).\
    filter(Course.users.any(User.id == current_user.id)).\
    all()

然后上面的查询将产生如下结果:

In [104]: current_user = User()

In [105]: db.session.add(current_user)

In [106]: db.session.add(Course(assignments=[Assignment(submissions=[Submission(user=current_user)])], users=[current_user]))

In [107]: db.session.commit()