SQLAlchemy Join Query 得到的不同資料行相同資料是同一個例項

Dxtr發表於2018-12-12

在開發的時候遇到了一個很大的坑(還是因為是我不瞭解 SQLAlchemy 吧?), 記錄一下免得往後再踩...

事情是這樣的, 有三張表, 快速地模擬一下表結構:

student表
|------+--------+----------+--------------|
|  id  +  name  +  gender  +  deleted_at  |
|------+--------+----------+--------------|

course表
|------+---------+--------------+--------------|
|  id  +  title  +  teacher_id  +  deleted_at  |
|------+---------+--------------+--------------|

student_course_rel表
|------+--------------+-------------+--------------|
|  id  |  student_id  +  course_id  +  deleted_at  |
|------+--------------+-------------+--------------|

複製程式碼

這裡模擬了一個場景需求, 需要列出某位老師所教授的不同課程的學生列表, 並且要支援全量更新(更新的時候傳入的列表為全部學生列表(新增和刪除)) 這個時候我要 Join 三張表, 並且同一個學生對應的不同課程的 rel id 也得記錄, 這樣我就可以知道那些是新增的那些是刪除的. 也就是說, 我需要將使用者和課程的 rel id 進行繫結. 所以, 我就用這條語句進行 Query:

teacher_id = 'teacher_id'
query_result = db.session.query(Course, Student, StudentCourseRel)\
    .join(StudentCourseRel, Course.id == StudentCourseRel.course_id)\
    .join(Student, StudentCourseRel.student_id == Student.id)\
    .filter(Course.teacher_id == teacher_id, Course.deleted_at.is_(None),
            Student.deleted_at.is_(None), StudentCourseRel.deleted_at.is_(None))\
    .all()
# query 出來的結果大致是這樣的
[(course_a, stu_1, rel_1), (course_a, stu_2, rel_2), (course_b, stu_1, rel_3), (course_c, stu_3, rel_4)]
複製程式碼

這個時候, query 出來的結果確實是我想要的, 每個使用者每個課程都會有一條記錄. 列表沒有任何問題. 但是在更新的時候就出 bug 了.
問題就出在student, course, rel_id繫結這一步使用者. 和課程關係的繫結我是遍歷資料列表, 並且將 rel id 作為 student 的一個屬性.

# 處理資料
result = set()
for item in query_result:
    course, student, rel = item
    if course not in result:
        course.students = []
        result.add(container)
    user.rel_id = rel.id
    course.users.append(user)
return result
複製程式碼

就上例而言, SQLAlchemy 得到的資料中, course_a 所對應的 stu_1 和 course_b 所對應的 stu_1 是同一個例項, 也就是說在遍歷並且進行賦值 rel_id 的時候, 後項會直接覆蓋前項, 所以會造成資料的混亂.

這裡就暴露出來一個點, 也就是說 SQLAlchemy 在處理 Join 後得到的同樣資料是採用 同一個例項 對不同資料行進行拼接.

相關文章