使用Flask-SQLAlchemy无法创建自增主键

77

我希望我的模型的主键是自增整数。这是我的模型的样子。

class Region(db.Model):
    __tablename__ = 'regions'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    name = db.Column(db.String(100))
    parent_id = db.Column(db.Integer, db.ForeignKey('regions.id'))
    parent = db.relationship('Region', remote_side=id, primaryjoin=('Region.parent_id==Region.id'), backref='sub-regions')
    created_at = db.Column(db.DateTime, default=db.func.now())
    deleted_at = db.Column(db.DateTime)

上述代码创建了我的表格,但没有使id自动递增。因此,如果在我的插入查询中缺少id字段,它将给出以下错误消息:

ERROR: null value in column "id" violates not-null constraint

因此,我将id声明更改为以下形式:

id = db.Column(db.Integer, db.Sequence('seq_reg_id', start=1, increment=1),
               primary_key=True)

依然是同样的错误。以上代码有什么问题?


你能发一下你用来创建一个区域对象的代码吗? - nothankyou
我正在尝试执行一个简单的 SQL 插入操作,例如 INSERT INTO regions(name, ) ... - lovesh
4
你的原始代码应该能够工作。你确定你实际上是在使用SQLAlchemy创建表吗?你确定你正在查看正确的数据库吗?尝试在SQLAlchemy中配置日志记录,并查看在使用SQLAlchemy创建表时输出的SQL语句。 - Mark Hildreth
非常好。你的代码解决了我的问题。我一直在寻找如何使用Oracle的序列,直到看到了你的帖子。谢谢! - Jcc.Sanabria
仍然出现相同的错误。 - Bawantha
9个回答

78

以上代码没有问题。事实上,您甚至不需要使用autoincrement=Truedb.Sequence('seq_reg_id', start=1, increment=1),因为SQLAlchemy将自动将第一个未标记为外键的Integer PK列设置为autoincrement=True

在这里,我基于您的设置构建了一个可用的配置。如果您使用了您定义的Declarative Base类来创建对象实例,SQLAlechemy的ORM将负责生成id并将其填充到对象中。

from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.debug = True
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://user:password@localhost/testdb'
app.config['SQLALCHEMY_ECHO'] = True
db = SQLAlchemy(app)

class Region(db.Model):
    __tablename__ = 'regions'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(100))

db.drop_all()
db.create_all()

region = Region(name='Over Yonder Thar')
app.logger.info(region.id) # currently None, before persistence

db.session.add(region)
db.session.commit()
app.logger.info(region.id) # gets assigned an id of 1 after being persisted

region2 = Region(name='Yet Another Up Yar')
db.session.add(region2)
db.session.commit()
app.logger.info(region2.id) # and 2

if __name__ == '__main__':
    app.run(port=9001)

4
我处于类似的情况。假设现在表格中有4条记录,从id 1到4。当我执行 db.session.query(Table_name).delete() db.session.commit() 后,如果我再次执行 db.session.add() 添加新记录,则下一条记录的id将为5。由于我的表格现在为空,我希望我的新记录从1开始获得id。我该如何实现这一点? - qre0ct
1
这是一个旁注,我试图解决一个不同但相关的问题,即如何基于model.py填充我的数据库,并发现db.drop_all()db.create_all()非常有帮助。谢谢。 - lindes
这同样适用于Oracle数据库,在那里您必须安装/设置一个序列来处理AUTOINCREMENT主键处理。 - phyatt
4
这不起作用。在“module_id”列中的空值违反了非空约束条件。 详细信息:失败的行包含... - Bawantha
2
@qre0ct 对于关系型数据库来说,这实际上是毫无意义的,因为您将无法同时添加/删除多行。每次更改ID时,您的数据库引擎都必须检查索引是否正确。ID列不应具有实际含义。当然,也有一些例外。如果您担心会用尽ID,请分配BigInt并永远忘记它。 - winwin

18

我在这里遇到了一个问题,我的SQLite表没有自动增加主键。我有一个稍微复杂的用例,在生产中想要使用postgres,但在测试时使用sqlite以使连续部署变得更容易些。

