J'ai essayé de comprendre comment parcourir la liste des colonnes définies dans un modèle SQLAlchemy. Je le veux pour écrire des méthodes de sérialisation et de copie sur quelques modèles. Je ne peux pas simplement parcourir le obj.__dict__
car il contient beaucoup SA éléments spécifiques.
Quelqu'un connaît-il un moyen d'obtenir simplement les noms id
et desc
parmi les suivants?
class JobStatus(Base):
__table= 'jobstatus'
id = Column(Integer, primary_key=True)
desc = Column(Unicode(20))
Dans ce petit cas, je pourrais facilement créer un:
def logme(self):
return {'id': self.id, 'desc': self.desc}
mais je préfère quelque chose qui génère automatiquement le dict
(pour les objets plus gros).
Vous pouvez utiliser la fonction suivante:
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]))
Il exclura les attributs SA magic, mais n'exclura pas les relations. Donc, fondamentalement, cela pourrait charger les dépendances, les parents, les enfants, etc., ce qui n'est certainement pas souhaitable.
Mais c'est en fait beaucoup plus facile car si vous héritez de Base
, vous avez un __table__
attribut, pour que vous puissiez faire:
for c in JobStatus.__table__.columns:
print c
for c in JobStatus.__table__.foreign_keys:
print c
Voir Comment découvrir les propriétés d'une table à partir d'un objet mappé SQLAlchemy - question similaire.
Edit by Mike: Veuillez voir les fonctions telles que Mapper.c et Mapper.mapped_table . Si vous utilisez 0.8 et plus, consultez également Mapper.attrs et les fonctions associées.
Exemple pour Mapper.attrs :
from sqlalchemy import inspect
mapper = inspect(JobStatus)
for column in mapper.attrs:
print column.key
Vous pouvez obtenir la liste des propriétés définies à partir du mappeur. Pour votre cas, seuls les objets ColumnProperty vous intéressent.
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)]
Je me rends compte que c'est une vieille question, mais je viens de rencontrer la même exigence et je voudrais offrir une solution alternative aux futurs lecteurs.
Comme le note Josh, les noms de champs SQL complets seront retournés par JobStatus.__table__.columns
, Donc plutôt que le nom de champ d'origine id, vous obtiendrez jobstatus.id. Pas aussi utile que cela pourrait l'être.
La solution pour obtenir une liste de noms de champs tels qu'ils ont été définis à l'origine est de rechercher l'attribut _data
Sur l'objet colonne, qui contient les données complètes. Si nous regardons JobStatus.__table__.columns._data
, Cela ressemble à ceci:
{'desc': Column('desc', Unicode(length=20), table=<jobstatus>),
'id': Column('id', Integer(), table=<jobstatus>, primary_key=True, nullable=False)}
De là, vous pouvez simplement appeler JobStatus.__table__.columns._data.keys()
qui vous donne une liste agréable et propre:
['id', 'desc']
self.__table__.columns
vous donnera "uniquement" les colonnes définies dans cette classe particulière, c'est-à-dire sans celles héritées. si vous avez besoin de tout, utilisez self.__mapper__.columns
. dans votre exemple, j'utiliserais probablement quelque chose comme ceci:
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)
Pour obtenir un as_dict
méthode sur toutes mes classes J'ai utilisé une classe Mixin
qui utilise les techniques décrites par Ants Aasma .
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
Et puis utilisez-le comme ça dans vos cours
class MyClass(BaseMixin, Base):
pass
De cette façon, vous pouvez appeler ce qui suit sur une instance de MyClass
.
> myclass = MyClass()
> myclass.as_dict()
J'espère que cela t'aides.
J'ai joué un peu plus loin avec cela, j'avais en fait besoin de rendre mes instances sous la forme dict
sous la forme d'un objet HAL avec ses liens vers des objets liés. J'ai donc ajouté cette petite magie ici, qui explorera toutes les propriétés de la classe comme ci-dessus, à la différence que j'explorerai plus profondément dans les propriétés Relaionship
et générer links
pour ces automatiquement.
Veuillez noter que cela ne fonctionnera que pour les relations avec une seule clé primaire
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
En supposant que vous utilisez le mappage déclaratif de SQLAlchemy, vous pouvez utiliser l'attribut __mapper__
Pour accéder au mappeur de classe. Pour obtenir tous les attributs mappés (y compris les relations):
obj.__mapper__.attrs.keys()
Si vous voulez des noms de colonnes strictement, utilisez obj.__mapper__.column_attrs.keys()
. Voir la documentation pour d'autres vues.
https://docs.sqlalchemy.org/en/latest/orm/mapping_api.html#sqlalchemy.orm.mapper.Mapper.attrs
self.__dict__
renvoie un dict où les clés sont des noms d'attribut et valorise les valeurs de l'objet.
/!\il y a un attribut supplémentaire: '_sa_instance_state' mais vous pouvez le gérer :)