在開發的時候遇到了一個很大的坑(仍是由於是我不瞭解 SQLAlchemy
吧😷), 記錄一下省得日後再踩...python
事情是這樣的, 有三張表, 快速地模擬一下表結構:bash
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:session
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 的一個屬性.app
# 處理數據
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 的時候, 後項會直接覆蓋前項, 因此會形成數據的混亂.spa
這裏就暴露出來一個點, 也就是說 SQLAlchemy 在處理 Join 後獲得的一樣數據是採用 同一個實例 對不一樣數據行進行拼接.code