通过accepts_nested_attributes_for对has_many关联记录进行唯一性验证

时间:2016-06-09 15:57:31

标签: ruby-on-rails ruby

以下是我的模型

class User < ApplicationRecord
  has_many :cities_users, inverse_of: :user
  has_many :cities, through: :cities_users

  accepts_nested_attributes_for :cities_users
end


class CitiesUser < ApplicationRecord
  belongs_to :user
  belongs_to :city

  validates :user_id, uniqueness: { scope: :city_id, message: "already specified that they have lived in this city"}
end


class City < ApplicationRecord
  has_many :cities_users
  has_many :users, through: :cities_users
end

创建新的user记录时:用户可以动态添加他们所居住的城市(通过nested_form_fields gem)。这最终会在cities_users的连接表上创建记录。

clicking button

点击“添加您居住的城市”之后&#39;按钮:

enter image description here

用户然后再次点击该按钮以添加其他城市:

click button again

上面的情况是我希望在用户点击Create User按钮时触发验证错误的地方。创建新用户记录时:用户不应指定他们多次居住在同一个城市。

如上所述:未经验证:将使用相同的cities_usersuser_id创建两个 city_id条记录。这不好,所以我想重新渲染表单,突出显示两个有问题的cities_users嵌套字段,并有一条错误消息说出&#34;你不能指定用户已经住在同一个城市更多比一次&#34;。

显然,这需要在user模型或cities_user模型上进行验证。我不知道验证应该去哪里,我不知道如何编写验证代码以便它捕获此错误。我在uniqueness模型上的当前CitiesUser验证没有发现这种情况。

额外的设置信息,以防万一有人想要重新创建场景

这就是我在用户_form.html.erb中设置nested_fields的方式:

<%= f.nested_fields_for :cities_users do |ff| %>

<div>
  <%= ff.label :city_id %>
  <%= ff.collection_select(:city_id, City.all, :id, :name) %>
</div>

<% end %>

<br>

<div><%= f.add_nested_fields_link :cities_users, "Add a City You have lived in" %></div>

users_controller#create操作是标准的,由scaffold命令生成。我确实添加了强大的参数,以便允许嵌套属性:

def user_params
  params.require(:user).permit(:name, cities_users_attributes: [:id, :city_id, :user_id])
end

3 个答案:

答案 0 :(得分:1)

我认为最好的办法就是在user_params方法中将其传递给User模型,这是在User模型中添加自定义验证。强制他们添加的城市的ID在通过&#39; id&#39;提交的城市数组中是唯一的。做类似的事情:

class User < ActiveRecord::Base

  validate :no_duplicate_cities

  #I'm assuming user_cities is an array of city id's so we only want one id of each different city at most.
  def no_duplicate_cities
    self.user_cities = self.user_cities.uniq
  end

这只会在该数组中保留唯一ID,从而删除任何重复的城市。

答案 1 :(得分:1)

我终于做到了这一点:

#models/user.rb
class User < ActiveRecord::Base

  has_many :cities_users
  has_many :cities, through: :cities_users

  accepts_nested_attributes_for :cities_users, allow_destroy: true

  validate :no_duplicate_cities

  private

  def no_duplicate_cities
    errors.add(:duplicate_cities, "are present") if self.cities_users.group_by(&:city_id).values.detect{|arr| arr.size > 1}
  end
end

答案 2 :(得分:0)

我在我的应用程序中做了类似的事情,在用户模型中尝试以下代码。

validate :unique_cities

def unique_cities
  unless self.user_cities.map(&:city_id).uniq
    errors.add(:cites, "- can not be 2 of the same cities")
  end
end