ruby-on-rails – Rails成语,以避免在has_many:through中出现重复

我在Rails应用程序中的用户和角色之间有一个标准的多对多关系:

class User < ActiveRecord::Base
  has_many :user_roles
  has_many :roles, :through => :user_roles
end

我想确保一个用户只能被分配任何角色一次。任何插入副本的尝试都应忽略请求,不会抛出错误或导致验证失败。我真正想表示的是一个“集合”,其中插入一个已经存在的集合中的元素没有效果。 {1,2,3} U {1} = {1,2,3},而不是{1,1,2,3}。

我意识到,我可以这样做:

user.roles << role unless user.roles.include?(role)

或者通过创建一个包装器方法(例如add_to_roles(role)),但我希望一些惯用的方法通过关联使它自动,所以我可以写:

user.roles << role  # automatically checks roles.include?

它只是为我做的工作。这样,我不必记住检查dups或使用自定义方法。在框架中有没有什么我缺少?我首先想到:uniq选项has_many会做到,但它基本上只是“选择不同”。

有声明地做这个方法吗?如果不是,也许通过使用关联扩展?

以下是默认行为失败的示例:

    >> u = User.create
      User Create (0.6ms)   INSERT INTO "users" ("name") VALUES(NULL)
    => #<User id: 3, name: nil>
    >> u.roles << Role.first
      Role Load (0.5ms)   SELECT * FROM "roles" LIMIT 1
      UserRole Create (0.5ms)   INSERT INTO "user_roles" ("role_id", "user_id") VALUES(1, 3)
      Role Load (0.4ms)   SELECT "roles".* FROM "roles" INNER JOIN "user_roles" ON "roles".id = "user_roles".role_id WHERE (("user_roles".user_id = 3)) 
    => [#<Role id: 1, name: "1">]
    >> u.roles << Role.first
      Role Load (0.4ms)   SELECT * FROM "roles" LIMIT 1
      UserRole Create (0.5ms)   INSERT INTO "user_roles" ("role_id", "user_id") VALUES(1, 3)
    => [#<Role id: 1, name: "1">, #<Role id: 1, name: "1">]
只要附加的角色是一个ActiveRecord对象,你正在做什么:

user.roles << role

应自动为:has_many关联重复。

对于has_many:通过,尝试:

class User
  has_many :roles, :through => :user_roles do
    def <<(new_item)
      super( Array(new_item) - proxy_association.owner.roles )
    end
  end
end

如果super不起作用,你可能需要设置一个alias_method_chain。

http://stackoverflow.com/questions/1315109/rails-idiom-to-avoid-duplicates-in-has-many-through

本站文章除注明转载外,均为本站原创或编译
转载请明显位置注明出处:ruby-on-rails – Rails成语,以避免在has_many:through中出现重复