代码之家  ›  专栏  ›  技术社区  ›  Plasma

SQLAlchemy关系:查找具有一个或多个共同属性的实例

  •  1
  • Plasma  · 技术社区  · 7 年前

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

    我有两种类型的用户, Student Instructor . 两者都可以有一个或多个 Tag S,基本上是一种流派(摇滚、滑稽等)。关联 学生 教练 用一个 标签 我每个人都有一个协会表。

    我有最后一个模型, Lesson 有身份证的 学生 教练 . 在创建一个 ,仅设置这些ID中的一个,具体取决于 学生 教练 就是这样。如果A 学生 创建一个 ,我需要找到所有 教练 有一个或多个 标签 S与 学生 . 我该如何实现这一目标?我对数据库关系的了解有限,因此任何帮助都是非常感谢的!

    下面是模型的代码:

    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")),
    )
    

    我知道这在多个层面上是错误的,但是说我有一个身份证 教练 ,然后我要查找这样的查询以查找经验教训:

    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"))
    
    1 回复  |  直到 7 年前
        1
  •  1
  •   SuperShoot npburns224    7 年前

    我的回答受到 this 一个。

    如果我的解释正确,我认为问题的关键在于你要找的课程有一个学生,但没有指派指导老师,在这个课程的学生之间有一个交叉点。 genres 还有教练的 体裁 .

    我添加了 student instructor 你的关系 Lesson 补充模型 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()
    

    然后,为了查询,我查询 加入 Student student_tag_association_table 以便我能找到 具有 instructor_id == None 和A 学生 用一个 tag_id 它符合 Instructor's 链接的 tag_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 每次输出。