SQLAlchemy能否自动处理表间关系并添加新记录而无需手动检查主键唯一性?
我刚接触SQLAlchemy,读了一些基础文档。目前我在跟着Mike Driscoll的MediaLocker教程,并根据自己的需求进行修改和扩展。
我有三个表(贷款、人员、卡片)。卡片与贷款、人员与贷款之间都是一对多的关系,设计成这样:
from sqlalchemy import Table, Column, DateTime, Integer, ForeignKey, Unicode
from sqlalchemy.orm import backref, relation
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
engine = create_engine("sqlite:///cardsys.db", echo=True)
DeclarativeBase = declarative_base(engine)
metadata = DeclarativeBase.metadata
class Loan(DeclarativeBase):
"""
Loan model
"""
__tablename__ = "loans"
id = Column(Integer, primary_key=True)
card_id = Column(Unicode, ForeignKey("cards.id"))
person_id = Column(Unicode, ForeignKey("people.id"))
date_issued = Column(DateTime)
date_due = Column(DateTime)
date_returned = Column(DateTime)
issue_reason = Column(Unicode(50))
person = relation("Person", backref="loans", cascade_backrefs=False)
card = relation("Card", backref="loans", cascade_backrefs=False)
class Card(DeclarativeBase):
"""
Card model
"""
__tablename__ = "cards"
id = Column(Unicode(50), primary_key=True)
active = Column(Boolean)
class Person(DeclarativeBase):
"""
Person model
"""
__tablename__ = "people"
id = Column(Unicode(50), primary_key=True)
fname = Column(Unicode(50))
sname = Column(Unicode(50))
当我尝试创建一个新的贷款(在我的控制器中使用下面的方法)时,对于唯一的卡片和人员都能正常工作,但一旦我尝试为某个特定的人员或卡片添加第二个贷款,就会出现“非唯一”的错误。显然,它不是唯一的,这正是问题所在,但我以为SQLAlchemy会自动处理这些后台的事情,把正确的现有人员或卡片ID作为外键添加到新的贷款中,而不是试图创建新的人员和卡片记录。难道我需要自己查询数据库来检查主键的唯一性并手动处理吗?我觉得这应该是SQLAlchemy能够自动处理的事情吧?
def addLoan(session, data):
loan = Loan()
loan.date_due = data["loan"]["date_due"]
loan.date_issued = data["loan"]["date_issued"]
loan.issue_reason = data["loan"]["issue_reason"]
person = Person()
person.id = data["person"]["id"]
person.fname = data["person"]["fname"]
person.sname = data["person"]["sname"]
loan.person = person
card = Card()
card.id = data["card"]["id"]
loan.card = card
session.add(loan)
session.commit()
在MediaLocker的例子中,即使是重复的记录也会创建一个自增的主键(这不符合规范化规则)。我想要一个规范化的数据库(即使在一个小项目中,这也是学习的最佳实践),但在网上找不到任何可以研究的例子。
我该如何实现以上目标?
1 个回答
10
在你尝试添加一个主键重复的新对象之前,你需要先获取并分配已经存在的 Person
或 Card
对象到这个关系中。你可以通过对你的代码做一些小改动来实现这一点。
def addLoan(session, data):
loan = Loan()
loan.date_due = data["loan"]["date_due"]
loan.date_issued = data["loan"]["date_issued"]
loan.issue_reason = data["loan"]["issue_reason"]
person = session.query(Person).get(data["person"]["id"])
if not person:
person = Person()
person.id = data["person"]["id"]
person.fname = data["person"]["fname"]
person.sname = data["person"]["sname"]
loan.person = person
card = session(Card).query.get(data["card"]["id"])
if not card:
card = Card()
card.id = data["card"]["id"]
loan.card = card
session.add(loan)
session.commit()
如果你想把这个过程简化成一步,还有一些关于 get_or_create 函数的解决方案。
如果你是从零开始往一个新数据库里加载大量记录,而你的查询比 get
更复杂(因为会话对象本身应该能缓存 get
的查找),你可以通过将每个新创建的 Person 和 Card 对象按 ID 添加到一个临时字典中,从而避免查询数据库,虽然这样会占用一些内存。然后你可以直接从这个字典中获取已经存在的对象,而不是去数据库里查。