2017-04-02 9 views
0

私は、FlaskとSQLAlchemyを使って少し学習管理システムのようなものを開発しています。私はユーザー、コース、課題、提出物を持っています。ユーザーは複数のコースを受講できます。各課題は正確に1つのコースに属します。ユーザーは、自分のコースに関連付けられたすべての課題に対して複数の提出を提出することができます。私は、すでに提出されたすべての投稿をユーザが利用できるすべての課題を表示する関数を作成しようとしています。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クエリで行うことができると信じています。詳細は以下のとおりです。

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ビュー用のデータを準備

マイモデルspecificionはこのように基本的にあるので

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

私はコースのリストを持っており、各コースには含まれてい各割り当てレコードはペアであり、第1の要素は割り当て自体であり、第2の要素は、この割り当ておよびこのユーザに属するサブミッションのリストである。全く優雅ではない。

このロジックをSQLAlchemyクエリで置き換える最も良い方法は何ですか?結合?グループビー?

答えて

1

あなたが現在行っていることは、何かを行う「ORM方法」です。何も問題はありません。インスタンスとその関係は、他のオブジェクトのコレクションを含むプレーンなPythonオブジェクトであるかのように扱います。実際には、あなたの関係をlazy='dynamic'の関係に設定しているので、QueryオブジェクトはDBに対して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 Sをフェッチイーガーローディングクエリを形成することができます0は、そのAssignment sおよび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() 

あなたはその後、私たちは熱心にロードされたコレクションにアクセスするために定義された新しい関係の属性を使用します。私たちがした場合たとえば:

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

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

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

その後、上記のクエリは、次のような結果をもたらすであろう

関連する問題