在 Rails 迁移中具有 has_many 的唯一性(不通过)

Uniqueness with has_many on Rails migration (without through)

我正在为省份和国家/地区创建 Rails 迁移。这个想法是,对于每个国家,我们不能允许多个省份同名。
我的 create_provinces 迁移是:

class CreateProvinces < ActiveRecord::Migration
def change
    create_table :provinces do |t|
      t.string :name
      t.references :country, index: true, foreign_key: true
    end
end


我的 country.rb 是:

class Country < ActiveRecord::Base
  has_many :provinces, :uniq => true
end

我的province.rb是:

class Province < ActiveRecord::Base
  belongs_to :country
  private
  validates_presence_of :country
  validate :canada_or_usa?
  validates_presence_of :name
  validate :in_right_country?
  validates_associated :country
  def canada_or_usa?
    errors.add(:country, "can only add province for Canada or the United States") unless (country.name == "Canada" || country.name == "United States")
  end
  def in_right_country?
    if country.name == "Canada"
      errors.add(:name, "Name must be the name of a province in Canada") unless (DataHelper::canada_provinces_with_caption.include? name)
    end
    if country.name == "United States"
      errors.add(:name, "Name must be the name of a province in the United States") unless (DataHelper::usa_provinces_with_caption.include? name)
    end
  end
end

在 country.rb 中使用 :uniq => true,我收到错误消息:uniq 不是已知密钥。请注意,根据其他问题,我也没有使用 through 。有没有办法确保每个国家不能有两个同名的省份?

在您的省模型中您需要添加

validates uniqueness: { scope: :country }

这将允许您拥有同名的省份,但不在一个国家内。

首先,:uniq => true 只会伪造数据的唯一性。当通过 some_country_instance.provinces 获取省份时,这会在查询中添加一个 DISTINCT 子句。有了它,您可以为一个国家插入两个同名的省份。我建议你删除它。你不想被骗。

您实际上应该做的是将 [country_id、province_name] 列添加为 table 个省份的主键。

您可以使用以下代码通过迁移完成此操作:

...
def change
   add_index :provinces, [:country_id, :name], unique: true
end
...

我假设您的 table provincescountry_id 作为 countries 的外键列,并且 name 列包含省份名称。

为了验证模型中的数据,我建议:

...
validates :name, uniqueness: { scope: :country_id,
           message: "My world my rules. Country cannot have provinces with same name." }
...

有了它,您将用几行代码优雅地完成您的工作,并充分利用 Rails 框架。

正确的语法是

has_many :provinces, -> { uniq }

然而,这只是定义了一个关联,并没有真正验证数据库的输入。如果你看 sql 你会看到类似的东西。

Select distinct provinces from provinces where provinces.country_id = '';