【问题标题】:SQLAlchemy Object already attached to sessionSQLAlchemy 对象已附加到会话
【发布时间】:2014-08-09 03:01:06
【问题描述】:

我正在尝试让应用程序运行的服务器,但我在登录时遇到错误:

[!] Object '<User at 0x7f12bc185a90>' is already attached to session '2' (this is '3')

看来我要添加的会话已经在数据库中了。这是导致问题的代码的 sn-p:

@app.route('/login', methods=['POST'])
def login():
    u = User.query.filter(User.username == request.form["username"]).first()
    if not u or u.password != request.form["password"]:
        return error("E1")

    s = Session.get_by_user(u)
    if s is not None:
         db_session.delete(s)
         db_session.commit()

     print db_session.execute("SELECT * FROM sessions").fetchall()

     s = Session(u)
     db_session.add(s)
     db_session.commit()

     return jsonify(s.values)

如您所见,在尝试添加任何内容之前,我正在打印会话表中的内容,但它是空的! ([])

还有什么可能导致这种情况?

这里是“会话”的实现:

class Session(Base):
__tablename__ = "sessions"
id = Column(Integer, primary_key=True)
user_id = Column(Integer, ForeignKey('users.id'), unique=True)
user = relationship(User)
key = Column(String(50), unique=True)
created = Column(DateTime)

def __init__(self, user=None):
    self.user = user
    self.key = base64.encodestring(os.urandom(24)).strip()
    self.created = datetime.now()

def __repr__(self):
    return '<Session %r>' % (self.key)

@property
def values(self):
    return {"username" : self.user.username,
            "key" : self.key,
            "created" : str(self.created),
            }
@classmethod
def get_by_key(cls, key):
    s = cls.query.filter(cls.key == key).first()
    #print datetime.now() - s.created
    if s and datetime.now() - s.created > settings.SESSION_LIFETIME:
        s = None
    return s

@classmethod
def get_by_user(cls, user):
    s = cls.query.filter(cls.user == user).first()
    if s and datetime.now() - s.created > settings.SESSION_LIFETIME:
        s.query.delete()
        db_session.commit()
        s = None
    return s

