Rails的成语,以避免重复的has_many:通过 [英] Rails idiom to avoid duplicates in has_many :through

查看:139
本文介绍了Rails的成语,以避免重复的has_many:通过的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

 类用户的LT;的ActiveRecord :: Base的
  的has_many:user_roles
  的has_many:角色:通过=> :user_roles
结束
 

我想,以确保用户只能分配任何角色一次。任何企图插入重复应该忽略的要求,不会引发错误或导致验证失败。我真的想重新present是一个集是什么,在这里插入一个已经存在的集合的一个元素没有任何影响。 {1,2,3} U {1} = {1,2,3},{不} 1,1,2,3

我知道我能做到这一点是这样的:

  user.roles<<角色除非user.roles.include?(角色)
 

或创建一个包装方法(如 add_to_roles(角色)),但我希望对一些惯用的方式,使其自动通过关联关系,这样我就可以写:

  user.roles<<角色#自动检查roles.include?
 

和它只是不适合我的工作。这样一来,我就不必记住要检查的DUP或使用自定义的方法。有什么在我失踪的框架?我第一个想到的:uniq的选项HAS_MANY将做到这一点,但它基本上只是

选择不重复的。

有没有一种方法来声明这样做吗?如果没有,则是通过关联扩展?

下面是一个如何的默认行为失​​败的例子:

>>  U = User.create 
      用户创建(0.6ms)INSERT INTO用户(名)VALUES(NULL)
    => #<使用者ID:3,名称:无>
    >>  u.roles<< Role.first 
      角色负荷(0.5毫秒)SELECT * FROM角色LIMIT 1
      UserRole创建(0.5毫秒)INSERT INTOuser_roles(ROLE_ID,USER_ID)VALUES(1,3)
      角色负荷(0.4ms)选择角色。* FROM角色INNER JOINuser_rolesON角色.ID =user_roles.role_id WHERE((user_roles.user_id = 3))
    => [#<角色ID:1,名称:1>]
    >>  u.roles<< Role.first 
      角色负荷(0.4ms)SELECT * FROM角色LIMIT 1
      UserRole创建(0.5毫秒)INSERT INTOuser_roles(ROLE_ID,USER_ID)VALUES(1,3)
      => [#<角色ID:1,名称:1>中#<角色ID:1,名称:1>]  

解决方案

只要附加的角色是一个ActiveRecord的对象,你在做什么:

  user.roles<<角色
 

的has_many 协会:

应该自动删除重复。

有关的has_many:通过,试试:

 类用户
  的has_many:角色:通过=> :user_roles做
    DEF&其中;≤(NEW_ITEM)
      超(阵列(NEW_ITEM) -  proxy_association.owner.roles)
    结束
  结束
结束
 

如果超不工作,你可能需要建立一个alias_method_chain。

I have a standard many-to-many relationship between users and roles in my Rails app:

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

I want to make sure that a user can only be assigned any role once. Any attempt to insert a duplicate should ignore the request, not throw an error or cause validation failure. What I really want to represent is a "set", where inserting an element that already exists in the set has no effect. {1,2,3} U {1} = {1,2,3}, not {1,1,2,3}.

I realize that I can do it like this:

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

or by creating a wrapper method (e.g. add_to_roles(role)), but I was hoping for some idiomatic way to make it automatic via the association, so that I can write:

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

and it just does the work for me. This way, I don't have to remember to check for dups or to use the custom method. Is there something in the framework I'm missing? I first thought the :uniq option to has_many would do it, but it's basically just "select distinct."

Is there a way to do this declaratively? If not, maybe by using an association extension?

Here's an example of how the default behavior fails:

    >> 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">]

解决方案

As long as the appended role is an ActiveRecord object, what you are doing:

user.roles << role

Should de-duplicate automatically for :has_many associations.

For has_many :through, try:

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

if super doesn't work, you may need to set up an alias_method_chain.

这篇关于Rails的成语,以避免重复的has_many:通过的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