Skip to main content
 首页 » 编程设计

python之使用 SQLAlchemy 在关联表中插入数据时出现 IntegrityError

2025年05月04日21mq0036

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

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: (raised as a result of Query-invoked autoflush; consider using a session.no_autoflush block if this flush is occurring prematurely) (sqlite3.IntegrityError) NOT NULL constraint failed: association.team_id [SQL: u'INSERT INTO association (user_id, role) VALUES (?, ?)'] [parameters: (1, u'point guard')]

你知道我该如何解决吗?

请您参考如下方法:

导致 autoflush 的行是

t.users.append(a) 

你还没有定义你的 relationship loading strategies , 所以它们默认为 "select" .由于您是第一次访问 Team.users 关系属性,它将发出一个 SELECT 以获取相关对象。

您还间接地对 session 进行了更改

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

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

为了保持数据库和 session 的状态一致,SQLAlchemy 必须 flush您在发出查询之前对 DB 所做的更改,因此半初始化的 Association 在与 Team 实例关联之前被发送到 DB。

可能的解决方案:

  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. 一个不太明显的解决方案是 eager load获取 Team 实例时关系的内容,因此不需要 SELECT:

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

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

注意

db.session.add(t) 

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