首页 > 解决方案 > SQLAlchemy 关系:查找具有一个或多个共同属性的实例

问题描述

我不知道如何表达这个问题,因为我不知道我想要达到的目标的术语(mods,请随时提出改进建议)。

我有两种类型的用户,StudentInstructor. 两者都可以有一个或多个Tags,它们基本上是一种流派(摇滚、放克等)。为了与一个关联StudentInstructorTag有一个关联表。

我有最后一个模型,Lesson它有Student和的 ID Instructor。在创建 aLesson时,仅设置这些 ID 中的一个,具体取决于它是它Student还是Instructor创建它的 an。如果 aStudent创建 a Lesson,我需要找到所有Instructor与 s 有一个或多个Tags 的 s Student。我该如何实现这一目标?我对数据库关系的了解有限,因此不胜感激!

这是模型的代码:

class Instructor(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    genres = db.relationship("Tag", secondary=instructor_tag_association_table, backref=db.backref("instructors", lazy="dynamic"))

class Student(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    genres = db.relationship("Tag", secondary=student_tag_association_table, backref=db.backref("students", lazy="dynamic"))

class Tag(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    genre = db.Column(db.String)

class Lesson(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    instructor_id = db.Column(db.Integer, db.ForeignKey("instructor.id"))
    student_id = db.Column(db.Integer, db.ForeignKey("student.id"))

# Association tables
instructor_tag_association_table = db.Table(
    "instructor_tags",
    db.Column("instructor_id", db.Integer, db.ForeignKey("instructor.id")),
    db.Column("tag_id", db.Integer, db.ForeignKey("tag.id")),
)
student_tag_association_table = db.Table(
    "student_tags",
    db.Column("student_id", db.Integer, db.ForeignKey("student.id")),
    db.Column("tag_id", db.Integer, db.ForeignKey("tag.id")),
)

我知道这在几个层面上都是错误的,但是说我有一个单一的 ID Instructor,然后我正在寻找这样的查询来寻找教训:

instructor_id = 1
instructor = Instructor.query.filter_by(id=instructor_id).first()
eligible_lessons = Lesson.query.filter(Lesson.student_id.has("genre in instructor.genre"))

标签: pythonsqlalchemy

解决方案


我的回答受到了这个启发。

如果我的解释正确,我认为问题的症结在于您正在寻找有学生但没有指定教师的课程,该课程的学生genres和教师的genres.

我在您的模型上添加了student关系instructorLesson补充Foreign Keys您已经创建的模型,这使我更容易生成一些测试数据,如果没有别的:

class Lesson(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    instructor_id = db.Column(db.Integer, db.ForeignKey("instructor.id"))
    student_id = db.Column(db.Integer, db.ForeignKey("student.id"))
    student = db.relationship(Student, uselist=False, backref='lessons')
    instructor = db.relationship(Instructor, uselist=False, backref='lessons')

这是我生成的测试数据:

import random

db.drop_all()
db.create_all()
# create a set of tags
tags = [Tag(genre=g) for g in ('Jazz', 'Funk', 'Rock', 'Classical', 'Metal')]
# create 10 students with 2 tags randomly assigned
students = [Student(genres=random.sample(tags, 2)) for _ in range(10)]
# create 2 instructors with 2 tags randomly assigned
instructors = [Instructor(genres=random.sample(tags, 2)) for _ in range(2)]
# create a lesson for each student
lessons = [Lesson(student=s) for s in students]
db.session.add_all(tags + students + instructors + lessons)
db.session.commit()

然后对于查询,我查询Lessonjoin on Studentstudent_tag_association_table以便我可以找到与其中一个链接匹配的Lessonwithinstructor_id == None和 a Studentwith a :tag_idInstructor'stag_ids

# randomly select an instructor
instructor = random.choice(instructors)
possible_lessons = db.session.query(Lesson).\
    join(Student).\
    join(student_tag_association_table).\
    filter(and_(Lesson.instructor_id == None,
        or_(
            student_tag_association_table.c.tag_id.in_(
                g.id for g in instructor.genres
            )
        )
    )).all()

然后进行测试:

for lesson in possible_lessons:
    try:
        assert any([g in lesson.student.genres for g in instructor.genres])
    except AssertionError:
        print('uh oh')
    else:
        print('yes')

# yes
# yes
# yes
# yes
# yes

由于测试数据是随机的,因此您每次都会得到不同数量的yes输出。


推荐阅读