2010-10-13 57 views
15

我有一個sqlalchemy模型,其中所有大多數表/對象都有一個註釋字段。因此,爲了遵循DRY原則,我將該領域轉移到了混合課程中。sqlalchemy將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... 

現在筆記列是模型/ db中的第一列。我知道它不會影響我的應用程序的功能,但是它讓我感到有點在id之前看到註釋等等。任何將它移動到最後的方法?

回答

15

發現一個清潔的解決方案:

使用在SQLAlchemy的0.6.5的​​裝飾(sqlalchemy.util.classproperty在SQLAlchemy的< = 0.6.4)

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

根據該文檔,這是「爲列,其有外鍵,以及各種需要目標顯式上下文的映射級結構「。儘管在這裏嚴格來說不是這樣,但是它在構造子類時調用方法(並創建列),從而避免了複製的需要。這意味着mixin列將會結束。可能是一個更好的解決方案比黑客_creation_order ...

+0

我確實更喜歡更清潔的解決方案,因爲它已經內置到sqlalchemy中。我注意到,如果您有多個聲明性基本mixin,那麼您繼承mixin的順序很重要。這是我預計的。 – 2014-01-29 21:17:19

+0

美麗的解決方案,謝謝! – sleepycal 2014-07-23 20:14:58

+1

雖然這需要較少的代碼,但它使用「黑客」方式感覺有點迂迴,也就是外鍵約束的非內在副作用,以確保排序。我更喜歡'_creation_order'解決方案,因爲這是** EXPLICITLY **設置列創建順序的預期方式,無論是否爲FK。 – Devy 2015-07-16 15:11:39

5

簡單的回答:只需自己創建數據庫表,而不是讓sqlalchemy與metadata.create_all()一起完成。

如果你沒有找到可以接受的,這恐怕將需要sqlalchemy.ext.declarative本身就是一個(小)的變化,或者你就必須創建自己的元類,並將其與metaclass關鍵字參數傳遞給declarative_base()。然後該類將被使用,而不是默認DeclarativeMeta

說明:sqlalchemy使用列屬性的創建順序,它存儲在「private」屬性._creation_order(在調用Column()時生成)。聲明性擴展通過從mixin類創建列對象的副本並將其添加到類中來混合列。該副本的._creation_order設置爲與mixin類的原始屬性相同的值。由於mixin類當然是先創建的,它的列屬性將具有比子類更低的創建順序。

因此,爲了使您的請求成爲可能,創建副本時應創建一個新的創建順序,而不是採取原件。你可以嘗試根據這個解釋製作你自己的元類,並使用它。但你也可以試着問問sqlalchemy的開發者。也許他們願意接受這個錯誤/功能要求?至少,似乎就像一個小小的(一行)變化,除了你所要求的改變之外沒有任何其他影響(可以說它更好)。

+4

感謝。使用這個信息,我做了:'NotesMixin.notes._creation_order = 9999' – 2010-10-14 10:55:47

+0

即使投票少,這應該是「正確」的方式,因爲它**顯式設置列創建順序就像OP問的一樣,無論列是否是FK。 – Devy 2015-07-16 15:13:10

+0

你把NotesMixin.notes._creation_order = 9999放在哪裏?在Mixin中還是在子類中?或者別的地方? – 2018-02-08 20:49:46

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()工作。

+0

我使用了一個變體:爲每列添加一個「排序」鍵(即Column(...,info = {'ordering':0})),並使用該鍵對element.columns進行排序在create table編譯中''element.columns = sorted(element.columns,key = lambda c:c.element.info.get('ordering')'')。 – jadkik94 2017-10-05 20:09:36

0

我發現我可以在密新使用設置列順序(最後位置):

@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 
相關問題