sqlalchemy将mixin列移动到结尾

时间:2010-10-13 13:03:26

标签: 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...

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

5 个答案:

答案 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)