2017-04-02 31 views
0

我正在用Flask和SQLAlchemy開發一些類似學習管理系統的東西。我有用戶,課程,作業和提交。用戶可以參加幾門課程。每項任務只屬於一門課程。用戶可以爲與課程相關的每項作業提交多份提交材料。我正在嘗試編寫一個函數,該函數將顯示所有提交已提交的用戶可用的所有分配。輸出頁面的結構是這樣的:在SQLAlchemy中分組

  • 課程1
    • 分配1
      • 提交1(2017年1月4日1點02分03秒)
      • 提交2( 2017年1月4日1點03分05秒)
    • 作業2
      • 沒有提交尚未
  • 課程2
    • 作業2
      • 提交1(2017年1月4日2點03分10秒)

因此,對於固定用戶,我必須查詢用戶所屬的所有課程,爲每個課程查詢所有分配,併爲每個分配查詢屬於特定用戶的所有提交。

我可以用一些Python代碼來實現,但我相信這可以通過明智的SQLAlchemy查詢來完成。詳情如下。

我的模型specificion基本上是這樣的:

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

你目前所做的是做事的「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小號屬於current_user,用自己Assignment小號一起和Submission S:

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

然後上述查詢將產生的結果,例如:

Out[110]: [<__main__.Course at 0x7f37a4a2dcc0>] 

In [111]: _[0].assignments_collection[0].submissions_collection 
Out[111]: [<__main__.Submission at 0x7f37a49d5358>]