在sqlalchemy中插入具有一对多关系的新记录
Posted
技术标签:
【中文标题】在sqlalchemy中插入具有一对多关系的新记录【英文标题】:Inserting new records with one-to-many relationship in sqlalchemy 【发布时间】:2013-05-02 06:00:32 【问题描述】:我正在关注 declaring models 上关于一对多关系的 flask-sqlalchemy 教程。示例代码如下:
class Person(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50))
addresses = db.relationship('Address', backref='person',
lazy='dynamic')
class Address(db.Model):
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(50))
person_id = db.Column(db.Integer, db.ForeignKey('person.id'))
现在我想知道如何使用这种模型将新记录插入数据库。我假设我需要一个构造函数 init,但我很难理解它应该如何实现和使用。我这里的主要问题是 Person 依赖于 Address 并且 Address 对 Person 有 ForeignKey,所以它应该提前知道 Person。
请帮助我了解它应该如何执行。
提前谢谢你。
【问题讨论】:
【参考方案1】:您不需要编写构造函数,您可以将Person
实例上的addresses
属性视为一个列表:
a = Address(email='foo@bar.com')
p = Person(name='foo')
p.addresses.append(a)
或者您可以将地址列表传递给Person
构造函数
a = Address(email='foo@bar.com')
p = Person(name='foo', addresses=[a])
在任何一种情况下,您都可以像这样访问Person
实例上的地址:
db.session.add(p)
db.session.add(a)
db.session.commit()
print(p.addresses.count()) # 1
print(p.addresses[0]) # <Address object at 0x10c098ed0>
print(p.addresses.filter_by(email='foo@bar.com').count()) # 1
【讨论】:
太棒了!现在,如果数据库中已经存在一个人,并且我们已经为他们添加了一个新地址 (p.addresses.append(a)
),那么我们是否需要在 db.session.commit
之前添加db.session.add
人、地址、两者,或者两者都不是叮?
您必须从数据库中查询现有数据,例如p = db.session.query(Person).filter(Person.name == 'Name of existing Person').first()
,然后执行相同的p.addresses.append(a)
。您不必将它添加到会话中,因为它直接来自会话。但在所有更改完成后我仍然会db.session.commit()
这会创建一个新的Address
记录。如果我想用email='foo@bar.com'
分配Address
记录的id
(主键)怎么办。我想将已经创建的地址分配给人员,但我不想先查询地址。【参考方案2】:
研究这个模型时最重要的是要理解这个模型具有一对多关系的事实,即一个人有多个地址,在我们的例子中,我们会将这些地址存储在一个列表中。
因此,带有 init 的 Person 类将如下所示。
class Person(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50))
addresses = db.relationship('Address', backref='person',
lazy='dynamic')
def __init__(self,id,name,addresses = tuple()):
self.id = id
self.name = name
self.addresses = addresses
所以这个 Person 类将需要一个 id、一个名称和一个包含 Address 类型对象的列表。我将默认值保留为空列表。
希望对您有所帮助。 :)
【讨论】:
可变的默认参数是common gotcha,您可能需要更改示例以反映这一点:) @DazWorrall 我编辑了答案以改变它。【参考方案3】:我在这里和其他地方收集了信息,并找到了 3 种方法。在此模型示例中(与问题相同):
class Person(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50))
addresses = db.relationship('Address', backref='person',
lazy='dynamic')
class Address(db.Model):
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(50))
person_id = db.Column(db.Integer, db.ForeignKey('person.id'))
1.
a = Address(email='foo@bar.com')
p = Person(name='foo', addresses=[a])
2.
p = Person(name='foo')
a = Address(email='foo@bar.com', person_id=p.id)
3.
a = Address(email='foo@bar.com')
p = Person(name='foo')
p.addresses.append(a)
【讨论】:
【参考方案4】:除了所有之前的答案,与uselist=False
建立一对一关系,例如:
class Person(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50))
address = db.relationship('Address', backref='person',
lazy=True, uselist=False)
class Address(db.Model):
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(50))
person_id = db.Column(db.Integer, db.ForeignKey('person.id'))
只有下一种方法有助于插入记录:
p = Person(name='foo')
a = Address(email='foo@bar.com', person=p) # Adding created person directly to Address's backref
【讨论】:
以上是关于在sqlalchemy中插入具有一对多关系的新记录的主要内容,如果未能解决你的问题,请参考以下文章