sqlalchemy将mixin列移动到结尾

Gar*_*rwe 19 python sqlalchemy

我有一个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...
Run Code Online (Sandbox Code Playgroud)

现在notes列是model/db中的第一列.我知道这不会影响我的应用程序的功能,但是在ID之前看到笔记会让我感到恼火等等.有什么方法可以将它移到最后?

Ste*_*ven 23

找到更清洁的解决方案

sqlalchemy.ext.declarative.declared_attr在sqlalchemy 0.6.5中使用装饰器(sqlalchemy sqlalchemy.util.classproperty<= 0.6.4)

class NotesMixin(object):
    @declared_attr
    def notes(cls):
        return sa.Column(sa.String(4000) , nullable=False, default='')
Run Code Online (Sandbox Code Playgroud)

根据文档,这是"对于具有外键的列,以及需要目标显式上下文的各种映射器级构造".虽然严格来说这不是这里的情况,但它是通过在构造子类时调用方法(并创建列)来实现的,因此无需进行复制.这意味着mixin专栏将在最后出现.可能是一个比黑客更好的解决方案_creation_order......

  • 虽然这需要更少的代码,但是它使用"黑客"方式感觉有点迂回,也就是说,外键约束的非意图副作用以确保排序.我更喜欢`_creation_order`解决方案,因为这是**明确**设置列创建顺序的预期方式,无论是否为FK. (3认同)

Ste*_*ven 8

答案很简单:只需自己创建数据库表,而不是使用sqlalchemy metadata.create_all().

如果您没有找到可接受的,我担心这本身需要(小)更改sqlalchemy.ext.declarative,或者您必须创建自己的元类并declarative_base()使用metaclass关键字参数传递给它.然后将使用该类而不是默认值DeclarativeMeta.

说明:sqlalchemy使用列属性的创建顺序,它存储在"private"属性中._creation_order(在调用Column()时生成).声明性扩展通过从mixin类创建列对象的副本并将其添加到类中来混合列.在._creation_order这个副本中设置为相同的值mixin类的原始属性.由于mixin类当然是首先创建的,因此它的列属性将具有比子类更低的创建顺序.

因此,为了使您的请求成为可能,应在制作副本时分配新的创建顺序,而不是采用原始顺序.你可以尝试根据这个解释制作自己的元类,并使用它.但您也可以尝试询问sqlalchemy开发人员.也许他们愿意接受这个作为错误/功能请求?至少,它看起来像是一个小的(一线)改变,除了你要求的改变之外没有任何影响(这可能更好).

  • 谢谢.使用该信息,我做了:`NotesMixin.notes._creation_order = 9999` (9认同)

aca*_*ala 6

我迟到了这个问题,但作为参考,如果您使用 SA 2.0,只需将 sort_order 传递给mapped_column,例如:

class NotesMixin:
    note = mapped_column(String(255), sort_order=999)
Run Code Online (Sandbox Code Playgroud)

https://docs.sqlalchemy.org/en/20/changelog/whatsnew_20.html#orm-declarative-applies-column-orders- Differently-control-behavior-using-sort-order


mdh*_*mdh 5

还可以在 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)
Run Code Online (Sandbox Code Playgroud)

然后这可以与metadata.create_all().