我正在学习SQLAlchemy,今天我已经阅读了很多关于人际关系的文章,包括SO上的一些帖子。然而,我发现的所有例子都没有回答这个问题,尽管我认为这是处理人际关系时首先要回答的问题之一。
我有网页表单数据,其中很多都是重复的 - 比如访问者浏览器用户代理字符串,提供表单的域和服务的域。我想保留这些数据,但显然将代理程序存储在自己的表中,然后在表单数据表中保留ID更有意义。所以我有一个像这样的Agents类:
class Agent(Base):
__tablename__ = 'agents'
__table_args__ = {'mysql_engine': 'InnoDB'}
ID = Column(Integer, autoincrement = True, primary_key = True)
UserAgent = Column(VARCHAR(256), nullable = False, unique = True)
#UniqueConstraint('UserAgent')
def __repr__(self):
return "<Agent(UserAgent='%s')>" % (self.UserAgent)
然后我有一个表单数据类:
class Lead(Base):
__tablename__ = 'leads'
__table_args__ = {'mysql_engine': 'InnoDB'}
ID = Column(String(32), primary_key = True)
AgentID = Column(Integer, ForeignKey('agents.ID'), nullable = False)
.... other non relational fields ....
IsPartial = Column(Boolean, nullable = False)
Agent = relationship('Agent', backref = backref('leads', uselist = True))
def __repr__(self):
return "<Lead(ID='%s')>" % (self.ID)
此时,SQLAlchemy会创建我要求的所有表格,并且我可以创建测试主管实例:
testLead = Lead(ID='....', ...)
然后创建一个测试代理实例:
testAgent = Agent(UserAgent='...', leads=testLead)
testLead实例化得很好。但是,代理实例化失败,并显示:
TypeError: Incompatible collection type: Lead is not list-like
使用testLead.Agent = [...]会导致:
AttributeError: 'list' object has no attribute '_sa_instance_state'
理想情况下,我希望能够使用代理字符串实例化Lead对象。然后,当我使用session.add和session.commit时,ORM会将代理字符串添加到代理程序表中(如果缺少)。同样,当我实例化Lead类时,我希望能够使用:
lead = Lead(ID='...')
然后,如果我使用:
lead.Agent
代理字符串应该显示出来。如果我已经阅读了正确的文档,则需要添加一些延迟加载设置。
有办法做到这一点吗?如果没有,我该如何解决上述错误?
由于
答案 0 :(得分:0)
您似乎混淆了1-many
关系的两个方面:您的代码与测试数据的工作原理就像一个Lead
有很多Agents
,而relationship
定义恰恰相反。假设模型是正确的,下面应该有效:
testLead = Lead(ID='....', ...)
testAgent = Agent(UserAgent='...', leads=[testLead])
或:
testAgent = Agent(UserAgent='...')
testLead = Lead(ID='....', ..., Agent=testAgent)
至于你问题的第二部分,你可以用简单的python实现它:
class Lead(Base):
# ...
@property
def UserAgent(self):
return self.Agent and self.Agent.UserAgent
@UserAgent.setter
def UserAgent(self, value):
# @note: you might first want to search for agent with this UserAgent value,
# and create a new one only if one does not exist
# agent_obj = object_session(self).query(Agent).filter(Agent.UserAgent == value).one()
agent_obj = Agent(UserAgent=value)
self.Agent = agent_obj
@UserAgent.deleter
def UserAgent(self):
self.Agent = None
或者您可以将Association Proxy
用于此目的:
def _agent_find_or_create(user_agent):
agent = session.query(Agent).filter(Agent.UserAgent==user_agent).first()
return agent or Agent(UserAgent=user_agent)
class Lead(Base):
# ...
Agent = relationship('Agent', backref = backref('leads', uselist = True))
UserAgent = association_proxy('Agent', 'UserAgent',
creator=_agent_find_or_create)