是否有可能拥有一对多和多对多关系的模型?

时间:2013-02-14 21:36:02

标签: python-2.7 sqlalchemy flask jinja2 flask-sqlalchemy

我有一个系统,我将拥有可由一个或多个教师讲授的产品(课程)。这些教师还可以创建产品,在这种情况下,他们应该可以通过产品作为其开发人员访问。但是,我还希望能够访问该产品可用的所有教师。

我不确定如何设置SQLAlchemy模型,以便我可以为Product.developer建立一对多的关系,但Product.instructors可以建立多对多的关系。

models.py

from app import db

instructors = db.Table('instructors',
    db.Column('instructor_id', db.Integer, db.ForeignKey('instructor.id')),
    db.Column('product_id', db.Integer, db.ForeignKey('product.id'))
)    


class Instructor(db.Model):
    id = db.Column(db.Integer, primary_key = True)
    name = db.Column(db.String(64))
    #courses qualified for etc

class Product(db.Model):
    id = db.Column(db.Integer, primary_key = True)
    courseName = db.Column(db.String(64))
    developer = db.relationship('Instructor', secondary=instructors,
        backref=db.backref('developed', lazy='dynamic'))
    instructors = db.relationship('Instructor', secondary=instructors,
        backref=db.backref('products', lazy='dynamic'))

    def __repr__(self):
        return '<Category> %r>' % (self.courseName)

views.py

from flask import render_template, flash, redirect
from app import app, models

@app.route('/product/<courseName>')
def show_product(courseName):
    product = models.Product.query.filter_by(courseName=courseName).first_or_404()
    return render_template('show-product.html', product=product)

template:show-product.html

<!-- extend base layout -->
{% extends "base.html" %}

{% block content %}
<br><b>Course Name:</b> {{product.courseName}}
<br><b>Curriculum developer:</b> 
    {% for developer in product.developer %}
        {{developer.name}}
    {% endfor %}
<br><b>Lead Instructor:</b> 
<br><b>Certified Instructors:</b> 
    {% for instructor in product.instructors %}
        {{instructor.name}}&nbsp;
    {% endfor %} 


{% endblock %}`enter code here`

这是我运行的代码,用于在数据库中放置一个值。

#!flask/bin/python
from app import db, models

instruct1 = models.Instructor(name="instructor1")
db.session.add(instruct1)
db.session.commit()

instruct2 = models.Instructor(name="instructor2")
db.session.add(instruct2)
db.session.commit()

dev = models.Instructor(name="developer")
db.session.add(dev)
db.session.commit()

prod = models.Product(courseName = "test")

prod.instructors = [instruct1]
prod.instructors.append(instruct2)

prod.developer = [dev]

print "Instructors:"
for instructor in prod.instructors:
    print instructor.name

print "Developer"
for i in prod.developer:
    print i.name

db.session.add(prod)
db.session.commit()

这是输出:

Instructors:
instructor1
instructor2
Developer
developer

但是当我查看网页时,我看到了:

Course Name: test 
Curriculum developer: instructor1 instructor2 developer 
Lead Instructor: 
Certified Instructors: instructor1  instructor2  developer 

更新:谢谢doobeh!我更新了我的代码http://bpaste.net/show/o8PtsY3Kvu8bxKnDEo24/但是当我运行python代码时,我得到错误“在关系'Product.instructors'上创建backref'产品'时出错:该名称的属性存在于mapper'Mapper | Instructor | instructor'上

1 个答案:

答案 0 :(得分:3)

如果您只是正确地创建开发者关系,听起来您的问题就会消失。目前,您已经复制了您正在使用的多对多关系。因此,您的开发人员被添加到与教师相同的堆栈中 - 您没有立即看到它的原因是因为您没有将其提交到数据库,直到您再次打印出列表。一旦将这些列表提交到数据库并重新查询,您将获得您在模板中看到的倍数。

我认为如果您将Product模型更改为:

class Product(db.Model):
    id = db.Column(db.Integer, primary_key = True)
    courseName = db.Column(db.String(64))

    developer_id = db.Column(db.Integer, db.ForeignKey='instructor.id'))
    developer = db.relationship('Instructor', backref='develops')

    instructors = db.relationship('Instructor', secondary=instructors,
        backref=db.backref('products', lazy='dynamic'))

    def __repr__(self):
        return '<Category> %r>' % (self.courseName)

它可以按预期工作(未经测试的代码!)。由于您现在只在每个Product上存储一个开发人员,因此您不需要迭代这些值,也不需要在添加它们时传入列表/追加。

更清楚地说明为什么你不能有多个多对多的连接 使用相同的字段,让我们分解SQL级别上发生的事情。

您的M2M联接表instructors有两个字段instructor_idproduct_id。然后创建三个教师:

id:1    instructor1
id:2    instructor2
id:3    developer

然后你创建一个产品:

id:1    product

如果我将前两位教师添加到该产品,instructors 链接表将存储如下信息:

product_id      instructor_id
1               1
1               2

然后将开发人员添加到同一个表中:

product_id      instructor_id
1               1
1               2
1               3

所以,当你说“给我这个产品的所有教师”时,SQLAlchemy就是 要问模特,“嘿,我在这里有一个展示产品的关系 与instructors表中的教师相关联,所以请给我所有的教师 它的产品编号为1“,当然你会得到三个结果,而不仅仅是你期望的那两个,当你要求开发人员使用相同的链接时,你得到相同的三个。

如果你想创建两个多对多的连接,你只需要创建一个名为developers或类似的第二个链接表,它只包含与该关系相关的信息。但是,在这种情况下,因为您只想要一个链接到产品的开发人员,您只需使用简单的ForeignKey来映射该关系。