繁体   English   中英

在SQLAlchemy中为ORM创建一对多关系

[英]Creating One-to-Many with Relationship for the ORM in SQLAlchemy

我正在学习SQLAlchemy,并且今天我已经阅读了很多有关关系的文章,包括关于SO的许多文章。 但是,尽管我认为这将是处理关系时最先回答的问题之一,但我发现的所有示例都无法完全回答这个问题。

我有网页表单数据,并且其中很多都是重复性的-就像访问者的浏览器用户代理字符串,用于提供表单的域和用于提供表单的域。 我想保留这些数据,但是显然将诸如Agent之类的东西存储在自己的表中,然后在表单数据表中保留一个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创建了我要求的所有表,并且可以创建测试Lead实例:

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'

理想情况下,我希望能够使用Agent字符串实例化Lead对象。 然后,当我使用session.add和session.commit时,如果缺少ORM,则将Agent字符串添加到agent表中。 同样,当我实例化Lead类时,我希望能够做到这一点:

lead = Lead(ID='...')

然后,如果我使用:

lead.Agent

代理字符串应显示。 如果我正确阅读了文档,则需要添加一些“延迟加载”设置。

有没有办法做到这一点? 如果没有,我该如何解决以上错误?

谢谢

您似乎混合了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)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM