在Rails 7模式中错误的postgresql约束



我在使用Postgresql 13的Rails 7应用程序中进行了此迁移:

class AddContractsDateRangeConstraint < ActiveRecord::Migration[7.0]
def up
execute "CREATE EXTENSION IF NOT EXISTS btree_gist"
execute <<~SQL
ALTER TABLE contracts
ADD CONSTRAINT date_overlap_exclude
EXCLUDE USING GIST (
employee_id WITH =,
daterange(starts_on, ends_on) WITH &&
)
SQL
end
def down
execute "ALTER TABLE contracts DROP CONSTRAINT date_overlap_exclude"
end
end

当通过rails db:migrate执行时,约束像这样被添加到DB中,一切都工作:

app_development=# d+ contracts
...
Indexes:
...
"date_overlap_exclude" EXCLUDE USING gist (employee_id WITH =, daterange(starts_on, ends_on) WITH &&)
...

从Rails生成的schema.rb看起来像这样:

create_table "contracts", force: :cascade do |t|
# ...
t.index "employee_id, daterange(starts_on, ends_on)", name: "date_overlap_exclude", using: :gist
# ...
end

这看起来很可疑,因为它缺少我约束的整个EXCLUDE部分。实际上,当使用rails db:schema:load从生成的模式创建数据库时,约束被打破,生成如下:

app_development=# d+ contracts
...
Indexes:
"contracts_pkey" PRIMARY KEY, btree (id)
"date_overlap_exclude" gist (employee_id, daterange(starts_on, ends_on))

当然,整个约束不再有效。知道怎么解吗?

Rails Ruby模式转储器对于简单的应用程序和用例工作得非常好,但是它实际上只能理解SQL的一个非常有限的子集。正如您在这里所经历的那样,数据库模式中任何无法理解的部分都将在翻译中丢失。

一旦你需要使用函数、约束、物化视图或任何其他数据库特定的特性,你就需要使用SQL模式转储,它的保真度要高得多,因为它只是使用数据库自己的转储工具将模式输出为SQL。当您失去模式的简单性和与数据库无关的特性时。随着应用程序复杂度的增加,这是不可避免的变化。

您可以在配置中使用简单的一行来切换模式格式:

module YourApp
class Application < Rails::Application

# Add this line:
config.active_record.schema_format = :sql
end
end

当你运行rails db:schema:dump时,它现在会输出一个structure.sql文件。此时,您应该删除schema.rb并将structure.sql检入版本控制。

:

  • 使用结构的利弊。sql在你的Ruby on Rails应用程序
  • Rails指南:Schema dump and You

最新更新