Rails嵌套属性:validates_uniqueness_of不起作用

时间:2016-05-04 06:06:43

标签: ruby-on-rails ruby validation rails-activerecord nested-attributes

我有3个型号:

class UserLanguage < ActiveRecord::Base
    belongs_to :user
    belongs_to :language
end

class Language < ActiveRecord::Base
    has_many :user_languages
    has_many :users, :through => :user_languages
end

class User < ActiveRecord::Base
    has_many :user_languages, :dependent => :destroy
    has_many :languages, :through => :user_languages
    accepts_nested_attributes_for :user_languages, :allow_destroy => true
end

我正在使用nested_form gem来帮助用户选择他们可以说的语言。对此的CRUD工作正常。

但是,我无法验证UserLanguage的唯一性。我尝试了这两种语法,但它们对我不起作用:

validates_uniqueness_of :language_id, scope: :user_id
validates :language_id, :uniqueness => {:scope => user_id}

我的user_languages表架构:

  create_table "user_languages", force: :cascade do |t|
    t.integer  "user_id"
    t.integer  "language_id"
    t.datetime "created_at",  null: false
    t.datetime "updated_at",  null: false
  end

  add_index "user_languages", ["language_id"], name: "index_user_languages_on_language_id", using: :btree
  add_index "user_languages", ["user_id"], name: "index_user_languages_on_user_id", using: :btree

我应该怎样做以确保一个用户只能选择一种语言?比如,如果我在下拉列表中选择英语,我的第二个英语将不会被保存(重复)并被拒绝。

2 个答案:

答案 0 :(得分:0)

如果用户已经且应该只有一种语言,那么您可以更改模型之间的基数:

class Language < ActiveRecord::Base
    has_many :users
end

class User < ActiveRecord::Base
    has_one :language
end

然后根据定义,您的用户一次只能使用一种语言,您的整体模型会更简单。

您可以阅读有关有效记录关联和基数的更多信息in this Association Basics guide

答案 1 :(得分:0)

这就是我最终做到的:

class UserLanguage < ActiveRecord::Base

    belongs_to :user
    belongs_to :language

    def self.delete_duplicated_user_languages(user_id)
        user_languages_ids      = UserLanguage.where(user_id: user_id).pluck(:language_id).sort
        duplicate_language_ids  = user_languages_ids.select {|language| user_languages_ids.count(language) > 1}
        duplicate_language_ids.uniq!
        keep_this_language = []
        duplicate_language_ids.each do |language_id|
            keep_this_language << UserLanguage.find_by(user_id: user_id, language_id: language_id).id
        end
        single_language = user_languages_ids.select {|language| user_languages_ids.count(language) == 1}
        single_language.each do |language|
            keep_this_language << UserLanguage.find_by(user_id: user_id, language_id: language).id
        end
        UserLanguage.where(user_id: user_id).where.not(id: keep_this_language).destroy_all
    end

end

我首先保存所有UserLanguage并稍后删除它们(重复的)。