从对象初始化时,“ flush”失败

时间:2019-06-16 16:56:22

标签: flask-sqlalchemy python-3.7 mysql-8.0

我目前在使用flush下的SQLAlchemy commit的{​​{1}}(或依赖于它的session)方法时遇到永久性问题

Flask Alchemy部分始终因flush而失败(以下是全栈错误)。直接运行时,来自引擎的sqlalchemy.exc.ResourceClosedError: This transaction is closed调用以及使用insert构建器检索数据的工作原理。

此外,删除某项操作正常(通过querysession.delete(model)

这是代码失败:

session.commit()

SQLAlchemy通过以下方式初始化

roles_put = Blueprint('roles_put', __name__)


@roles_put.route('<role_id>', methods=['PUT'])
def role_update(role_id):
    role = Role.query.get(role_id)
    if not role:
        role = Role.query.filter_by(name=role_id).first()
        if not role:
            raise IDNotFoundError()

    print(role)
    role.set_data(
        request.form,
        [
            'name', 'manage_user', 'manage_video', 'manage_comment', 'manage_avatar', 'manage_channel', 'manage_reward',
            'manage_role', 'manage_top', 'manage_calendar', 'manage_setting', 'validate_video', 'moderate_comment',
        ]
    )
    MainAPI.db.session.add(role)
    MainAPI.db.session.flush()
    MainAPI.db.session.commit()
    # if not role.save():
    #     raise UpdateError()
    return jsonify(role.serialize())

角色模型:

app = Flask('Name')

def init_db(app, config):
        """
        Init SQLAlchemy DB
        """
        app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
        app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://%s:%s@%s/%s' % (
            config.get('database', 'user'),
            config.get('database', 'password'),
            config.get('database', 'host'),
            config.get('database', 'database')
        )
        print(app.config['SQLALCHEMY_DATABASE_URI'])


MainApi.db = SQLAlchemy(app)

完整错误堆栈:

class RoleModel(MainApi.db.Model):
    __tablename__ = 'roles'
    LOCKED_ROLE_NAMES = ['guest', 'admin', 'logged', 'public']
    id = MainApi.db.Column(MainApi.db.Integer, primary_key=True, unique=True, autoincrement=True)
    name = MainApi.db.Column(MainApi.db.String(40), nullable=False, unique=True)

    # manage rights
    manage_user = MainApi.db.Column(MainApi.db.Boolean, nullable=False, default=False)
    moderate_comment = MainApi.db.Column(MainApi.db.Boolean, nullable=False, default=False)

    created_at = MainApi.db.Column(MainApi.db.DateTime, nullable=False, default=datetime.utcnow)
    last_updated_at = MainApi.db.Column(MainApi.db.DateTime, nullable=True)

    created_by = MainApi.db.Column(
        MainApi.db.Integer,
        MainApi.db.ForeignKey(
            'users.id', ondelete='RESTRICT', onupdate='CASCADE'
        ),
        nullable=True
    )
    last_updated_by = MainApi.db.Column(
        MainApi.db.Integer,
        MainApi.db.ForeignKey(
            'users.id', ondelete='CASCADE', onupdate='CASCADE'
        ), nullable=True
    )

    users = MainApi.db.relationship(
        'UserModel', foreign_keys='UserModel.role_id',
        back_populates='role'
    )

    @staticmethod
    def is_allowed(action, role):
        """
        Check if user having role can make action

        :param action: action name
        :type action: str
        :param role: user role
        :type role: int|str|RoleModel
        :return:
        """
        if isinstance(role, str):
            role = RoleModel.query.filter_by(name=role).first()
        elif isinstance(role, int):
            role = RoleModel.query.get(role)
        if not isinstance(role, RoleModel):
            raise Exception
        return getattr(role, action.strip().replace(' ', '_'))

    @staticmethod
    def get_role_id(role):
        """
        Check if user having role can make action

        :param role: user role
        :type role: int|str|RoleModel
        :return: role id
        :rtype: int
        """
        if isinstance(role, str):
            role = RoleModel.query.filter_by(name=role).first()
        elif isinstance(role, int):
            role = RoleModel.query.get(role)
        if not isinstance(role, RoleModel):
            raise Exception
        return role.id

    def serialize(self):
        users = [u.serialize() for u in self.users] if self.users else []
        return {
            'id':               self.id,
            'name':             self.name,
            'manage_user':      self.manage_user,
            'moderate_comment': self.moderate_comment,
            'created_at':       self.created_at,
            'last_updated_at':  self.last_updated_at,
            'created_by':       self.created_by,
            'last_updated_by':  self.last_updated_by,
            'users':            users,
        }


@event.listens_for(RoleModel.name, 'set', propagate=True)
def before_set_name(_target, value, old, _initiator):
    print(_initiator)
    print(request.url)
    if request and 'roles/init' not in request.url:
        if old in RoleModel.LOCKED_ROLE_NAMES or value in RoleModel.LOCKED_ROLE_NAMES:
            raise UnauthorizedError()


@event.listens_for(RoleModel, 'before_insert', propagate=True)
def receive_before_insert(_mapper, _connection, target):
    user = Registry.registered('current-user-id')
    target.created_at = datetime.utcnow()
    if user:
         target.created_by = user


@event.listens_for(RoleModel, 'before_update', propagate=True)
def receive_before_update(_mapper, _connection, target):
    user = Registry.registered('current-user-id')
    target.updated_at = datetime.utcnow()
    if user:
       target.updated_by = user

版本:

  • Python 3.7.3(VENV)
  • MySQL 8(码头工人)
  • Flask-SQLAlchemy == 2.4.0 / Flask-SQLAlchemy == 2.3.2(都尝试了)
  • 烧瓶== 1.0.3
  • SQLAlchemy == 1.3.4

感谢任何在这里提出建议的人。

1 个答案:

答案 0 :(得分:0)

最后解决了。

问题出在模型before_insertbefore_update回调中。 尝试从current-user-id检索Flask.g似乎对session产生了奇怪的影响。当然,这与我的Registry类实现的工作方式有关。