【问题标题】:IntegrityError when inserting data in an association table using SQLAlchemy使用 SQLAlchemy 在关联表中插入数据时出现 IntegrityError
【发布时间】:2018-06-19 16:42:54
【问题描述】:

我正在尝试在此关联表中插入角色数据:

class Association(db.Model):
    __tablename__ = 'associations'
    user_id = db.Column(db.Integer, db.ForeignKey('users.id'), primary_key=True)
    team_id = db.Column(db.Integer, db.ForeignKey('teams.id'), primary_key=True)
    role = db.Column(db.String)
    user = db.relationship("User", back_populates="teams")
    team = db.relationship("Team", back_populates="users")

它链接到另外 2 个表:

class User(db.Model, UserMixin):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String)
    username = db.Column(db.String, nullable=False, unique=True, index=True)
    password_hash = db.Column(db.String)
    teams = db.relationship("Association", back_populates="user")

class Team(db.Model):
    __tablename__ = 'teams'
    id = db.Column(db.Integer, primary_key = True)
    name = db.Column(db.String)
    player = db.Column(db.String)
    users = db.relationship("Association", back_populates="team")

但是,当我尝试在关联表中插入角色时:

t = Team.query.filter_by(id=team_name1_id).first()
a = Association(role=form.role.data)
a.user = User.query.filter_by(id=current_user.id).first()
t.users.append(a)
db.session.add(t)
db.session.commit()

我得到这个错误:

IntegrityError:(由于查询调用自动刷新而引发; 如果发生此刷新,请考虑使用 session.no_autoflush 块 过早地)(sqlite3.IntegrityError)NOT NULL 约束失败: Association.team_id [SQL: u'INSERT INTO 关联(user_id,角色) VALUES (?, ?)'] [参数:(1, u'point guard')]

你知道我该如何解决吗?

【问题讨论】:

标签: python sqlalchemy


【解决方案1】:

导致autoflush 的行是

t.users.append(a)

您尚未定义您的relationship loading strategies,因此它们默认为"select"。由于您是第一次访问 Team.users 关系属性,它会发出一个 SELECT 以获取相关对象。

您还间接更改了会话

a.user = User.query.filter_by(id=current_user.id).first()

由于save-update cascade,默认情况下,将新的Association 实例也放置到会话中。

为了保持数据库和会话的状态一致,SQLAlchemy 必须在发出查询之前flush 你对数据库的更改,因此在与@987654336 关联之前将初始化的一半Association 发送到数据库@实例。

可能的解决方案:

  1. Temporarily disable the autoflush feature 用于该特定附加,因为您知道您正在添加一个新的Association

    with db.session.no_autoflush:
        t.users.append(a)
    

    但仍会发出 SELECT。

  2. 反转操作。不要将Association 实例附加到Team.users 集合,而是将Team 分配给Association.team

    # Instead of t.users.append(a):
    a.team = t  # Mr.
    
  3. 一个不太明显的解决方案是在获取 Team 实例时将关系的内容设置为 eager load,这样就不需要 SELECT:

    t = db.session.query(Team).options(db.joinedload(Team.users)).first()
    

    或者使用db.noload(Team.users) 选项根本不加载它们。

注意

db.session.add(t)

是多余的。获取的Team 实例已经在会话中——否则Team.users 的延迟加载首先是could not have proceeded

【讨论】:

  • 我将“t.users.append(a)”替换为“a.team = t”,效果很好……谢谢!
猜你喜欢
  • 2021-06-03
  • 1970-01-01
  • 2021-08-23
  • 2016-04-04
  • 1970-01-01
  • 2020-02-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多