103 votes

SQLAlchemy, obtenir un objet non lié à une Session

Je cherche à obtenir une collection d'objets d'une base de données et à la passer à un autre processus qui n'est pas connecté à la base de données. Mon code ressemble à celui ci-dessous mais je reçois toujours :

sqlalchemy.exc.UnboundExecutionError: L'instance  n'est pas liée à une session ; l'opération de rafraîchissement de l'attribut ne peut pas continuer

Quand j'essaie de regarder les éléments de ma liste en dehors de la méthode get_list().

def get_list (obj):
    session = Session()
    lst = session.query(MyClass).all()
    session.close()
    return lst

Cependant, si j'utilise ceci :

def get_list_bis (obj):
    session = Session()
    return session.query(MyClass).all()

Je peux utiliser les éléments mais je m'inquiète de l'état de la session car elle n'a pas été fermée.

Qu'est-ce que je rate ici ?

102voto

Pavel Repin Points 13751

Si vous souhaitez qu'un ensemble d'objets produits par une requête à une session soit utilisable en dehors de la session, vous devez les expurger de la session.

Dans votre premier exemple de fonction, vous devrez ajouter une ligne :

session.expunge_all()

avant

session.close()

De manière plus générale, supposons que la session ne soit pas fermée immédiatement, comme dans le premier exemple. Peut-être s'agit-il d'une session maintenue active pendant toute la durée d'une requête web ou quelque chose du genre. Dans de tels cas, vous ne voulez pas utiliser expunge_all. Vous voudrez être plus ciblé :

for item in lst:
    session.expunge(item)

51voto

Antash Points 388

Cela arrive souvent en raison d'objets étant dans un état expiré, les objets expirent par exemple après committing, puis lorsque de tels objets expirés sont sur le point d'être utilisés, l'ORM tente de les refresh, mais cela ne peut être fait lorsque les objets sont détachés de la session (par exemple, car cette session était fermée). Ce comportement peut être géré en créant une session avec le paramètre expire_on_commit=False.

>>> from sqlalchemy import inspect
>>> insp = inspect(my_object)
>>> insp.expired
True  # alors il sera rafraîchi...

4voto

kolypto Points 3161

Dans mon cas, je sauvegardais également une entité connexe, et cette recette m'a aidé à actualiser toutes les instances dans une session, en tirant parti du fait que Session est itérable :

map(session.refresh, iter(session))  # appeler refresh() sur chaque instance

C'est extrêmement inefficace, mais ça fonctionne. Devrait être bien pour les tests unitaires.

Note finale : en Python3 map() est un générateur et ne fera rien. Utilisez de vraies boucles ou des compréhensions de listes

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X