假设你有一个“作者”对象,它有几本书,你想要在模型中构建一些方法。您的基本设置如下所示:
class Author
def book_count(fiction = nil, genre = nil, published = nil)
end
end
对于每个参数,您有几种方法可以操作:
fiction = true #retrieve all fiction books
fiction = false #retrieve all nonfiction
fiction = nil #retrieve books, not accounting for type
genre = nil #retrieve books, not accounting for genre
genre = some_num #retrieve books with a specific genre id
published = true #retrieve all published
published = false #retrieve all unpublished
published = nil #retrieve books, not accounting for published
现在,我写了一些基本的选择语句,其中包括:
if published == true
return self.books.select{ |b| b.published == true }.size
elsif published == false
return self.books.select{ |b| b.published == false}.size
else
return self.books.size
end
当我只有一两个论点时,这很笨重,但很容易。但是,随着客户端请求将更多条件添加到方法中,编写它会变得越来越乏味。
最好的“轨道”方式是什么才能解决这个问题?
谢谢!
答案 0 :(得分:6)
范围,(或者如果你使用Rails< 3则命名为“named_scopes”)可能是最好的方法。
以下是针对rails 3,但可以通过轻微的语法调整来完成 您可以在模型中创建一组范围。即。
scope :with_genre, lambda {|genre| where(:genre => genre) unless genre.nil?}
scope :published, lambda{|published| where(:published => published) unless published.nil?}
scope :fiction,, lambda{|fiction| where(:fiction => fiction) unless fiction.nil?}
等
然后,只要您需要访问它们,您就可以执行
之类的操作def book_count(..)
self.books.with_genre(genre).published(published).fiction(fiction).size
end
此外,您可以将book_count参数设置为哈希值,然后您可以使用任意数量的选项而不会使该函数具有大量参数。
答案 1 :(得分:1)
首先,您可能希望book_count
获取哈希options={}
并在方法本身中定义键默认值。这样,由于客户端需要更多选项(或决定删除一些选项),您不必追逐项目中的所有调用并相应地更改它们。我更喜欢这样做,但你也可以使用*arguments
。
作为选项哈希传递的一个好处是,如果值为nil
,您只是不传递密钥,那么您只需找到符合搜索条件的书籍数量,如下所示:
return self.books.find(:all, :conditions => options).count
这应该可以正常工作,并允许以后添加其他规范。只需确保options
哈希中的键与您的模型属性匹配。
答案 2 :(得分:0)
if published.nil?
return books.size
else
return books.count{ |b| b.published == published }
end
或
if published.nil?
return books.size
else
return books.map(&:published).count published
end
或
return books.count{ |b| published.nil? || b.published == published }
或
return published.nil? ? books.size : books.map(&:published).count(published)
或
return published.nil? ? books.size : books.count{ |b| b.published == published }
答案 3 :(得分:0)
更多的Rails-y方式是使用ActiveRecord的内置find方法将这些方法从数据库中取出而不是在Ruby中过滤它。它会更快,代码也会更清晰。 where
方法可以获取属性和值的哈希值。 (有关更多信息,请参阅ActiveRecord guide to querying,这是一个很好的介绍)
您使用的是Rails 3吗?在这种情况下,ActiveRecord更容易使用。
这样的东西可能会起作用(虽然我现在无法访问rails,所以这可能包含错误):
class Author
def book_count( filter )
Book.find_by_author( self ).where( filter ).count
end
end
那应该找到该作者的所有书籍(假设您在作者和书籍之间有模型关联),其中您指定的所有条件都是真的。您可能需要先筛选出任何nils。 filter
将是{ :genre => 'Horror', :published => true }
等条件的哈希值。
请注意,我使用count
而不是size
。 count
使用SQL计数函数而不是返回数据,然后在ruby中对其进行计数。
希望有所帮助。
答案 4 :(得分:0)
如果你已经加载了books
,那么你可以试试这个:
def book_count(options = {})
books.select{|b| options.all?{|k, v| v.nil? || b.send(key) == v} }.size
end
现在您可以拨打电话,例如
author.books.book_count(:genre => "foo", :fiction => true)
如果要从过滤条件中删除属性,请从参数哈希中排除属性。在上面的示例中,:published
被排除在过滤条件之外,因为它在参数哈希中缺失。我添加了额外的nil
检查,以满足属性值真正为零的情况。
如果未加载books
列表,请使用Olives建议的named_scope方法。