从表属性中创建与另一个表属性匹配的值数组

时间:2013-12-20 21:17:39

标签: ruby-on-rails ruby arrays

在我的Ruby on Rails 3 app控制器中,我试图在我的编辑视图中创建一个实例变量数组。

User表有一个user_id和reseller_id。 Certificate表有一个user_id。

我需要从User表中获取在User表和Certificate表中都有user_id的reseller_id。

这是我的用户模型:

class User < ActiveRecord::Base
attr_accessible :email, :name, :password, :password_confirmation, :remember_token, :reseller_id, :validate_code, :validate_url, :validated, :admin, :avatar

belongs_to :reseller
has_one :certificate
end

这是我的证书模型:

class Certificate < ActiveRecord::Base
attr_accessible :attend, :pass, :user_id

validates :user_id, presence: true
end

这是我的控制器,这似乎只存储证书表中的最后一个user_id。

#@train should be reseller.id(s) of all users in Certification table.
@certs = Certificate.all
  @certs.each do |user| 
     @id = []
     @id << user.user_id 
     @id.each do |id|
       if User.find(id)
         @train = []
         @train << User.find(id).reseller_id
       end
     end
  end

谢谢

2 个答案:

答案 0 :(得分:1)

首先,您不应将{id} each块嵌套在each块内以获取证书。你应该构建你的id数组,然后再循环它。你只得到最后一个user_id的原因是因为“@id”在你的代码当前写入时只会有一个元素。你的“@train”数组也会遇到同样的问题。因为您在迭代器中声明了数组,所以每次迭代都会重新创建它(在其中没有任何内容)。使用现有代码,这应该有效:

@certs = Certificate.all
@ids = []
@certs.each do |user|  
 @ids << user.user_id 
end

@train = []
@ids.each do |id|
  if User.find(id)
    @train << User.find(id).reseller_id
  end
end

更简洁的方式如下:

cert_user_ids = Certificate.all.map(&:user_id)
reseller_ids = cert_user_ids.map { |id| User.find(id).reseller_id rescue nil }.compact

Map是一个可枚举的方法,它返回与第一个数组大小相等的数组。在每次迭代中,无论块内的代码返回“替换”返回的新数组中的该元素。换句话说,它将一个数组的值映射到相同大小的新数组。第一个map函数获取所有证书的user_ids(使用&:user_idCertificate.all.map { |cert| cert.user_id }的快捷方式)如果找到用户,则第二个map函数返回用户的“reseller_id”。如果没有找到具有该id的用户,则返回nil。最后,compact从新映射的reseller_id数组中删除所有nil值,只留下代理商ID。

如果您希望以最有效和最有效的方式执行此操作,最大限度地减少数据库调用并允许数据库执行大部分繁重工作,您可能希望使用连接:

reseller_ids = User.joins(:certificates).all.map(&:reseller_id) 

这将抓取具有该用户ID的证书的所有用户。然后它再次利用map将返回的用户映射到一个只包含user.reseller_id的新数组。

Ruby在这种类型的过滤中往往比RDBM系统(如mysql)慢,因此最好尽可能多地将数据委托给数据库。

(请注意,默认情况下,此连接会将user.id与certificate.user_id进行比较。因此,如果您的users表中的'primary key'被命名为'user_id',那么这将无效。为了使其成为工作时,您应该使用标准“id”作为主键,或者您需要指定'user_id'是用户模型中的主键)

答案 1 :(得分:1)

1)正确版本的代码

@certs = Certificate.all
@reseller_id = []                  # 1
@certs.each do |user| 
  id = user.user_id                # 2 
  if u = User.find(id)
    @reseller_id << u.reseller_id  # 3
  end
end

2)Rails方式

像这样的东西

@reseller_id = User.joins(:certificates).select('users.reseller_id').map { |u| u['reseller_id']}

PS

请不要将此代码保留在控制器中: - )