【问题讨论】:

    标签: python session python-2.7 sqlalchemy flask-sqlalchemy


    【解决方案1】:

    此错误表示您正在处理的记录附加到 2 个不同的会话(db)!

    其中一个原因是您可以使用一个 db = SQLAlchemy(app) 定义您的模型并使用另一个添加/插入/修改数据库!

    我的解决方案是统一数据库!

    试试这个:

    u = db.session.query(User).filter(User.username == request.form["username"]).first()
    

    而不是这个:

    u = User.query.filter(User.username == request.form["username"]).first()
    

    【讨论】:

      【解决方案2】:

      我也有这个问题。 我创建了一个 test_file.py 并添加了以下代码:

      from app import app
      from models import Tovar  
      from flask_sqlalchemy import SQLAlchemy
      db = SQLAlchemy(app)
      
      tovardel = Tovar.query.filter(Tovar.region == 1 and Tovar.price == 12).first()
      db.session.delete(tovardel)
      tovar = Tovar.query.filter(Tovar.region == 1 and Tovar.price == 12).first()
      print(tovar.description)
      

      当我运行代码时,我得到了这个错误:

      Object '<Tovar at 0x7f09cbf74208>' is already attached to session '1' (this is '2')
      

      问题解决:

      如果您在例如 text_file.py 和 app.py 中有 db = SQLAlchemy(app),那么您总是会遇到这个问题。你应该删除db = SQLAlchemy(app),并从应用from app import db导入db

      【讨论】:

        【解决方案3】:

        正如@marcinkuzminski 提到的,您不能添加已经附加到另一个会话的对象。但是,如果您不确定该会话源自您当前正在操作的同一线程上下文,那么仅使用 object_session() 从对象中提取原始会话是有风险的。线程安全的方法是使用 merge()

            local_object = db_session.merge(original_object)
            db_session.add(local_object)
            db_session.commit()
        

        【讨论】:

          【解决方案4】:

          如果您让 server.py 和 model.py 相互导入,则会出现此数据库会话问题

          server.py

          from flask import Flask
          import os
          import models as appmod #################### importing models here in server.py<----------
          
          app = Flask(__name__)                                  # L1
          app.config.from_object(os.environ['APP_SETTINGS'])     # L2
          app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False   # L3
          database = SQLAlchemy(app)                             # L4
          db = database                                          # L5
          
          @app.route('/item_delete/<id>', methods=['DELETE'])
          def remove_method(id = None):
              data_rec = appmod.Employee.query.get(id)    
              db.session.delete(data_rec)
              db.session.commit()
              return "DELETE"
          
          if __name__ == '__main__':
          
              app.run(port=5000, host='0.0.0.0',debug=True,threaded=True)
          

          models.py

          from server import db #################### importing server in models.py here <------------
          from sqlalchemy.dialects.mysql import JSON
          
          
          class Employee(db.Model):
              __tablename__ = 'employe_flask'
          
              id = db.Column(db.Integer, primary_key=True)
              name = db.Column(db.String(128))
              datetime = db.Column(db.DateTime)
              designation = db.Column(db.String(128))
          
          
              def __init__(self, name, datetime, designation):
                  self.name = name
                  self.datetime = datetime
                  self.designation = designation
          
              @staticmethod
              def delete_rec(data_rec):
                  db.session.delete(data_rec)#.delete
                  db.session.commit()
          
              def __repr__(self):
                  record = {"name":self.name,"date":self.datetime.ctime(),"designation":self.designation}.__str__()
                  return record
          

          从 server.py 中删除行 L1 到 L5 并将其放置在通用文件中,如 settings.py 并将'app'和'db'导入server.py并在models.py中导入db

          喜欢下面的文件

          server.py

          from flask import Flask
          import os
          import models as appmod 
          from settings import app, db
          
          
          @app.route('/item_delete/<id>', methods=['DELETE'])
          def remove_method(id = None):
              data_rec = appmod.Employee.query.get(id)    
              db.session.delete(data_rec)
              db.session.commit()
              return "DELETE"
          
          if __name__ == '__main__':
          
              app.run(port=5000, host='0.0.0.0',debug=True,threaded=True)
          

          settings.py

          import os
          from flask import Flask
          from flask.ext.sqlalchemy import SQLAlchemy
          
          app = Flask(__name__)                                  # L1
          app.config.from_object(os.environ['APP_SETTINGS'])     # L2
          app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False   # L3
          database = SQLAlchemy(app)                             # L4
          db = database                                          # L5
          

          models.py

          from settings import db
          from sqlalchemy.dialects.mysql import JSON
          
          
          class Employee(db.Model):
              __tablename__ = 'employe_flask'
          
              id = db.Column(db.Integer, primary_key=True)
              name = db.Column(db.String(128))
              datetime = db.Column(db.DateTime)
              designation = db.Column(db.String(128))
          
          
              def __init__(self, name, datetime, designation):
                  self.name = name
                  self.datetime = datetime
                  self.designation = designation
          
              @staticmethod
              def delete_rec(data_rec):
                  db.session.delete(data_rec)#.delete
                  db.session.commit()
          
              def __repr__(self):
                  record = {"name":self.name,"date":self.datetime.ctime(),"designation":self.designation}.__str__()
                  return record
          

          【讨论】:

          • 这个答案也解决了 not update 的问题,db.session.commit() not update util apply this solution
          【解决方案5】:

          您尝试修改的对象已附加到另一个会话。 也许你有错误的导入,而 db_session 是一个新实例。

          一个很好的解决方法是提取当前绑定的会话并使用它:

          代替:

          db_session.add(s)
          

          做:

          current_db_sessions = db_session.object_session(s)
          current_db_sessions.add(s)
          

          【讨论】:

          • 在这种情况下s 是什么?插入的对象?我们如何通过传入一个对象来获取当前会话??
          • s 是来自查询的对象,例如 s = db_session.query(OrmObject).first()
          • 在执行此操作之前,您不需要确保您在同一个线程中吗?
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2021-11-27
          • 1970-01-01
          • 2018-01-08
          • 1970-01-01
          • 1970-01-01
          • 2020-02-12
          • 1970-01-01
          相关资源
          最近更新 更多