我正在使用Flask和SQLAlchemy开发类似学习管理系统的东西。我有用户,课程,作业和提交。用户可以参加几门课程。每个作业只属于一门课程。用户可以为与课程相关的每项作业提交多份提交。我正在尝试编写一个功能,该功能将显示已提交所有提交的用户可用的所有分配。输出页面的结构如下:
因此,对于固定用户,我必须查询用户所属的所有课程,每个课程查询所有作业以及每个作业查询属于特定用户的所有提交。
我可以使用一些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'?的
答案 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_user
和Assignment
:
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()