【问题标题】:Why is rails 5 adding nextval method in schema file?为什么 rails 5 在模式文件中添加 nextval 方法?
【发布时间】:2016-07-29 19:09:04
【问题描述】:

升级到 Rails 5 后,我的模式文件在运行 db:migrate 时不断改变。 Rails 正在改变:

create_table "flightlessons", force: :cascade do |t|

到:

create_table "flightlessons", id: :integer, default: -> { "nextval('lessons_id_seq'::regclass)" }, force: :cascade do |t|

它只发生在这一个模型上。为什么 Rails 在这个特定模型上实现 nextval?而且,为什么模型名称错误(lessons_id_seq 应该是 flightlessons_id_seq)。但是,手动将其更改为 flightlessons_id_seq 会导致相同的无关联错误。

PG::UndefinedTable: ERROR:  relation "lessons_id_seq" does not exist

要继续,我只需将 schema.rb 文件改回“应该”的行。然后,我可以迁移或测试:准备或其他任何东西,直到下次 rails 将其改回使用 nextval 方法。

感谢您对此的任何见解。

【问题讨论】:

    标签: ruby-on-rails postgresql


    【解决方案1】:

    这个答案有点长,所以我把它分成几个部分。系好安全带!

    我的理论

    我的猜测是您的开发数据库确实包含lessons_id_seq 序列,并且它的flightlessons.id 定义设置为依赖于它(即,正是Rails 将什么放入您的架构中文件)。

    如何以及为什么?您可能在过去的某个时间点将 lessons 表重命名为 flightlessons,但重命名并没有改变表所依赖的顺序 - 而且由于 schema.rb 确实 not 记录序列,lessons_id_seq 序列不会被复制到您的测试数据库,因此您会收到此错误。

    要验证我的理论,请运行 rails db 并尝试以下命令:

    \d lessons_id_seq
    

    这应该返回该序列的定义。然后,尝试:

    \d flightlessons
    

    并查看id 列的定义。我希望它包含DEFAULT nextval('lessons_id_seq')

    修复

    解决此问题的最简单方法是改用structure.sql 而不是schema.rb(请参阅the docs)。这将继承您数据库的确切状态,并避免 Rails 的任何干扰或解释,这是导致您当前问题的原因。对于生产系统,我总是推荐 structure.sql

    但是,您也可以进入您的开发数据库并更改序列名称:

    ALTER SEQUENCE lessons_id_seq RENAME TO flightlessons_id_seq;
    ALTER TABLE flightlessons ALTER COLUMN id SET DEFAULT nextval('flightlessons_id_seq');
    

    在生产系统上这将是一个糟糕的主意,但如果您的问题只是本地问题,它应该使用您的 schema.rb 纠正您当前的数据库状态,从而解决您当前的问题。如果您希望 rails db:drop db:create db:migrate 在新的应用程序上工作,您可能希望将其编码到迁移中。

    为什么是现在?

    Rails 为您的表的主键转储 default 值的行为在 Rails 5 中很可能是新的。以前,Rails 可能只是相信您的 ID 列具有合理的默认值,而忽略了它的任何值居然看到了。但我还没有研究过这是否属实。

    【讨论】:

    • 这是我在 StackOverflow 上得到的最佳答案!我不仅对得到回应感到惊讶,而且你完全正确——而且你的解释超出了彻底。谢谢!
    • 只是想再次感谢您。您对我的问题和回答的理解给我留下了深刻的印象。极好的!有时,stackoverflow 对我的影响与 Facebook 相同,我认为“好吧,再也不会去那里了”。但是,像您这样的人使这一切变得如此伟大。
    • 很高兴听到这个消息!你的 bug 会让程序员发疯,所以我很高兴能帮上忙。
    • 非常有帮助的问答。只是为了添加一点颜色,当 rails 在 Postgresql 中创建一个表时,似乎发生的是它创建了一个名为“#{table_name}_id_seq”的序列。如果表使用其他序列来生成 id,rails 会将序列名称放在 schema.rb 中(就像它为 @hellion 和我所做的那样)。在我的情况下,我手动删除了表,然后重新创建了它,这导致第二个序列被创建并在 schema.rb 中明确列出。无论如何...谢谢!
    • @Marc 我的“可怕的想法”警告仅适用于直接在您的产品数据库上运行命令:)“重命名序列并更改 PK 的默认值”方法也应该在生产中工作,但是你'需要确保问题确实存在于生产中(Rails 不会强制您的 dev 架构实际上与您的 prod 架构匹配,因此 OP 所经历的症状很容易只是他们机器的本地)。如果您可以确认,那么重命名架构并更新表以使用新架构的迁移应该可以解决问题。
    【解决方案2】:

    最简单的解决方法是重命名生产中的序列以匹配当前表名。例如。在生产 Rails 控制台中:

    ActiveRecord::Base.connection.execute("ALTER SEQUENCE lessons_id_seq RENAME TO flightlessons_id_seq;")
    

    如果你没有对序列做任何花哨的事情(比如实现你自己的通过名称引用它的 Postgres 函数),那么重命名它就可以了。

    显然,该表按 ID 而非名称指向序列,因此重命名是即时的,并且没有我们可以看到的不良影响。更多细节在这里:https://dba.stackexchange.com/questions/265569/how-can-i-safely-rename-a-sequence-in-postgresql-ideally-without-downtime

    我们首先在 staging 上进行了尝试,并验证了在 staging 和 production 进行更改后 ID 序列一直在滴答作响。一切正常。

    (有关正在发生的事情的更多详细信息,请参阅 Robert Nubel 的精彩回答。)

    【讨论】:

      猜你喜欢
      • 2010-10-31
      • 1970-01-01
      • 1970-01-01
      • 2014-02-14
      • 2019-03-29
      • 2018-05-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多