事实证明,SQLite不喜欢被定义为BigIntegers的列,为了使自增工作,它们应该被设置为Integers。令人惊讶的是,SQLAlchemy可以使用with_variant函数处理这种情况。我认为这可能对某些人很有用:

id = db.Column(db.BigInteger().with_variant(db.Integer, "sqlite"), primary_key=True)

更多细节请查看https://docs.sqlalchemy.org/en/13/dialects/sqlite.html


3
我来处理相同的问题(生产环境使用postgresql,测试环境使用sqlite),但是我的所有主键已经是整数了。有些荒谬,但下面的代码仍然可以解决问题:id = db.Column(db.Integer().with_variant(Integer, "sqlite"), primary_key=True) - fanfabbb
1
这是我问题的最佳答案(postgresql->生产环境,sqlite->测试环境)。谢谢。 - Hadi Farhadi

13
我认为一旦您进行了设置,就不需要自动递增。
id = db.Column(db.Integer , primary_key=True , autoincrement=True)
我认为应该是,
id = db.Column(db.Integer , primary_key=True)

它会给你所期望的独特性。


8
我在模型类中声明复合键时遇到了问题。
如果您想要为复合键(即具有多个db.Column(..)定义且primary_key=True)添加自增长的id字段,则添加autoincrement=True可以解决该问题。
class S3Object(db.Model):
    __tablename__ = 's3_object'

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

    # composite keys
    bucket_name = db.Column(db.String(), primary_key=True)
    key = db.Column(db.String(), primary_key=True)

因此,关于不需要autoincrement=True的陈述应该是:
你甚至不需要autoincrement=True,因为SQLAlchemy会自动将第一个未标记为外键的整数主键列设置为autoincrement=True除非你正在定义多个primary_key=True的组合键

5

默认情况下,即使没有设置autoincrement=True标志,您的id也会自动递增。

因此,使用这样的代码是没有问题的:

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

你遇到的错误是由于尝试在表格中填充一个id属性造成的。你的插入查询不应该包含任何id属性,否则你会得到那个错误。

3

我遇到了同样的错误,即使添加autoincrement=True后仍然如此。

问题是我已经创建了迁移。 所以我降级到先前的迁移,删除了迁移,再次创建迁移并进行升级。

然后错误就消失了。

希望能帮助那些卡在这个问题上的人。

总结:添加autoincrement=True,确保您的迁移已更新并应用。


1
您不能在列定义中添加 "autoincrement" 标志,此外需要在 __table__name 后面添加 "__table__args" 属性。就像这样:
    __tablename__ = 'table-name'
    __table_args__ = {'sqlite_autoincrement': True} -> This adds autoincrement to your primary key.
尝试一下,希望这对您有用 ;)!

-1

试试这段代码,它对我有效。

__init__函数中不要指定id,这样当你创建一个新的“User”对象时,SQLAlchemy会自动为你生成一个唯一的id号码。

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)

app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///users.sqlite3'
app.config['SQLAlCHEMY_TRACK_MODIFICATIONS'] = False

db = SQLAlchemy(app)

class User(db.Model):
    _id = db.Column(db.Integer, primary_key = True, autoincrement = True)
    username = db.Column(db.String(80), unique = True, nullable = False)
    email = db.Column(db.String(120), unique = True, nullable = False)

def __init__(self, username, email):
    self.username = username
    self.email = email

这行代码将在我们的数据库中创建我们想要的表格。
with app.app_context():
    db.create_all()


admin = User(username = 'another admin', email='another_admin@mail.com')
guest = User(username = 'another guest', email='another_guest@mail.com')

以下代码将把我们的数据推送到表中。

with app.app_context():
    db.session.add(admin)
    db.session.add(guest)
    db.session.commit()

SQLAlchemy已经提供了默认的__init__函数,不需要再添加一个。 - snakecharmerb

-1
在我的情况下,我只是将ID作为外部参数添加,而不依赖于SQLAlchemy。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接