Flask SQLAlchemy NOT NULL 约束在主键上失败

Posted

技术标签:

【中文标题】Flask SQLAlchemy NOT NULL 约束在主键上失败【英文标题】:Flask SQLAlchemy NOT NULL constraint failed on primary key 【发布时间】:2016-04-07 12:28:43 【问题描述】:

我正在尝试在 SQLite 中创建一个数据库,该数据库有两个表,一个用于机场列表,另一个用于这些机场对之间的旅行列表。我已将其设置为自引用的多对多关系:

class Trips(db.Model):

    __tablename__ = 'trips'

    id = db.Column(db.Integer, primary_key=True)
    airport_from = db.Column(db.Integer, db.ForeignKey('airport.id'))
    airport_to = db.Column(db.Integer, db.ForeignKey('airport.id'))
    price = db.Column(db.Float)
    date = db.Column(db.Date)

class Airport(db.Model):

    __tablename__ = 'airport'

    id = db.Column(db.Integer, primary_key=True)
    iata = db.Column(db.String(8), index=True, unique=True)
    name = db.Column(db.String(120), index=True, unique=True)
    city = db.Column(db.String(120))
    region = db.Column(db.String(120))
    country = db.Column(db.String(120))

    flying_from = db.relationship('Trips', backref='end', primaryjoin=(id==Trips.airport_to))
    flying_to = db.relationship('Trips', backref='start', primaryjoin=(id==Trips.airport_from))

    def __repr__(self):
        return '<Airport: 0; IATA: 1>'.format(self.name, self.iata)

当我打开我的 Python shell 并导入这些模型时,我让 SQLAlchemy 会话添加 Airport 对象并提交就好了,但是当我执行以下操作时:

>>> t = models.Trips(airport_from=3, airport_to=4, price=230.0)
>>> db.session.add(t)
>>> db.session.commit()

它给了我这个回溯:

Traceback (most recent call last):
  File "/Users/heli/nomad/flask/lib/python3.4/site-packages/sqlalchemy/engine/base.py", line 1139, in _execute_context
context)
  File "/Users/heli/nomad/flask/lib/python3.4/site-packages/sqlalchemy/engine/default.py", line 450, in do_execute
cursor.execute(statement, parameters)
sqlite3.IntegrityError: NOT NULL constraint failed: trips.id

上述异常是以下异常的直接原因:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/heli/nomad/flask/lib/python3.4/site-packages/sqlalchemy/orm/scoping.py", line 150, in do
    return getattr(self.registry(), name)(*args, **kwargs)
  File "/Users/heli/nomad/flask/lib/python3.4/site-packages/sqlalchemy/orm/session.py", line 813, in commit
    self.transaction.commit()
  File "/Users/heli/nomad/flask/lib/python3.4/site-packages/sqlalchemy/orm/session.py", line 392, in commit
    self._prepare_impl()
  File "/Users/heli/nomad/flask/lib/python3.4/site-packages/sqlalchemy/orm/session.py", line 372, in _prepare_impl
    self.session.flush()
  File "/Users/heli/nomad/flask/lib/python3.4/site-packages/sqlalchemy/orm/session.py", line 2027, in flush
    self._flush(objects)
  File "/Users/heli/nomad/flask/lib/python3.4/site-packages/sqlalchemy/orm/session.py", line 2145, in _flush
    transaction.rollback(_capture_exception=True)
  File "/Users/heli/nomad/flask/lib/python3.4/site-packages/sqlalchemy/util/langhelpers.py", line 60, in __exit__
    compat.reraise(exc_type, exc_value, exc_tb)
  File "/Users/heli/nomad/flask/lib/python3.4/site-packages/sqlalchemy/util/compat.py", line 183, in reraise
    raise value
  File "/Users/heli/nomad/flask/lib/python3.4/site-packages/sqlalchemy/orm/session.py", line 2109, in _flush
    flush_context.execute()
  File "/Users/heli/nomad/flask/lib/python3.4/site-packages/sqlalchemy/orm/unitofwork.py", line 373, in execute
    rec.execute(self)
  File "/Users/heli/nomad/flask/lib/python3.4/site-packages/sqlalchemy/orm/unitofwork.py", line 532, in execute
    uow
  File "/Users/heli/nomad/flask/lib/python3.4/site-packages/sqlalchemy/orm/persistence.py", line 174, in save_obj
    mapper, table, insert)
  File "/Users/heli/nomad/flask/lib/python3.4/site-packages/sqlalchemy/orm/persistence.py", line 800, in _emit_insert_statements
    execute(statement, params)
  File "/Users/heli/nomad/flask/lib/python3.4/site-packages/sqlalchemy/engine/base.py", line 914, in execute
    return meth(self, multiparams, params)
  File "/Users/heli/nomad/flask/lib/python3.4/site-packages/sqlalchemy/sql/elements.py", line 323, in _execute_on_connection
    return connection._execute_clauseelement(self, multiparams, params)
  File "/Users/heli/nomad/flask/lib/python3.4/site-packages/sqlalchemy/engine/base.py", line 1010, in _execute_clauseelement
    compiled_sql, distilled_params
  File "/Users/heli/nomad/flask/lib/python3.4/site-packages/sqlalchemy/engine/base.py", line 1146, in _execute_context
    context)
  File "/Users/heli/nomad/flask/lib/python3.4/site-packages/sqlalchemy/engine/base.py", line 1341, in _handle_dbapi_exception
    exc_info
  File "/Users/heli/nomad/flask/lib/python3.4/site-packages/sqlalchemy/util/compat.py", line 189, in raise_from_cause
    reraise(type(exception), exception, tb=exc_tb, cause=exc_value)
  File "/Users/heli/nomad/flask/lib/python3.4/site-packages/sqlalchemy/util/compat.py", line 182, in reraise
    raise value.with_traceback(tb)
  File "/Users/heli/nomad/flask/lib/python3.4/site-packages/sqlalchemy/engine/base.py", line 1139, in _execute_context
    context)
  File "/Users/heli/nomad/flask/lib/python3.4/site-packages/sqlalchemy/engine/default.py", line 450, in do_execute
    cursor.execute(statement, parameters)
