问题 SQLAlchemy:避免在声明式样式类定义中重复


我正在使用SQLAlchemy,我的对象模型中的许多类具有相同的两个属性:id和(整数和主键),以及name(字符串)。我试图避免在每个类中声明它们如此:

class C1(declarative_base()):
    id = Column(Integer, primary_key = True)
    name = Column(String)
    #...

class C2(declarative_base()):
    id = Column(Integer, primary_key = True)
    name = Column(String)
    #...

有什么好办法呢?我尝试使用元类但它还没有用。


8289
2017-09-02 11:49


起源



答案:


你可以把你的共同属性分解成一个 mixin类,并且多次继承它 declarative_base()

from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base

class IdNameMixin(object):
    id = Column(Integer, primary_key=True)
    name = Column(String)

class C1(declarative_base(), IdNameMixin):
    __tablename__ = 'C1'

class C2(declarative_base(), IdNameMixin):
    __tablename__ = 'C2'

print C1.__dict__['id'] is C2.__dict__['id']
print C1.__dict__['name'] is C2.__dict__['name']

编辑:你可能会认为这会导致 C1 和 C2 分享同样的 Column 对象,但如中所述 SQLAlchemy文档,列对象是 复制 当源自mixin类时。我已更新代码示例以演示此行为。


9
2017-09-02 18:56



不幸的是,这不会起作用,因为id属性将在IdNameMixin的所有子类之间共享。在SQLAlchemy中,每个类必须有自己的id(一个新创建的Column类对象)。 - max
通常你是对的,但请看我更新的答案。 - dhaffey
啊很酷!!谢谢。现在,如果我能做些什么的话 __tablename__,这绝对必须是独一无二的:)但我喜欢你的mixin类比我的元类修改更好。 - max


答案:


你可以把你的共同属性分解成一个 mixin类,并且多次继承它 declarative_base()

from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base

class IdNameMixin(object):
    id = Column(Integer, primary_key=True)
    name = Column(String)

class C1(declarative_base(), IdNameMixin):
    __tablename__ = 'C1'

class C2(declarative_base(), IdNameMixin):
    __tablename__ = 'C2'

print C1.__dict__['id'] is C2.__dict__['id']
print C1.__dict__['name'] is C2.__dict__['name']

编辑:你可能会认为这会导致 C1 和 C2 分享同样的 Column 对象,但如中所述 SQLAlchemy文档,列对象是 复制 当源自mixin类时。我已更新代码示例以演示此行为。


9
2017-09-02 18:56



不幸的是,这不会起作用,因为id属性将在IdNameMixin的所有子类之间共享。在SQLAlchemy中,每个类必须有自己的id(一个新创建的Column类对象)。 - max
通常你是对的,但请看我更新的答案。 - dhaffey
啊很酷!!谢谢。现在,如果我能做些什么的话 __tablename__,这绝对必须是独一无二的:)但我喜欢你的mixin类比我的元类修改更好。 - max


你还可以使用Column的复制方法吗?这样,可以独立于表定义字段,而重用的字段只是field.copy() - ed。

id = Column(Integer, primary_key = True)
name = Column(String)

class C1(declarative_base()):
    id = id.copy()
    name = name.copy()
    #...

class C2(declarative_base()):
    id = id.copy()
    name = name.copy()
    #...

2
2018-05-24 20:42



有人知道如何改变不同表类中这些列的属性吗?也就是说,如果id在C1中不是主要的,但在C2中是主要的,那么如何在不重复我们避免的情况下进行区分? - jkmacc


我想我得上班了。

我创建了一个派生自DeclarativeMeta的元类,并使其成为C1和C2的元类。在那个新的元类中,我只是说

def __new__(mcs, name, base, attr):
  attr['__tablename__'] = name.lower()
  attr['id'] = Column(Integer, primary_key = True)
  attr['name'] = Column(String)
  return super().__new__(mcs, name, base, attr)

它似乎工作正常。


1
2017-09-03 09:39