2010-10-13 12 views
15

Tengo un modelo sqlalchemy, donde la mayoría de las tablas/objetos tienen un campo de notas. Entonces, para intentar seguir el principio DRY, moví el campo a una clase mixin.sqlalchemy Mover columnas mixin al final

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

Ahora la columna de notas es la primera columna en el modelo/db. Sé que no afecta la funcionalidad de mi aplicación, pero me irrita un poco ver las notas antes de la identificación, etc. ¿Hay alguna manera de moverlo hasta el final?

Respuesta

15

encontrado una solución limpia:

Utilice el decorador sqlalchemy.ext.declarative.declared_attr en sqlalchemy 0.6.5 (sqlalchemy.util.classproperty en sqlalchemy < = 0.6.4)

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

Según los documentos, esto es "para las columnas que tener claves externas, así como también la variedad de construcciones de nivel de mapeador que requieren un contexto de destino explícito ". Si bien esto no es estrictamente el caso aquí, lo hace llamando al método (y creando la columna) cuando se construye la subclase, evitando así la necesidad de hacer una copia. Lo que significa que la columna de mixin vendrá al final. Probablemente una solución mejor que la piratería _creation_order ...

+0

Definitivamente prefiero la solución limpiadora ya que está integrada en sqlalchemy. Observé que el orden en el que heredas la mixin importa, si tienes múltiples mixins de base declarativa. Eso es de esperar, supongo. –

+0

Hermosa solución, gracias! – sleepycal

+1

Si bien esto requiere menos código, pero se siente un poco indirecto ya que utiliza una forma de "hackeo", es decir, un efecto secundario no intencionado de la restricción de clave externa para garantizar el orden. Prefiero la solución '_creation_order' porque esa es la forma prevista de ** EXPLICITAMENTE ** establecer el orden de creación de la columna, sin importar si es FK o no. – Devy

5

La respuesta fácil: simplemente cree las tablas de la base de datos usted mismo, en lugar de tener sqlalchemy hacerlo con metadata.create_all().

Si no encuentra que aceptable, me temo que esto requeriría un cambio (pequeña) en sqlalchemy.ext.declarative sí mismo, o que tendría que crear su propia metaclase y pasarlo a declarative_base() con el argumento metaclass palabra clave. Esa clase se usará en lugar de la predeterminada DeclarativeMeta.

Explicación: sqlalchemy utiliza el orden de creación de las propiedades de la columna, que almacena en el atributo "privado" ._creation_order (generado cuando se llama a Column()). La extensión declarativa se mezcla en columnas al crear una copia del objeto de columna de su clase mixin y agregar eso a la clase. El ._creation_order de esta copia tiene el mismo valor que la propiedad original de la clase mixin. Como la clase de mixin se crea primero, las propiedades de la columna tendrán un orden de creación inferior al de la subclase.

Por lo tanto, para hacer su solicitud posible, se debe asignar una nueva orden de creación cuando se realiza la copia, en lugar de tomar el original. Podrías probar y hacer tu propia metaclase en base a esta explicación, y usar eso. Pero también puedes intentar preguntar a los desarrolladores de sqlalchemy. Tal vez están dispuestos a aceptar esto como una solicitud de error/función? Al menos, parece como un cambio menor (una línea), que no tendría ningún otro efecto que no sea el cambio que usted solicita (que podría decirse que también es mejor).

+4

Gracias. Usando esa información, hice: 'NotesMixin.notes._creation_order = 9999' –

+0

Aunque con menos votos, esta debería ser la forma" correcta ", ya que ** EXPLÍCITAMENTE ** estableció el orden de creación de la columna tal como lo hizo OP, independientemente de la columna ser un FK o no. – Devy

+0

¿Dónde pone NotesMixin.notes._creation_order = 9999? En el Mixin o en la subclase? ¿O en otra parte? –

2

También se puede cambiar el orden de las columnas en la compilación CREAR TABLA (en este ejemplo para el postgresql dialecto):

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) 

Esto entonces trabaja con metadata.create_all().

+0

Utilicé una variación de esto: agregué una clave de "orden" a cada columna (es decir, 'Columna (..., info = {'ordenar': 0})') y ordené 'elemento.columnas' usando esa tecla (ej. 'element.columns = sorted (element.columns, key = lambda c: c.element.info.get ('ordering')') en la compilación create table. – jadkik94

0

I encontré que podría establecer el orden de la columna (a la última posición) en la Mixin usando:

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

Para establecer el orden de las columnas basado en la ubicación de otra columna (similar a

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

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

Se puede utilizar:

@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 

NOTA: Si utiliza + 1 terminas 2 columnas espalda. Realmente no entiendo por qué puedes usar un decimal.

Para establecer el orden de las columnas con sede fuera de la ubicación de la primera columna (que esto siempre es la cuarta columna) que podría hacer:

@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 
Cuestiones relacionadas