【问题标题】:How to define (or simulate) a collection containing different types in SQLAlchemy?如何在 SQLAlchemy 中定义(或模拟)包含不同类型的集合?
【发布时间】:2014-11-16 16:23:38
【问题描述】:

在使用 SQLAlchemy 进行项目时,我试图使用我认为的组合模式。我有一类“所有者”对象;我将一些功能封装在组件类中,并通过为所有者分配组件来赋予它们不同的功能。所有者和组件都有需要序列化的状态,所以它们都是 SQLAlchemy 对象。这是一个简单的例子(linked for readability):

class Employee(DeclarativeBase):
    __tablename__ = 'employee'
    id = Column(Integer, primary_key=True)
    name = Column(String)

    def __init__(self, name):
        self.name = name


class SalesRole(DeclarativeBase):
    __tablename__ = 'sales_role'
    id = Column(Integer, primary_key=True)
    employee_id = Column(Integer, ForeignKey('employee.id'))
    employee = relationship(
        'Employee',
        backref=backref('sales_role', uselist=False)
    )

    def __init__(self, employee):
        self.employee = employee
        self.total_sales = 0

    def __repr__(self):
        return "<SalesRole(employee='%s')>" % self.employee.name

    # Sales-specific data and behavior
    total_sales = Column(Float)


class CustomerSupportRole(DeclarativeBase):
    __tablename__ = 'support_role'
    id = Column(Integer, primary_key=True)
    employee_id = Column(Integer, ForeignKey('employee.id'))
    employee = relationship(
        'Employee',
        backref=backref('support_role', uselist=False)
    )

    def __init__(self, employee):
        self.employee = employee
        self.tickets_resolved = 0

    def __repr__(self):
        return "<CustomerSupportRole(employee='%s')>" % self.employee.name

    # Support-specific data and behavior
    tickets_resolved = Column(Integer)

我想做的是在所有者类上定义一个属性,该属性返回一个集合(任何类型),其中包含已分配给所有者的所有此类组件,即,

# Define an Employee object bob and assign it some roles
>>> bob.roles
[<SalesRole(employee='Bob')>, <CustomerSupportRole(employee='Bob')>]

我想在不硬编码对所有者类中可能存在的组件类型的任何引用的情况下完成此任务——我想定义新组件而不更改其他任何地方的代码。

我可以或多或少地通过使用 sqlalchemy-utils 中的generic_relationship 将所有者实例映射到其组件的中间表来完成此操作。不幸的是,generic_relationship 切断了 SQLAlchemy 对子对象的自动级联。

我在其他地方尝试过的另一种方法是使用 SQLAlchemy 的事件框架来侦听针对所有者类的关系映射('mapper_configured' 事件)。组件将定义一个从自己到所有者类的反向引用,并使用info 参数设置一个任意标志,将这种关系表示为引用我们希望通过此集合提供的组件之一。为捕获映射事件而注册的函数将测试此标志,并假设构建包含这些关系的集合,但我们永远无法弄清楚如何使其工作。

  • 这个集合是否是一个 SQLAlchemy 对象对我来说并不重要,我可以通过它实际写入数据库(即bob.roles.append(SalesmanRole())。这将非常酷,但只是一个用作只读可迭代视图的属性就足够了.
  • 命名属性 backrefs 是否存在于所有者类中并不重要(例如,bob.sales_role。如果存在也没关系,但我认为集合对我来说实际上更重要。
  • 就像我之前提到的,级联很重要(除非您想说服我不是!)。
  • 再次强调,我不必在任何地方硬编码组件类型列表,这一点很重要。无论我想在这个组件集合中显示的类与其他所有内容的区别是什么,都应该存在于组件本身的定义中。在我定义新组件时,我希望它可以轻松扩展。

有没有办法让它工作?或者我应该采取不同的方法——请随时告诉我这听起来像是一个 XY 问题。

【问题讨论】:

    标签: python sqlalchemy


    【解决方案1】:

    答案是我怀疑但没有足够努力寻找的东西:class inheritance。简单、优雅,完成我想要的一切。

    class Role(DeclarativeBase):
        __tablename__ = 'role'
        id = Column(Integer, primary_key=True)
        role_type = Column(String)
        employee_id = Column(Integer, ForeignKey('employee.id'))
        employee = relationship('Employee', backref='roles')
    
        __mapper_args__ = {
            'polymorphic_identity': 'employee',
            'polymorphic_on': role_type
        }
    
    
    class SalesRole(Role):
        __tablename__ = 'sales_role'
        id = Column(Integer, ForeignKey('role.id'), primary_key=True)
    
        __mapper_args__ = {
            'polymorphic_identity': 'sales_role'
        }
        # Sales-specific attributes, etc.
    
    
    class CustomerSupportRole(Role):
        __tablename__ = 'support_role'
        id = Column(Integer, ForeignKey('role.id'), primary_key=True)
    
        __mapper_args__ = {
            'polymorphic_identity': 'support_role'
        }
        # Support-specific attributes, etc.
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-12-31
      • 2011-11-21
      • 1970-01-01
      • 2013-05-27
      • 2013-06-23
      • 2021-12-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多