sqlalchemy.exc.IntegrityError: (sqlite3.IntegrityError) NOT NULL constraint failed: trips.id [SQL: 'INSERT INTO trips (airport_from, airport_to, price, date) VALUES (?, ?, ?, ?)'] [parameters: (3, 4, 230.0, None)]

关键部分似乎是底线:

sqlalchemy.exc.IntegrityError: (sqlite3.IntegrityError) NOT NULL constraint failed: trips.id [SQL: 'INSERT INTO trips (airport_from, airport_to, price, date) VALUES (?, ?, ?, ?)'] [parameters: (3, 4, 230.0, None)]

看起来是因为我没有给 id 参数赋值,所以它拒绝添加对象。但我认为这个 id 会自动添加和递增,就像 Airport 对象的 id 参数一样。我在这里错过了什么?

【问题讨论】:

【参考方案1】:

在 Trips 类定义中添加显式 autoincrement=True

id = db.Column(db.Integer, primary_key=True, autoincrement=True)

如果表是在没有显式 AUTOINCREMENT 的情况下创建的,则需要传递 Trips.id=NULL 以增加它,请参阅https://www.sqlite.org/faq.html#q1

【讨论】:

谢谢 D,这成功了!关于为什么我必须为 Trips 执行此操作,而不是在添加 Airport 对象时,您知道吗? IE。当我创建一个没有明确 id 的 Airport 对象时,我可以添加它并提交会话而不会出现任何问题。 好问题,他)当您创建新的 Airport 对象或 Airport 表已在 SQLite 中使用 AUTOINCREMENT 属性定义时,您是否有可能传递 Airport.id=NULL? 我仔细检查过,但我都没有做过,尽管我可能弄错了。不过问题不大,只要一切正常 我无意中偶然发现了同样的问题。我认为它首先发生在我对源文件中的表定义重新排序时。不知道。 @HeLi 也许你忘了接受答案;)?【参考方案2】:

在版本中: SQLAlchemy 1.2.7 Flask-SQLAlchemy 2.3.2 autoincrement=True 不是必需的参数,但要小心你的主键定义,因为你已经定义了 'id' 列作为主键,如果没有必要,你不应该将另一列定义为主键,否则,你必须设置插入记录时自己设置id列的值。

【讨论】:

【参考方案3】:

我在一个类映射表中遇到了同样的问题,该表被用作带有附加属性字段的多对多。弹出错误的原因不是因为没有为主键显式定义自动增量,而是因为 foreign_key 列也有 primary_key = True。如果没有在模型中设置额外的 primary_keys,这种关系就可以正常工作。

【讨论】:

以上是关于Flask SQLAlchemy NOT NULL 约束在主键上失败的主要内容,如果未能解决你的问题,请参考以下文章

flask-sqlalchemy

PyCharm 解析 - flask.ext.sqlalchemy 与 flask_sqlalchemy

Python flask-sqlalchemy初级解析

flask-sqlalchemy

Flask-SQLAlchemy 和 SQLAlchemy

Flask-SQLAlchemy使用方法