我有一个烧瓶,sqlalchemy绑在postgres db上。所有组件都使用完全功能的读取。我有一个简单的模型:
class School(db.Model):
__tablename__ = 'schools'
id = db.Column(db.Integer, db.Sequence('schools_id_seq'), primary_key=True)
name = db.Column(db.String(80))
active = db.Column(db.Boolean)
created = db.Column(db.DateTime)
updated = db.Column(db.DateTime)
def __init__(self, name, active, created, updated):
self.name = name
self.active = active
self.created = created
self.updated = updated
正在使用postgres表:
CREATE SEQUENCE schools_id_seq;
CREATE TABLE schools(
id int PRIMARY KEY NOT NULL DEFAULT nextval('schools_id_seq'),
name varchar(80) NOT NULL,
active boolean DEFAULT TRUE,
created timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
);
ALTER SEQUENCE schools_id_seq OWNED BY schools.id;
当我从psql使用此表上的插入时,一切都很好:
cake=# select nextval('schools_id_seq');
nextval
---------
65
(1 row)
cake=# INSERT INTO schools (id, name, active, created, updated) VALUES (nextval('schools_id_seq'),'Test', True, current_timestamp, current_timestamp);
INSERT 0 1
导致:
66 | Test | 0 | t | 2016-08-25 14:12:24.928456 | 2016-08-25 14:12:24.928456
但是当我尝试烧瓶中的相同插入时,堆栈跟踪会抱怨重复的id
,但它使用nextval
来获取该值:
sqlalchemy.exc.IntegrityError:(psycopg2.IntegrityError)重复键值违反了唯一约束" schools_pkey" DETAIL:Key(id)=(7)已经存在。 [SQL:" INSERT INTO学校(id,name,active,created,updated)VALUES(nextval(' schools_id_seq'),%(name)s,%(active)s,%(已创建) )s,%(已更新)s)返回学校。"] [参数:{'活跃':正确,'名称':' Testomg', '更新':datetime.datetime(2016,8,25,14,10,5,703471),'创建':datetime.datetime(2016,8,25,14,10) ,5,703458)}]
为什么sqlalchemy对nextval
的调用不会返回与postgres db中的同一调用相同的下一个val?
更新:@RazerM告诉我有关我不知道的echo = true param。随着
app.config['SQLALCHEMY_ECHO']=True
我从一个新的插页中得到了(请注意,在这次尝试中它获取了10
,应该是67
):
2016-08-25 14:47:40,127 INFO sqlalchemy.engine.base.Engine select version()
2016-08-25 14:47:40,128 INFO sqlalchemy.engine.base.Engine {}
2016-08-25 14:47:40,314 INFO sqlalchemy.engine.base.Engine select current_schema()
2016-08-25 14:47:40,315 INFO sqlalchemy.engine.base.Engine {}
2016-08-25 14:47:40,499 INFO sqlalchemy.engine.base.Engine SELECT CAST('test plain returns' AS VARCHAR(60)) AS anon_1
2016-08-25 14:47:40,499 INFO sqlalchemy.engine.base.Engine {}
2016-08-25 14:47:40,594 INFO sqlalchemy.engine.base.Engine SELECT CAST('test unicode returns' AS VARCHAR(60)) AS anon_1
2016-08-25 14:47:40,594 INFO sqlalchemy.engine.base.Engine {}
2016-08-25 14:47:40,780 INFO sqlalchemy.engine.base.Engine show standard_conforming_strings
2016-08-25 14:47:40,780 INFO sqlalchemy.engine.base.Engine {}
2016-08-25 14:47:40,969 INFO sqlalchemy.engine.base.Engine BEGIN (implicit)
2016-08-25 14:47:40,971 INFO sqlalchemy.engine.base.Engine INSERT INTO schools (id, name, active, created, updated) VALUES (nextval('schools_id_seq'), %(name)s, %(active)s, %(created)s, %(updated)s) RETURNING schools.id
2016-08-25 14:47:40,971 INFO sqlalchemy.engine.base.Engine {'name': 'Testing', 'created': datetime.datetime(2016, 8, 25, 14, 47, 38, 785031), 'active': True, 'updated': datetime.datetime(2016, 8, 25, 14, 47, 38, 785050)}
2016-08-25 14:47:41,064 INFO sqlalchemy.engine.base.Engine ROLLBACK
sqlalchemy.exc.IntegrityError:(psycopg2.IntegrityError)重复键值违反了唯一约束" schools_pkey" DETAIL:Key(id)=(10)已经存在。 [SQL:" INSERT INTO学校(id,name,active,created,updated)VALUES(nextval(' schools_id_seq'),%(name)s,%(active)s,%(已创建) )s,%(更新)s)返回schools.id"] [参数:{'更新&#39 ;: datetime.datetime(2016,8,25,14,54,18,262873),& #39;已创建':datetime.datetime(2016,8,25,14,54,18,262864),'活跃':正确,'名称':' ;测试'}]
答案 0 :(得分:0)
序列总是递增,因此select语句和SQLAlchemy都会增加值。
如Sequence Manipulation Functions中所述:
将序列对象前进到其下一个值并返回该值。这是以原子方式完成的:即使多个会话同时执行nextval,每个会话也会安全地接收一个不同的序列值。
如果使用默认参数创建了序列对象,则连续的nextval调用将返回以1开头的连续值。通过使用CREATE SEQUENCE命令中的特殊参数可以获得其他行为。有关详细信息,请参阅其命令参考页。
重要事项:为了避免阻止从同一序列获取数字的并发事务,永远不会回滚nextval操作;也就是说,一旦获取了一个值,就会被认为是使用过的,并且不会再次返回。即使周围的事务稍后中止,或者调用查询最终没有使用该值,也是如此。例如,具有ON CONFLICT子句的INSERT将计算要插入的元组,包括执行任何所需的nextval调用,然后检测任何会导致其遵循ON CONFLICT规则的冲突。这样的情况会留下未使用的空洞"按指定值的顺序。因此,PostgreSQL序列对象不能用于获得"无间隙"序列强>
答案 1 :(得分:0)
那么,在这种情况下,解决方案很简单,它无法解释原因,因为我认为我们应该关注整个环境,这些环境无法向我们展示,也不会花费太长时间。因此,尝试插入尽可能多的记录,因为它将达到67并且下一个插入应该应用而没有任何错误,因为序列最小值将达到适当的值。当然,您可以尝试首先向server_default
属性添加id
选项:
server_default=db.Sequence('schools_id_seq').next_value()
所以
seq = db.Sequence('schools_id_seq')
在课堂上:
id = db.Column(db.Integer, seq, server_default=seq.next_value(), primary_key=True)
Sqlalchemy以这种方式提到这一点:
Sequence最初的目的是首先是Python端指令,所以以这种方式指定它可能是一个好主意。