我正在努力让以下模型协同工作。首先,情景如下:
电子邮件地址是用户的ID,因此他们必须始终拥有一个,但是当他们更改时,我想跟踪他们过去使用过的其他内容。到目前为止,设置是有一个帮助表user_emails
,它在电子邮件和用户之间保持联系,我听说不应该将其设置为使用声明性SQLAlchemy方法的类(尽管我不这样做)知道为什么)。另外,我是否正确地认为我需要使用use_alter=True
因为users
表在插入之前不会知道外键email_id
?
models.py
看起来像这样:
"""models.py"""
user_emails = Table('user_emails', Base.metadata,
Column('user_id', Integer, ForeignKey('users.id'),
primary_key=True),
Column('email', String(50), ForeignKey('emails.address'),
primary_key=True))
class User(Base):
__tablename__ = 'users'
id = Column(Integer, Sequence('usr_id_seq', start=100, increment=1),
primary_key=True)
email_id = Column(String(50),
ForeignKey('emails.address', use_alter=True, name='fk_email_id'),
unique=True, nullable=False)
first = Column(String(25), unique=True, nullable=False)
last = Column(String(25), unique=True, nullable=False)
def __init__(self, first, last):
self.first = first
self.last = last
class Email(Base):
__tablename__ = 'emails'
address = Column(String(50), unique=True, primary_key=True)
user = relationship(User, secondary=user_emails, backref='emails')
added = Column(DateTime, nullable=False)
verified = Column(Boolean, nullable=False)
def __init__(self, address, added, verified=False):
self.address = address
self.added = added
self.verified = verified
在尝试提交数据库之前,一切似乎都没问题:
>>> user = models.User("first", "last")
>>> addy = models.Email("example@example.com", datetime.datetime.utcnow())
>>> addy
<Email 'example@example.com' (verified: False)>
>>> user
>>> <User None (active: True)>
>>>
>>> user.email_id = addy
>>> user
>>> <User <Email 'example@example.com' (verified: False)> (active: True)>
>>> Session.add_all([user, addy])
>>> Session.commit()
>>> ...
>>> sqlalchemy.exc.ProgrammingError: (ProgrammingError) can't adapt type 'Email' "INSERT INTO users (id, email_id, first, last, active) VALUES (nextval('usr_id_seq'), %(email_id)s, %(first)s, %(last)s, %(active)s) RETURNING users.id" {'last': 'last', 'email_id': <Email 'example@example.com' (verified: False)>, 'active': True, 'first': 'first'}
所以,我认为我做错了什么/傻瓜,但我是SQLAlchemy的新手,所以我不确定我需要做些什么来正确设置模型。
最后,假设我获得了正确的模型设置,是否可以添加关系,以便通过加载任意电子邮件对象,我将能够从Email对象中的属性访问拥有它的用户? / p>
谢谢!
答案 0 :(得分:5)
您已经有了一个非常好的解决方案,一个小修补程序将使您的代码正常工作。请在下面找到有关您的代码的快速反馈:
use_alter=True
吗?没有,你实际上并不需要那个。如果primary_key
表的Email
是在数据库级别计算的(与基于autoincrement
的主键一样),那么当您有两个{{1}表时,可能需要它} 对彼此。在你的情况下,你甚至没有,因为你有第三个表,所以对于任何关系组合,foreign_keys
将通过插入新的电子邮件,然后是用户,然后是关系来解决它。SA (sqlalchemy)
的实例分配给Email
,该实例应仅获取User.email_id
值。有两种方法可以解决它:
email
更改为user.email_id = addy
user.email_id = addy.address
,然后进行分配(请参阅下面的代码)。
就个人而言,我更喜欢选项-2。relationship
实际上是User.email_id
之一。这可能是设计使然,但只需添加User.emails
ForeignKey
即可
版本2的示例代码:
[users.id, users.email_id] to [user_emails.user_id, user_emails.email]
答案 1 :(得分:3)
我不熟悉Python / SQLAlchemy,但这是表示数据库中所需内容的一种方法:
您要么使用延迟约束(如果您的DBMS支持它们),请将USER.PRIMARY_EMAIL留空(如上面的模型所示)以打破数据修改周期。
或者,您可以这样做:
订购属于同一用户的电子邮件(请注意:{USER_ID,ORDER}上的备用密钥),并且在该订购之上的任何电子邮件都可以被视为“主要”。这种方法的好处在于它完全避免了循环引用。
答案 2 :(得分:1)
http://docs.sqlalchemy.org/en/latest/orm/tutorial.html中的示例与您想要的一致。我从不使用像user_emails这样的中间连接表,除非我需要多对多关系。用户到电子邮件应该是一对多的。
您是否需要跟踪旧电子邮件地址?将“过时的”布尔属性添加到您的电子邮件类,并对其进行过滤以显示当前或旧的电子邮件地址。