Rails范围,数组作为参数Rails 4

时间:2016-02-25 19:23:42

标签: ruby-on-rails ruby ruby-on-rails-4 rails-activerecord

我的Member模型first_namelast_name中有两个属性。我的要求是我将获得一系列全名:

["Jane Doe", "John Doe", "Foo Bar"]

并且,对于我需要的每个元素:

  1. 将每个元素解析为名字和姓氏
  2. first_namelast_name(如果存在)
  3. 查找每个会员记录

    我试图在模型范围内创建它,如下所示:

    class Member < ActiveRecord::Base
    
      scope :find_by_full_name, -> (full_name) { |full_name|
          first_name = full_name.split(' ').first
          last_name = full_name.split(' ').last
          where(first_name: first_name, last_name: last_name)
      }
    

    它适用于字符串,但不适用于数组。我查看了这篇文章 - https://stackoverflow.com/a/8155276/4379077。看起来Rails应该检测它是一个数组还是一个字符串,并相应地处理每个类型。在一堆或控制台尝试之后(包括将元素转换为字符串)我不知道我做错了什么,或者除了创建一个范围之外我可能会采用不同的方式。任何帮助表示赞赏。

4 个答案:

答案 0 :(得分:1)

由于范围只是Class方法的语法糖,我建议只将其创建为类方法,整个过程变得更容易/更清洁:

class Member < ActiveRecord::Base

  self.find_by_full_name(array_of_names)
    members = []

    array_of_names.each do |name|
      split_names = name.split(' ')
      members.push(Member.find_by_first_name_and_last_name(split_names.first, split_names.last))
    end

    members.compact
  end

end

更新

如果您希望它能够接受单个名称或一组名称,您甚至可以轻松地扩展它的功能(另外一行):

class Member < ActiveRecord::Base

  self.find_by_full_name(names)
    members = []
    names = [names] if names.is_a?(String)

    names.each do |name|
      split_names = name.split(' ')
      members.push(Member.find_by_first_name_and_last_name(split_names.first, split_names.last))
    end

    members.compact
  end

end

注意

不确定您是否熟悉.compact,但这将完成问题的"if it exists"部分。 Member.find_by...会将nil值推送到members,如果没有匹配,则.compact会从{{1}中移除nil值}}

答案 1 :(得分:0)

如果我正确理解您的问题,您想通过这一系列名称查找您的成员吗?

你也可以使用像

这样的东西
full_names.each do |name|
  first = name.split(" ").first
  last = name.split(" ").last
  variable = Member.find_by_first_name_and_last_name(:first_name => first,
                                                    :last_name => last)
  # member_records[] << variable 
  # or whatever you're doing with them next.

end

您可以请求&#34; find_by&#34;有多个参数 因此 Rails - Find By with 2 fields?

答案 2 :(得分:0)

如果您想避免N + 1查询 - 我建议 - 有一次性解决方案。您可以在SQL查询中连接名字和姓氏,并将其与给定的参数进行比较。

class Member < ActiveRecord::Base
  space = Arel::Nodes.build_quoted(' ')
  concat_op = '||' # check which operator your database uses
  first_name_space = Arel::Nodes::InfixOperation.new(concat_op, arel_table[:first_name], space)
  first_name_space_last_name = Arel::Nodes::InfixOperation.new(concat_op, first_name_space, arel_table[:last_name])

  scope :find_by_full_name, -> (full_name) { where(first_name_space_last_name.in(full_name)) }
end

这适用于单个字符串和数组作为参数。

注意检查数据库使用哪个运算符作为连接运算符并进行相应调整。此示例假定为||,这适用于PostgreSQL和SQLite数据库。

答案 3 :(得分:-1)

不是很优雅,但是怎么样

class Member < ActiveRecord::Base

  scope :find_by_full_name, -> (*args) {
    conds = []
    conds << args.count.times.map { "first_name = ? AND last_name = ?" }.join(' OR ')
    args.map { |arg| arg.split(' ') }.flatten.each { |e| conds << e }
    where(conds)
  }

end