2010-10-13 12 views
15

Ich habe ein SQL-Modell, in dem alle meisten Tabellen/Objekte ein Notizenfeld haben. Um dem DRY-Prinzip zu folgen, habe ich das Feld in eine Mixinklasse verlegt.sqlalchemy Move mixin Spalten zu Ende

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... 

Jetzt ist die Spalte notizen die erste Spalte im Modell/db. Ich weiß, es hat keinen Einfluss auf die Funktionalität meiner App, aber es irritiert mich ein wenig, um Notizen vor der ID usw. zu sehen. Irgendeine Möglichkeit, es zum Ende zu bewegen?

Antwort

15

eine sauberere Lösung gefunden:

Mit dem sqlalchemy.ext.declarative.declared_attr decorator in sqlalchemy 0.6.5 (sqlalchemy.util.classproperty in sqlalchemy < = 0.6.4)

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

die Dokumentation Nach ist diese „für Spalten, die haben Fremdschlüssel, sowie für die Vielzahl von Mapper-Level-Konstrukten, die Ziel-expliziten Kontext erfordern ". Obwohl dies hier streng genommen nicht der Fall ist, geschieht dies, indem die Methode aufgerufen wird (und die Spalte erstellt wird), wenn die Unterklasse erstellt wird, wodurch vermieden wird, dass eine Kopie erstellt werden muss. Was bedeutet, dass die Mixin-Kolonne am Ende kommen wird. Wahrscheinlich eine bessere Lösung als _creation_order Hacking ...

+0

Ich bevorzugte definitiv die Reinigerlösung, da sie bereits in sqlalchemy eingebaut ist. Ich stelle fest, dass die Reihenfolge, in der Sie das Mixin erben, von Bedeutung ist, wenn Sie mehrere deklarative Base-Mixins haben. Das ist zu erwarten, nehme ich an. –

+0

Schöne Lösung, danke! – sleepycal

+1

Zwar erfordert dies weniger Code, aber es fühlt sich ein wenig umständlich an, da es einen "Hack" -Weg verwendet, d. H. Einen nicht beabsichtigten Nebeneffekt der Fremdschlüsseleinschränkung, um die Reihenfolge sicherzustellen. Ich bevorzuge die '_creation_order'-Lösung, da dies die beabsichtigte Möglichkeit ist, ** die Erstellungsreihenfolge der Spalten ** explizit festzulegen, unabhängig davon, ob sie ein FK sind oder nicht. – Devy

5

Die einfache Antwort: Erstellen Sie einfach die Datenbanktabellen selbst, anstatt sqlalchemy tun Sie es mit metadata.create_all().

Wenn Sie nicht finden, dass akzeptabel, ich fürchte, das würde sich in sqlalchemy.ext.declarative eine (kleine) Änderung erfordern, oder würden Sie haben Ihre eigene Metaklasse erstellen und an declarative_base() mit dem metaclass Stichwort Argument übergeben. Diese Klasse wird dann anstelle des Standards DeclarativeMeta verwendet.

Erläuterung: sqlalchemy verwendet die Erstellungsreihenfolge der Spalteneigenschaften, die im Attribut "private" ._creation_order gespeichert sind (wird generiert, wenn Column() aufgerufen wird). Die deklarative Erweiterung mischt Spalten, indem sie eine Kopie des Spaltenobjekts aus Ihrer Mixinklasse erstellt und diese zur Klasse hinzufügt. Die ._creation_order dieser Kopie wird auf denselben Wert wie die ursprüngliche Eigenschaft der Mixin-Klasse festgelegt. Da die Mixin-Klasse natürlich zuerst erstellt wird, haben die Spalteneigenschaften eine niedrigere Erstellungsreihenfolge als die Unterklasse.

Also, um Ihre Anfrage zu ermöglichen, sollte eine neue Erstellungsreihenfolge zugewiesen werden, wenn die Kopie erstellt wird, anstatt das Original zu nehmen. Sie könnten versuchen, Ihre eigene Metaklasse basierend auf dieser Erklärung zu erstellen und diese zu verwenden. Aber Sie könnten auch versuchen und fragen Sie die sqlalchemy Entwickler. Vielleicht sind sie bereit, dies als Bug/Feature-Anfrage zu akzeptieren? Zumindest scheint es wie eine geringfügige Änderung (eine Zeile), das würde keinen anderen Effekt als die Änderung haben, die Sie verlangen (was wohl auch besser ist).

+4

Dank. Mit diesen Informationen habe ich: 'NotesMixin.notes._creation_order = 9999' –

+0

Auch wenn mit weniger Stimmen, sollte dies der" richtige "Weg sein, da es ** EXPLICITLY ** die Spaltenerstellungsreihenfolge wie OP gefragt, unabhängig von Spalte ein FK sein oder nicht. – Devy

+0

Wo setzen Sie die NotesMixin.notes._creation_order = 9999? Im Mixin oder in der Unterklasse? Oder irgendwo anders? –

2

Man kann auch die Reihenfolge der Spalten ändern auf TABLE Kompilation CREATE (hier beispielhaft für den postgresql Dialekt):

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) 

Diese dann mit metadata.create_all() arbeitet.

+0

Ich verwendete eine Variante von diesem: fügen Sie einen "ordnenden" Schlüssel zu jeder Spalte (dh 'Column (..., info = {'ordernd': 0})') und sortieren Sie 'element.columns' mit diesem Schlüssel (z 'element.columns = sortierte (element.columns, key = lambda c: c.element.info.get ('ordern')') in der create table compilation. – jadkik94

0

fand ich, dass ich die Spaltenreihenfolge (auf die letzte Position) auf der Mixin mit einstellen könnte:

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

So stellen Sie die Spaltenreihenfolge auf den Standort einer anderen Spalte (ähnlich

basierend

alter table `some_table` modify `some_colum` `some_type` after `some_other_column;

siehe https://stackoverflow.com/a/3822219/488331)

können Sie verwenden:

@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 

HINWEIS: Wenn Sie + 1 Sie am Ende 2 Spalten zurück. Ich verstehe nicht wirklich, warum Sie sogar eine Dezimalzahl verwenden können.

die Reihenfolge der Spalten aus der Position der ersten Spalte (macht dies immer die vierte Spalte) basierend So richten Sie tun können:

@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