query - sqlalchemy python
sqlalchemy Mueve las columnas de la mezcla al final (4)
Descubrí que podía establecer el orden de la columna (en la última posición) en el 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 en función de la ubicación de otra columna (similar a
alter table `some_table` modify `some_colum` `some_type` after `some_other_column;
vea https://stackoverflow.com/a/3822219/488331 )
Puedes usar:
@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 usas + 1
terminas 2 columnas atrás. Realmente no entiendo por qué puedes usar un decimal.
Para establecer el orden de las columnas en función de la ubicación de la primera columna (haga esto siempre la cuarta columna), puede 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
Tengo un modelo sqlalchemy, donde la mayoría de todas las tablas / objetos tienen un campo de notas. Así que para intentar seguir el principio DRY, moví el campo a una clase mixta.
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. ¿Alguna forma de moverla al final?
Encontró una solución más 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 tienen claves externas, así como para la variedad de construcciones de nivel de asignador que requieren un contexto de destino explícito". Si bien este no es el caso aquí en sentido estricto, 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 mezcla vendrá al final. Probablemente una mejor solución que hackear _creation_order
...
La respuesta fácil: solo cree las tablas de la base de datos, en lugar de hacer que sqlalchemy lo haga con metadata.create_all()
.
Si no lo encuentra aceptable, me temo que esto requeriría un cambio (pequeño) en sqlalchemy.ext.declarative
, o tendría que crear su propia metaclase y pasarla a declarative_base()
con la palabra clave metaclass
argumento. Esa clase se utilizará en lugar del DeclarativeMeta
predeterminado.
Explicación: sqlalchemy usa el orden de creación de las propiedades de la columna, que almacena en el atributo "privado" ._creation_order
(generado cuando se llama a la Columna ()). La extensión declarativa hace columnas de mezcla creando una copia del objeto de columna de su clase de mezcla, y agregando eso a la clase. El ._creation_order
de esta copia se establece en el mismo valor que la propiedad original de la clase mixin. Por supuesto, como la clase mixin se crea primero, sus propiedades de columna tendrán un orden de creación inferior al de la subclase.
Por lo tanto, para que su solicitud sea posible, se debe asignar un nuevo orden de creación cuando se realice la copia, en lugar de tomar el original. Puedes intentar crear tu propia metaclase en base a esta explicación y usarla. Pero también puedes intentar preguntar a los desarrolladores de sqlalchemy. ¿Quizás están dispuestos a aceptar esto como una solicitud de error / característica? Al menos, parece un cambio menor (una línea), que no tendría ningún otro efecto que el cambio que solicitas (que posiblemente sea mejor también).
También se puede cambiar el orden de las columnas en la compilación CREAR TABLA (aquí ejemplificada para el dialecto 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)
Esto luego funciona con metadata.create_all()
.