Error de sesiones SQLAlchemy

Antecedentes: Flask / Flask-SQLAlchemy / Flask-WTF, usando sesión declarativa y con ámbito

Operación POST simple:

 @tas.route('/order_add', methods=['GET', 'POST']) def tas_order_add(): if request.method == 'POST': order_form = OrderForm() if order_form.validate_on_submit(): order = Order() order_form.populate_obj(order) db_session.add(order) db_session.commit() 

Ahora tratando de ejecutarlo me sale un error:

InvalidRequestError: El objeto ” ya está adjunto a la sesión ‘1’ (esto es ‘2’)

Cambiar agregar para fusionar resuelve el problema, pero:

  • No sé por qué tengo que fusionar un objeto mientras lo acabo de iniciar.
  • Si cambio agregar para fusionar e intentar definir una de las propiedades algo en línea

     order = Order() order_form.populate_obj(order) order.order_status = OrderStatus.query.filter(OrderStatus.code=='PLACED').first() db_session.merge(order) db_session.commit() 

    Me sale el mismo error, justo ahora en el objeto OrderStatus

    InvalidRequestError: El objeto ” ya está adjunto a la sesión ‘2’ (esto es ‘1’)

¿Alguien me puede indicar dónde estoy haciendo algo mal porque me está volviendo loco? Tengo algo de experiencia con SQLAlchemy pero es la primera vez que veo un comportamiento así y no puedo identificar el problema.

Buscar todo lo que encontré fue un problema con la inicialización de la sesión de doble base de datos, pero no creo que sea este el caso.

EDITAR

db_session se define en el archivo database.py por separado con el siguiente contenido

 from sqlalchemy.engine import create_engine from sqlalchemy.ext.declarative.api import declarative_base from sqlalchemy.orm.scoping import scoped_session from sqlalchemy.orm.session import sessionmaker engine = create_engine('sqlite:///fundmanager_devel.db', convert_unicode=True) db_session = scoped_session(sessionmaker(autocommit=False, autoflush=False, bind=engine)) Base = declarative_base() Base.query = db_session.query_property() 

Parece un problema con la mezcla de sesión, lo usé de la siguiente manera y funciona:

 from  import db from  

Tuve un problema similar con QuerySelectField:

forms.py:

 class SurgeryForm(Form): study = QuerySelectField('Study', query_factory=StudyGroup.query.all, get_label='name') 

modelos.py

 class Animal(db.Model): study_id = db.Column(db.Integer, db.ForeignKey('study_group.id')) class StudyGroup(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(50)) animals = db.relationship('Animal', backref='group', lazy='dynamic') 

views.py:

 def do_surgery(): form = SurgeryForm(request.form) if form.validate_on_submit(): a = models.Animal() form.populate_obj(a) # Gather the easy stuff automagically #a.group = form.data['study'] #FAILS! a.study_id = form.data['study'].id #WORKS! 

Parece que SQLAlchemy (o posiblemente Flask-SQLAlchemy o Flask-WTF) usa una sesión para recostackr los valores en el QuerySelectField y otra sesión para crear el nuevo objeto Animal.

Si intenta adjuntar el objeto StudyGroup al Animal usando backref (Animal.group), se encontrará con este problema (ya que los objetos asociados con diferentes sesiones). La solución que estoy usando es establecer la clave externa (Animal.study_id) directamente.

Claramente llego un poco tarde a la fiesta con esta respuesta, ¡pero espero que ayude a alguien!

Eso es raro. ¿Por qué está creando el motor y la base declarativa explícitamente si está utilizando flask-sqlalchemy? Es probable que sea tu problema. Parece que tienes dos motores y sesiones ejecutándose al mismo tiempo, por eso obtuviste el error.

En lugar de crear el motor explícitamente, debe usar solo:

 from flask.ext.sqlalchemy import SQLAlchemy db = SQLAlchemy() 

Y dentro de tu fábrica de aplicaciones:

 app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///fundmanager_devel.db' db.init_app(app) 

Entonces su modelo declarativo básico es db.Model , su sesión está en db.session , y debe dejar que el matraz solicite la creación de la sesión en el contexto.

Verifique el ejemplo de aplicación mínima en los documentos de Flask-SQLAlchemy:

http://pythonhosted.org/Flask-SQLAlchemy/quickstart.html#a-minimal-application

Así lo recomienda SQLAlchemy:

La mayoría de los marcos web incluyen infraestructura para establecer una única sesión, asociada con la solicitud, que está correctamente construida y demolida al final de una solicitud. Dichas piezas de infraestructura incluyen productos como Flask-SQLAlchemy, para uso en conjunto con el marco web de Flask, y Zope-SQLAlchemy, para uso en conjunto con los marcos de Pyramid y Zope. SQLAlchemy recomienda encarecidamente que estos productos se utilicen como disponibles.

http://docs.sqlalchemy.org/en/rel_0_9/orm/session.html