防止Rails 5中重复的has_many记录

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了防止Rails 5中重复的has_many记录相关的知识,希望对你有一定的参考价值。

鉴于以下型号:

class Client < ApplicationRecord
  has_many :preferences
  validates_associated :preferences
  accepts_nested_attributes_for :preferences
end

class Preference < ApplicationRecord
  belongs_to :client
  validates_uniqueness_of :block, scope: [:day, :client_id]
end

在客户端创建期间创建一批首选项时,我仍然可以创建具有重复日期*的首选项。这(看似)是因为运行validates_uniqueness_of验证时client_id外键不可用。 (*我有一个索引,可以防止复制被保存,但是我想捕获错误,并在它到达数据库之前返回用户友好的错误消息。)

有没有办法通过ActiveRecord验证来防止这种情况发生?

编辑:This appears to be a known issue

答案

批量插入时,没有一种超级干净的方法可以使用AR验证,但您可以使用以下步骤手动执行此操作。

  1. 使用Postgresql VALUES列表对数据库进行单个查询以加载任何可能重复的记录。
  2. 比较您要批量创建和提取任何重复项的记录
  3. 手动生成并返回错误消息

第1步看起来有点像这样

# Build array of uniq attribute pairs we want to check for
uniq_attrs = new_collection.map do |record|
  [
    record.day,
    record.client_id,
  ]
end

# santize the values and create a tuple like ('Monday', 5)
values = uniq_attrs.map do |attrs|
  safe = attrs.map {|v| ActiveRecord::Base.connection.quote(v)}
  "( #{safe.join(",")} )"
end

existing = Preference.where(%{
    (day, client_id) in
    (#{values.join(",")})
 })

# SQL Looks like 
# select * from preferences where (day, client_id) in (('Monday',5), ('Tuesday', 3) ....)

然后你可以采取existing集合并在第2步和第3步中使用它来提取你的副本并生成你的错误消息。

当我需要这个功能时,我通常会把它作为我课堂上的一个自我方法,所以就像这样

class Preference < ApplicationRecord

  def self.filter_duplicates(collection)
    # blah blah blah from above

    non_duplicates = collection.reject do |record|
      existing.find do |exist|
        exist.duplicate?(record)
      end
    end

    [non_duplicates, existing]

  end

  def duplicate?(record)
    record.day == self.day && 
    record.client_id = self.client_id
  end
end

以上是关于防止Rails 5中重复的has_many记录的主要内容,如果未能解决你的问题,请参考以下文章

获取 STI 中的记录以获取 rails 4 中的空 has_many 记录

如何将记录添加到has_many:通过rails中的关联

更改连接表中的属性值时,Rails 5 正在插入而不是更新

Rails has_many 关联如何保证每条关联记录只有一个?

Rails:has_many 通过多态关联——这行得通吗?

如何在Rails 5中删除模型时删除模型的所有关联