【问题标题】:Cannot insert NULL as Primary Key value in Postgresql for Flask Sqlalchemy after switching from SQlite从 SQlite 切换后,无法在 Flask Sqlalchemy 的 Postgresql 中插入 NULL 作为主键值
【发布时间】:2017-12-30 14:36:51
【问题描述】:

我最近开始将 SQLite 数据库移植到 PostGreSQL,用于使用 SQLAlchemy 构建的 Flask 站点。我在 PGSQL 中有我的模式,甚至将数据插入到数据库中。但是,我无法运行常用的 INSERT 命令将信息添加到数据库。通常,我使用 SQL Alchemy 插入新记录,方法是将 ID 列保留为 NULL,然后只设置其他列。但是,这会导致以下错误:

sqlalchemy.exc.IntegrityError: (psycopg2.IntegrityError) null value in column "id" violates not-null constraint
DETAIL:  Failing row contains (null, 2017-07-24 20:40:37.787393+00, 2017-07-24 20:40:37.787393+00, episode_length_list = [52, 51, 49, 50, 83]

sum_length = 0

for ..., 0, f, 101, 1, 0, 0, , null).
 [SQL: 'INSERT INTO submission (date_created, date_modified, code, status, correct, assignment_id, course_id, user_id, assignment_version, version, url) VALUES (CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, %(code)s, %(status)s, %(correct)s, %(assignment_id)s, %(course_id)s, %(user_id)s, %(assignment_version)s, %(version)s, %(url)s) RETURNING submission.id'] [parameters: {'code': 'episode_length_list = [52, 51, 49, 50, 83]\n\nsum_length = 0\n\nfor episode_length in episode_length_list:\n    pass\n\nsum_length = sum_length + episode_length\n\nprint(sum_length)\n', 'status': 0, 'correct': False, 'assignment_id': 101, 'course_id': None, 'user_id': 1, 'assignment_version': 0, 'version': 0, 'url': ''}]

这是我的 SQL Alchemy 表声明:

class Base(Model):
    __abstract__  = True
    @declared_attr
    def __tablename__(cls):
        return cls.__name__.lower()
    def __repr__(self):
        return str(self)

    id =  Column(Integer(), primary_key=True)
    date_created  = Column(DateTime, default=func.current_timestamp())
    date_modified = Column(DateTime, default=func.current_timestamp(),
                                     onupdate=func.current_timestamp())

class Submission(Base):
    code = Column(Text(), default="")
    status = Column(Integer(), default=0)
    correct = Column(Boolean(), default=False)
    assignment_id = Column(Integer(), ForeignKey('assignment.id'))
    course_id = Column(Integer(), ForeignKey('course.id'))
    user_id = Column(Integer(), ForeignKey('user.id'))
    assignment_version = Column(Integer(), default=0)
    version = Column(Integer(), default=0)
    url = Column(Text(), default="")

我通过在脚本中调用 db.create_all() 创建了架构。

查看PostGreSQL端,我们可以看到构造好的表:

                                     Table "public.submission"
       Column       |           Type           | Modifiers | Storage  | Stats target | Description
--------------------+--------------------------+-----------+----------+--------------+-------------
 id                 | bigint                   | not null  | plain    |              |
 date_created       | timestamp with time zone |           | plain    |              |
 date_modified      | timestamp with time zone |           | plain    |              |
 code               | text                     |           | extended |              |
 status             | bigint                   |           | plain    |              |
 correct            | boolean                  |           | plain    |              |
 assignment_id      | bigint                   |           | plain    |              |
 user_id            | bigint                   |           | plain    |              |
 assignment_version | bigint                   |           | plain    |              |
 version            | bigint                   |           | plain    |              |
 url                | text                     |           | extended |              |
 course_id          | bigint                   |           | plain    |              |
Indexes:
    "idx_16881_submission_pkey" PRIMARY KEY, btree (id)
Foreign-key constraints:
    "submission_course_id_fkey" FOREIGN KEY (course_id) REFERENCES course(id)
    "submission_user_id_fkey" FOREIGN KEY (user_id) REFERENCES "user"(id)
Has OIDs: no

我还是新手,但不应该有一个序列吗?

任何关于下一步寻找什么的见解或建议都将非常感激。

【问题讨论】:

  • 请提供一个可验证的例子。默认情况下,在 SQLAlchemy 中,整数类型的单列主键将具有自动递增行为。使用您的自定义基础,一个不带外键的精简版提交,以及对db.create_all() 的调用将按应有的方式创建表。您的主键索引的名称似乎也很可疑。我认为默认命名约定不会添加“idx_16881”前缀。
  • 这是正确的建议。我最终验证(通过日志)SQLAlchemy WAS 生成序列列。但是,我用来加载旧数据的工具正在清除一些设置。从等式中删除工具解决了我的问题。谢谢你的建议!
  • 很高兴你知道发生了什么:)

标签: postgresql sqlite sqlalchemy flask-sqlalchemy


【解决方案1】:

PRIMARY KEYUNIQUENOT NULL,这是标准 SQL。 PostgreSQL 强制执行该标准,并且 not 是否允许您在桌面上拥有 any(不是一个)NULL。其他数据库允许您拥有一个 NULL,因此行为不同。

PostgreSQL current documentation on Primary Keys 明确指出:

5.3.4。主键

主键约束表示一列或一组列可以用作表中行的唯一标识符。这要求值是唯一且不为空

如果您希望您的PRIMARY KEY 是合成(即:非自然)序列号,您应该使用BIGSERIAL 类型而不是BIGINT 来定义它。我不知道如何使用 SQLAlchemy 实现这一点的详细信息,但请查看参考资料。

当您随后将INSERT 放入表中时,id 应该 NOTINSERT 列列表中(它不应设置为 null,只是 not时间>在那里)。即:

这将生成一个新的 id:

INSERT INTO public.submission (code) VALUES ('Some code') ;

会起作用的。

这不会:

INSERT INTO public.submission (id, code) VALUES (NULL, 'Some code') ;

我猜 SQLAlchemy 应该足够聪明一旦正确配置,就可以生成正确的 SQL INSERT 语句。


参考:

【讨论】:

  • 好的,但是我需要做什么才能正确配置它?该参考资料似乎没有说明我做错了什么。那张海报正在定义他们自己的序列,我认为我不应该这样做。它不应该工作吗?
  • @AustinCoryBart “那张海报正在定义他们自己的序列”,以便解释当你这样做时会发生什么。他们还展示了如何正确地做到这一点,即define a single-column primary key of an integer type, which will have autoincrementing behaviour by default
  • 结束这个谜团,是我的错。 SQLAlchemy 做了正确的事情,我只是通过使用 pgloader 覆盖了它的工作。基本上我是个白痴。
  • @AustinCoryBart:你和我们其他人一样是人类。如果您认为自己有答案(可能是您自己的答案),请将其标记为已接受,以便人们可以专注于大量 未回答 的问题。
  • 我发布了所发生情况的概要,但它不允许我再将其标记为已批准 2 天:/
【解决方案2】:

最终,我发现了问题所在,这绝对是我的错。我用来将旧数据加载到数据库 (pgloader) 的过程不仅仅是加载数据——它以某种方式覆盖了部分表定义!我能够将数据 pg_dump 出来,重置表,然后将其重新加载 - 一切都按预期工作。感谢您的健全性检查!

【讨论】:

    猜你喜欢
    • 2016-07-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多