python sqlalchemy将mixin列移至末尾

ev7lccsx  于 2023-04-19  发布在  Python
关注(0)|答案(6)|浏览(161)

我有一个sqlalchemy模型,其中大多数表/对象都有一个notes字段。因此,为了尝试遵循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之前看到notes等会让我有点恼火。有什么办法把它移到最后吗?

n9vozmp4

n9vozmp41#

找到了更清洁的解决方案:
在sqlalchemy 0.6.5中使用sqlalchemy.ext.declarative.declared_attr装饰器(sqlalchemy〈= 0.6.4中的sqlalchemy.util.classproperty

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

根据文档,这是“对于具有外键的列,以及对于需要明确的目标上下文的各种Map器级构造”。(以及创建列)当构造子类时,从而避免了复制的需要。这意味着mixin列将在最后出现。可能是比破解_creation_order更好的解决方案...

cgyqldqp

cgyqldqp2#

答案很简单:只需自己创建数据库表,而不是让sqlalchemy使用metadata.create_all()来完成。
如果你觉得这不能接受的话,恐怕这需要对sqlalchemy.ext.declarative本身做一个(小的)修改,或者你必须创建自己的元类,并使用metaclass关键字参数将其传递给declarative_base()。然后这个类将代替默认的DeclarativeMeta
说明:sqlalchemy使用列属性的创建顺序,它将列属性存储在“private”属性._creation_order中声明式扩展通过从mixin类创建column对象的副本来执行mixin列。并将其添加到类中。此副本的._creation_order设置为与mixin类的原始属性相同的值。由于mixin类当然是首先创建的,因此它的列属性的创建顺序将低于子类。
因此,为了使您的请求成为可能,在创建副本时应该分配一个 * 新的 * 创建顺序,而不是使用原始的。您可以尝试基于此解释创建自己的元类,并使用它。但您也可以尝试询问sqlalchemy开发人员。也许他们愿意接受这作为一个bug/功能请求?至少,它 * 看起来 * 像是一个小问题(一行)改变,那不会有任何效果,除了你要求的改变(可以说这也是更好的)。

gajydyqb

gajydyqb3#

还可以在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()

35g0bw71

35g0bw714#

关于这个问题我已经晚了,但作为参考,如果你使用SA 2.0,只需将sort_order传递给mapped_column,例如:

class NotesMixin:
    note = mapped_column(String(255), sort_order=999)

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

pw9qyyiw

pw9qyyiw5#

我知道已经有一段时间了,但我发现了一个非常简单的解决方案:

class PriorityColumn(Column):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._creation_order = 1

如果您正在使用Mixin并且希望您的Derived类的属性放在第一位,则这是Column的一个直接替代品。

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)
j91ykkif

j91ykkif6#

我发现我可以在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 tablesome_tablemodifysome_columsome_typeaftersome_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列。我真的不明白为什么你甚至可以使用小数。
要根据第一列的位置设置列顺序(使其始终为第四列),您可以执行以下操作:

@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

相关问题