我有一个sqlalchemy模型,其中所有表格/对象都有一个注释字段。因此,为了尝试遵循DRY原则,我将该字段移至mixin类。
class NotesMixin(object):
notes = sa.Column(sa.String(4000) , nullable=False, default='')
class Service(Base, NotesMixin):
__tablename__ = "service"
service_id = sa.Column(sa.Integer, primary_key=True)
name = sa.Column(sa.String(255), nullable=False, index=True, unique=True)
class Datacenter(Base, NotesMixin):
__tablename__ = "datacenter"
datacenter_id = sa.Column(sa.Integer, primary_key=True)
name = sa.Column(sa.String(255), nullable=False, index=True, unique=True)
class Network(Base, NotesMixin, StatusMixin):
__tablename__ = "network"
network_id = sa.Column(sa.Integer, primary_key=True)
etc...
现在notes列是model / db中的第一列。我知道这不会影响我的应用程序的功能,但是在id之前看到笔记会让我感到恼火等等。有什么方法可以将它移到最后?
答案 0 :(得分:20)
找到一个更清洁的解决方案:
在sqlalchemy 0.6.5中使用sqlalchemy.ext.declarative.declared_attr
装饰器(sqlalchemy中的sqlalchemy.util.classproperty
< = 0.6.4)
class NotesMixin(object):
@declared_attr
def notes(cls):
return sa.Column(sa.String(4000) , nullable=False, default='')
根据文档,这是“对于具有外键的列,以及需要目标显式上下文的各种映射器级别构造”。虽然严格来说这不是这里的情况,但它是通过在构造子类时调用方法(并创建列)来实现的,因此无需进行复制。这意味着mixin专栏将在最后出现。可能是比黑客攻击更好的解决方案_creation_order
...
答案 1 :(得分:5)
答案很简单:只需自己创建数据库表,而不是使用metadata.create_all()
进行sqlalchemy。
如果你没有找到可接受的,我担心这需要sqlalchemy.ext.declarative
本身的(小)更改,或者你必须创建自己的元类并将其传递给{{1}使用declarative_base()
关键字参数。然后将使用该类而不是默认的metaclass
。
说明:sqlalchemy使用列属性的创建顺序,它存储在“private”属性DeclarativeMeta
中(在调用Column()时生成)。声明性扩展通过从mixin类创建列对象的副本并将其添加到类中来混合列。此副本的._creation_order
设置为与mixin类的原始属性相同的值。由于mixin类当然是首先创建的,因此它的列属性将具有比子类更低的创建顺序。
因此,为了使您的请求成为可能,应在制作副本时分配新创建顺序,而不是采用原始顺序。你可以尝试根据这个解释制作自己的元类,然后使用它。但您也可以尝试询问sqlalchemy开发人员。也许他们愿意接受这个作为错误/功能请求?至少,似乎就像一个次要(一行)的变化,除了你要求的改变之外没有任何影响(这可能更好)。
答案 2 :(得分:2)
也可以在CREATE TABLE编译时改变列的顺序(这里以postgresql
方言为例):
from sqlalchemy.schema import CreateTable
from sqlalchemy.ext.compiler import compiles
@compiles(CreateTable, 'postgresql')
def _compile_create_table(element, compiler, **kwargs):
element.columns = element.columns[::-1] # reverse order of columns
return compiler.visit_create_table(element)
这适用于metadata.create_all()
。
答案 3 :(得分:0)
我发现我可以使用:
在Mixin上设置列顺序(到最后位置)@declared_attr
def notes(cls):
col = sa.Column(sa.String(4000) , nullable=False, default='')
# get highest column order of all Column objects of this class.
last_position = max([value._creation_order
for key, value in vars(cls).items()
if isinstance(value, Column)])
col._creation_order = last_position + 0.5
return col
class Service(Base, NotesMixin):
__tablename__ = "service"
service_id = sa.Column(sa.Integer, primary_key=True)
name = sa.Column(sa.String(255), nullable=False, index=True, unique=True)
根据另一列的位置设置列顺序(类似于
alter table `some_table` modify `some_colum` `some_type` after `some_other_column;
见https://stackoverflow.com/a/3822219/488331)
您可以使用:
@declared_attr
def notes(cls):
col = sa.Column(sa.String(4000) , nullable=False, default='')
col._creation_order = cls.some_other_column._creation_order + 0.5
return col
注意:如果您使用+ 1
,则最终会返回2列。我真的不明白为什么你甚至可以使用小数。
要根据第一列的位置设置列顺序(使其始终为第4列),您可以执行以下操作:
@declared_attr
def notes(cls):
col = sa.Column(sa.String(4000) , nullable=False, default='')
# get lowest column order of all Column objects of this class.
start_position = min([value._creation_order
for key, value in vars(cls).items()
if isinstance(value, Column)])
col._creation_order = start_position + 3.5
return col
答案 4 :(得分:0)
我知道已经有一段时间了,但我找到了一个非常简单的解决方案:
class PriorityColumn(Column):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._creation_order = 1
这是 Column 的直接替代品,如果您正在使用 Mixins 并且您希望您的派生类的属性排在第一位。
class A:
a = Column(Integer)
b = Column(String)
class B(A, Base):
c = PriorityColumn(Integer)
d = PriorityColumn(Float)
# Your table will look like this:
# B(c, d, a, b)