python - recorrer - SQLAlchemy: ¿Conoce los nombres de campo y los valores de un objeto modelo?
listas en python (1)
Estoy tratando de mantener los principios de programación orientada a objetos SÓLIDOS, permanecer seco, etc., pero mi novedad en Python / SQLAlchemy / Pyramid lo está haciendo muy difícil.
Estoy tratando de tomar lo que ahora sé que es un modelo de SQLAlchemy utilizado para crear un objeto simple de Pyramid Framework y usar lo que sé que es "reflejo" en C #, puede que se lo llame algo diferente en Python (¿Introspección? No estoy seguro como esta es solo mi segunda semana con Python, pero tengo mucha experiencia en otros idiomas (C / C ++ / C #, Java, etc.) así que el problema parece ser mapear mi conocimiento con el vocabulario de Python, lo siento), para descubrir el nombres de campo de la tabla de la base de datos, y lo más importante, los valores de campo actuales, cuando no conozco los nombres de las columnas o CUALQUIER forma del objeto de antemano.
Está bien; No sé si la instancia de ''derp'' tiene un campo llamado id o name, solo que tiene columnas y un valor en cada una de ellas. Y eso es todo lo que me importa.
El objetivo es poder tomar cualquier modelo de datos definido de SQLAlchemy y convertirlo en un diccionario de column_name -> column_value fields de tipos de datos simples del tipo encontrado en JSON, ya que quiero serializar finalmente cualquier objeto que cree en SQLAlchemy a un json objeto, pero me conformaré con un diccionario ya que a partir de ahí es trivial siempre que el diccionario tenga los tipos de datos correctos. Hacer esto para cada objeto a mano es una violación de demasiadas buenas reglas de código limpio y creará demasiado trabajo a lo largo del tiempo; Podría pasar otra semana más en esto y aun así ahorrar tiempo y esfuerzo al hacerlo de la manera correcta.
Entonces, si tengo una clase definida en SQLAlchemy como:
class SimpleFooModel(Base):
id = Column(Integer, primary_key=True, autoincrement=True, nullable=False)
name = Column(VARCHAR(length=12), nullable=False, index=True)
.. y tengo una instancia de esto igual a (en python):
derp = SimpleFooModel(id=7, name="Foobar")
Quiero poder SOLO tener la variable de instancia ''derp'' descrita anteriormente, y NO HAY OTRO CONOCIMIENTO sobre cómo se modela el modelo, y ser capaz de aplanarlo en un diccionario de clave python-> para ese objeto simple, donde cada el valor de ese diccionario se puede serializar a JSON utilizando import json from python syslib.
El problema es que llevo 2 días mirando esto y no puedo encontrar una respuesta que me brinde los resultados que quiero en mis pruebas unitarias DONDEQUIERA; Google me sigue llevando a publicaciones realmente antiguas aquí en SO sobre versiones realmente antiguas de la biblioteca que usan interfaces que ya no se aplican o que han aceptado respuestas que en realidad no funcionan en absoluto; y dado que ninguno de ellos es reciente, eso me sorprende (pero por qué Stack Overflow los mantiene cuando están equivocados y permite a google engañar a la gente) me sorprende)
Sé que podría conectar todos los objetos manualmente para cada objeto, etc., pero eso no solo NO ES ELEGANTE, es ineficiente porque me crea más trabajo ya que creo más objetos y podría provocar grandes errores en el futuro. Quiero saber cómo hacerlo de la manera correcta, con introspección / reflexión, pero nadie parece saberlo, y las personas que afirman haber dado todos los ejemplos aquí en desbordamiento de pila que en realidad no funcionan en absoluto (al menos con el actual versiones de cosas)
Esto parece ser un caso de uso muy común para mí; y obtener la lista de campos de la columna y luego iterar a través de ella con getattr, como muchas de las respuestas dicen que hacer, tampoco funciona como se esperaba; simplemente crea lo que parecen espacios de nombres que nunca devuelven el valor real de la columna, y no existen en realidad en ningún código, ya que ninguno de los campos creados por sqlalchmy son únicos / estáticos.
Asi que:
from sqlalchemy.inspection import inspect
obj = inspect(derp, raiseerr=True)
for key in obj.attrs.keys():
fields[key] = getattr(derp, key)
print fields[key]
Solo me da:
[Class Name].[Column Name]
.. o en este caso solo me da:
SimpleFooModel.id
SimpleFooModel.name
NO los valores de 7 y "Foobar" para identificación y nombre, respectivamente, que realmente esperaba en mis pruebas.
De hecho, parece que ni siquiera puedo encontrar DONDE se almacenan los valores en el modelo de objetos; o podría forzar el problema brutalmente y sacarlos de allí como un truco feo y malvado del que me avergonzaría mirar. Todo lo que obtengo a través de la "API pública oficial" es una gran cantidad de objetos que parecen no tener idea de dónde se almacenan los datos reales, pero con mucho gusto me dicen el nombre de la ruta utilizada por el nombre y el tipo de columna, las restricciones, etc. ... simplemente no los datos reales que quiero.
Sin embargo, dado que mi requisito es que no conozco los nombres de campo por adelantado, usar una llamada a derp.id o derp.name para recopilar el valor no es una opción, ya que eso violaría SOLID y me obligaría a duplicar el trabajo para cada clase. . Entonces no es una opción.
Tal vez es el hecho de que no he dormido en 2 días, pero es realmente difícil para mí no ver esto como un serio defecto de diseño en estas libs; Solo quiero serializar un objeto de modelo definido por SQLAlchemy que representa una sola fila en una tabla en un diccionario de Python sin tener que conocer los nombres de los campos de antemano, y mientras que muchos otros idiomas lo hacen fácil o incluso trivial, esto parece estar lejos demasiado duro de lo que debería ser.
¿Alguien puede explicar una solución de trabajo o por qué me equivoco al querer aplicar SOLID a mi código?
EDITAR: ortografía actualizada.
Extiende tu modelo con la siguiente clase:
class BaseModel(object):
@classmethod
def _get_keys(cls):
return sa.orm.class_mapper(cls).c.keys()
def get_dict(self):
d = {}
for k in self._get_keys():
d[k] = getattr(self, k)
return d
Esto hará exactamente lo que quieras, devuelve un dict en forma de {''column_name'': ''value''} pares.