python - tutorial - método de iteración sobre las columnas definidas del modelo sqlalchemy?
sqlalchemy relationship not null (6)
He estado tratando de averiguar cómo iterar sobre la lista de columnas definidas en un modelo SqlAlchemy. Lo quiero para escribir algunos métodos de serialización y copia en un par de modelos. No puedo simplemente iterar sobre el obj. dict ya que contiene una gran cantidad de elementos específicos de SA.
¿Alguien sabe de una forma de obtener los id, y desc nombres de los siguientes?
class JobStatus(Base):
__tablename__ = ''jobstatus''
id = Column(Integer, primary_key=True)
desc = Column(Unicode(20))
En este pequeño caso, podría crear fácilmente un:
def logme(self):
return {''id'': self.id, ''desc'': self.desc}
pero preferiría algo que se genere automáticamente para objetos más grandes.
Gracias por la asistencia.
Me doy cuenta de que esta es una vieja pregunta, pero acabo de encontrar el mismo requisito y me gustaría ofrecer una solución alternativa para los lectores futuros.
Como señala Josh, las JobStatus.__table__.columns
nombres completos de los campos SQL, por lo que en lugar del ID del nombre del campo original, obtendrá jobstatus.id . No es tan útil como podría ser.
La solución para obtener una lista de nombres de campo como se definieron originalmente es buscar el atributo _data
en el objeto de columna, que contiene los datos completos. Si miramos JobStatus.__table__.columns._data
, se ve así:
{''desc'': Column(''desc'', Unicode(length=20), table=<jobstatus>),
''id'': Column(''id'', Integer(), table=<jobstatus>, primary_key=True, nullable=False)}
Desde aquí, simplemente puede llamar a JobStatus.__table__.columns._data.keys()
que le proporciona una lista limpia y agradable:
[''id'', ''desc'']
Para obtener un método as_dict
en todas mis clases, utilicé una clase Mixin
que usa las técnicas descritas por .
class BaseMixin(object):
def as_dict(self):
result = {}
for prop in class_mapper(self.__class__).iterate_properties:
if isinstance(prop, ColumnProperty):
result[prop.key] = getattr(self, prop.key)
return result
Y luego úsala así en tus clases
class MyClass(BaseMixin, Base):
pass
De esta forma, puede invocar lo siguiente en una instancia de MyClass
.
> myclass = MyClass()
> myclass.as_dict()
Espero que esto ayude.
He jugado con esto un poco más allá, de hecho necesitaba representar mis instancias como dict
como la forma de un objeto HAL con sus enlaces a objetos relacionados. Así que he agregado esta pequeña magia aquí, que se rastreará sobre todas las propiedades de la clase, como la anterior, con la diferencia de que voy a rastrear más profundamente en Relaionship
propiedades de Relaionship
y generar links
para estos automáticamente.
Tenga en cuenta que esto solo funcionará para las relaciones tienen una sola clave principal
from sqlalchemy.orm import class_mapper, ColumnProperty
from functools import reduce
def deepgetattr(obj, attr):
"""Recurses through an attribute chain to get the ultimate value."""
return reduce(getattr, attr.split(''.''), obj)
class BaseMixin(object):
def as_dict(self):
IgnoreInstrumented = (
InstrumentedList, InstrumentedDict, InstrumentedSet
)
result = {}
for prop in class_mapper(self.__class__).iterate_properties:
if isinstance(getattr(self, prop.key), IgnoreInstrumented):
# All reverse relations are assigned to each related instances
# we don''t need to link these, so we skip
continue
if isinstance(prop, ColumnProperty):
# Add simple property to the dictionary with its value
result[prop.key] = getattr(self, prop.key)
if isinstance(prop, RelationshipProperty):
# Construct links relaions
if ''links'' not in result:
result[''links''] = {}
# Get value using nested class keys
value = (
deepgetattr(
self, prop.key + "." + prop.mapper.primary_key[0].key
)
)
result[''links''][prop.key] = {}
result[''links''][prop.key][''href''] = (
"/{}/{}".format(prop.key, value)
)
return result
Puede obtener la lista de propiedades definidas del asignador. Para su caso, solo le interesan los objetos ColumnProperty.
from sqlalchemy.orm import class_mapper
import sqlalchemy
def attribute_names(cls):
return [prop.key for prop in class_mapper(cls).iterate_properties
if isinstance(prop, sqlalchemy.orm.ColumnProperty)]
Puede usar la siguiente función:
def __unicode__(self):
return "[%s(%s)]" % (self.__class__.__name__, '', ''.join(''%s=%s'' % (k, self.__dict__[k]) for k in sorted(self.__dict__) if ''_sa_'' != k[:4]))
Excluirá los atributos mágicos de SA, pero no excluirá las relaciones. Así que, básicamente, podría cargar las dependencias, los padres, los niños, etc., lo que definitivamente no es deseable.
Pero en realidad es mucho más fácil porque si __table__
de Base
, tienes un atributo __table__
, de modo que puedes hacer:
for c in JobStatus.__table__.columns:
print c
for c in JobStatus.__table__.foreign_keys:
print c
Consulte Cómo descubrir las propiedades de tabla del objeto mapeado SQLAlchemy - pregunta similar.
Editar por Mike: vea funciones como Mapper.c y Mapper.mapped_table . Si usa 0.8 y más, también vea Mapper.attrs y funciones relacionadas.
Ejemplo para Mapper.attrs :
from sqlalchemy import inspect
mapper = inspect(JobStatus)
for column in mapper.attrs:
print column.key
Sé que esta es una vieja pregunta, pero ¿qué hay de:
class JobStatus(Base):
...
def columns(self):
return [col for col in dir(self) if isinstance(col, db.Column)]
Luego, para obtener los nombres de las columnas: jobStatus.columns()
Eso devolvería [''id'', ''desc'']
Luego puede recorrer y hacer cosas con las columnas y valores:
for col in jobStatus.colums():
doStuff(getattr(jobStatus, col))
self.__table__.columns
"solo" te dará las columnas definidas en esa clase particular, es decir, sin heredar. si necesita todo, use self.__mapper__.columns
. en su ejemplo, probablemente use algo como esto:
class JobStatus(Base):
...
def __iter__(self):
values = vars(self)
for attr in self.__mapper__.columns.keys():
if attr in values:
yield attr, values[attr]
def logme(self):
return dict(self)