【问题标题】:SQLAlchemy mixins, how to inherit from them and variable relationshipsSQLAlchemy mixins,如何从它们继承和变量关系
【发布时间】:2021-05-28 12:31:41
【问题描述】:

我已经开始使用 SQLAlchemy Mixins 并且我已经阅读了文档。它很有用,但我仍然发现我对我希望完成的任务有疑问。我应该说我对 SQLAlchemy 不是很有经验。

所以我想要一种将列和关系从 Mixin 继承到子类的方法。目前我有前者工作,但没有后者。

这些关系在文档中有所解释,如下所示:

@declarative_mixin
class RefTargetMixin:
   @declared_attr
   def target_id(cls):
       return Column('target_id', ForeignKey('target.id'))

   @declared_attr
   def target(cls):
       return relationship("Target")

这似乎正是我正在寻找的功能,除了一点:除非我弄错了,'target' 作为字符串在这里是固定的并且不是可变的。如果我要提出一个通用版本,我希望能够有一个子类指定表名和与之建立关系的类。

不幸的是,这就是我卡住的地方。我只是不知道该怎么做。我做了一些相当原始的尝试,例如:

@declared_attr
def target_id(cls, targetTable):
    return Column('target_id', ForeignKey(f'{targetTable}.id'))

但是我只是不知道如何“调用”这个方法。我尝试从子类 init 中调用“super”,但这一直告诉我:

TypeError: target_id() missing 1 required positional argument: 'targetTable'

我认为这行不通,但我只是不确定正确的方法是什么,我想我至少应该在问这里之前试一试。

【问题讨论】:

    标签: python sqlalchemy mixins


    【解决方案1】:

    我确实找到了答案,我想我会回来展示我是如何做到的。

    经过一番搜索,我发现了this question,它成为了解决方案的基础。这并不准确,但它表明,如果你知道如何去做,这些 @declared_attr 东西可以是可变的。

    所以让我们从 target_id 属性开始,这就是我在这里所拥有的。还有更多,我会讲到,但我会从那个开始。顺便说一句,这都在“父”混入中。

    基本模式是您需要一个 setter、一个 getter 和一个变量本身,如下所示:

    _targetTableName = 'Not defined target table name'
    
    def get_targetTableName(self):
        return self._targetTableName
    
    def set_targetTableName(self, value):
        self._targetTableName = value
    
    @declared_attr
    def targetTableName(cls):
        return synonym('_targetTableName', descriptor=property(cls.get_targetTableName, cls.set_targetTableName))
    

    那么我在同一个 mixin 中也需要这个:

    @declared_attr
    def target_id(cls):
        return Column('target_id', ForeignKey(f'{cls.targetTableName}.ID'))
    

    如您所见,这允许我更改 ForeignKey 参数中的内容 - 毕竟它只是一个字符串。

    作为一个相对新手,我要问的下一个问题是如何更改孩子的这个“targetTableName”属性?好吧,就像这样:

    class TestChild(TestParent, med.DeclarativeBase):
       __tablename__='testChild01'
       targetTableName = 'SchoolTypes'
    

    现在,当它全部自动创建后,您的子类将有一个外键,在本例中为 SchoolTypes.ID。

    但是等等,如果目标中的列不称为“ID”怎么办?好吧,您也可以以相同的方式将其设为变量。我选择不这样做是因为我所有的表都有一个“ID”列(我不知道这是好还是坏的做法)。

    但是等等,还有更多!

    你看,实际上 target_id 只是等式的一部分,我希望,如果我能解决这个问题,我就能得到奖品的另一部分:链接对象。也就是说,SQLAlchemy 的“关系”类型为您提供由外键链接的对象。

    正常创建时是这样的:

    targetType = relationship('SchoolType', foreign_keys='Search_Grade_SchoolType.targetID')
    

    那么如何使这个通用呢?好吧,用与上面相同的过程来使设置的东西可变。

    让我与您分享整个代码,使其更易于查看。但请注意,我所做的只是为目标表名、目标类名和当前类名设置了一个变量,所有这些都是您需要的:

    from sqlalchemy import Column, Integer, Boolean, String, DateTime, ForeignKey
    from sqlalchemy.orm import backref, relationship
    
    from sqlalchemy.orm import declarative_mixin, synonym
    from sqlalchemy.orm import declared_attr
    
    @declarative_mixin
    class TestParent:
        id = Column('ID', Integer, primary_key=True, autoincrement=True)
    
        _targetTableName = 'Not defined target table name'
        _targetClassName = 'Not defined target class name'
        _myClassName = 'Not defined class name'
    
        def get_myClassName(self):
            return self._myClassName
    
        def set_myClassName(self, value):
            self._myClassName = value
    
        @declared_attr
        def myClassName(cls):
            return synonym('_myClassName', descriptor=property(cls.get_myClassName, cls.set_myClassName))
    
        def get_targetTableName(self):
            return self._targetTableName
    
        def set_targetTableName(self, value):
            self._targetTableName = value
    
        @declared_attr
        def targetTableName(cls):
            return synonym('_targetTableName', descriptor=property(cls.get_targetTableName, cls.set_targetTableName))
    
        def get_targetClassName(self):
            return self._targetClassName
    
        def set_targetClassName(self, value):
            self._targetClassName = value
    
        @declared_attr
        def target_id(cls):
            return Column('target_id', ForeignKey(f'{cls.targetTableName}.ID'))
    
        @declared_attr
        def targetClassName(cls):
            return synonym('_targetClassName', descriptor=property(cls.get_targetClassName, cls.set_targetClassName))
    
        @declared_attr
        def target_object(cls):
            return relationship(f'{cls.targetClassName}', foreign_keys=f'{cls.myClassName}.target_id')
    
    
    class TestChild(TestParent, med.DeclarativeBase):
        __tablename__='testChild01'
        name = Column('NAME', String(50), unique=False)
        targetTableName = 'SchoolTypes'
        targetClassName = 'SchoolType'
        myClassName = 'TestChild'
    
        def __init__(self, name, targetID):
            self.name = name
            self.target_id = targetID
    
        def TestMethod(self):
            print(f'Print me!')
    

    那么这一切有什么作用呢?它允许子类(TestChild)通过在子类中输入一些变量来设置target_object和target_id等。父级可以拥有任意数量的列,子级也可以拥有它们,但它们也可以拥有自己的列(在这种情况下,父级的“ID”,子级的“名称”。

    我为什么要做这一切?它解决了什么问题?好吧,我知道我会有很多实际上是相同的表。相同的结构,除了它们彼此具有不同表的外键。所以我想要某种继承情况来允许代码重用,也让我变得懒惰。

    【讨论】:

      猜你喜欢
      • 2012-10-06
      • 1970-01-01
      • 2017-04-05
      • 1970-01-01
      • 2013-05-22
      • 1970-01-01
      • 2020-02-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多