我最近不得不做类似的事情,这就是我实现这一目标的方式。这很聪明(至少我是这么认为的。:))
我创建了一个查询模型,它在 JSON 中序列化查询列(文本字段)。我使用表单从带有选择字段的用户那里获取查询数据。
class BookQuery < ActiveRecord::Base
has_many :books
# loop through each foreign key of the Book table and create a hash with empty selection
def self.empty_query
q = {}
Book.column_names.each do |column_name|
next unless column_name.ends_with?("_id")
q.merge column_name => []
end
end
end
下面我以作者为例: <%= form_for @book_query do |f| %> <% for author in Author.all %> <%= check_box_tag "book_query[query][author_ids][]", author.id, false%> <%= author.name %> <% end %> <% = f.submit "保存查询" %> <% end %>
提交此表单时,您最终会得到如下参数: 提交表单时,它会生成此参数:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"XXXXXXXXXXX", "book_query"=>{"query"=>{"author_ids"=>["2", "3"]}}, "commit"=>"Save Query"}
现在在 BookQuery 控制器的 create 操作中,您可以执行 create 函数始终执行的操作:
def create
@book_query = BookQuery.build(params[:book_query])
if @book_query.save
flash[:success] = "Book query successfully saved."
redirect_to ...
else
flash[:error] = "Failed to save book query."
render :new
end
end
但默认情况下,rails 将数据序列化为哈希类型:
1.9.3p194 :015 > pp BookQuery.find(9).query
BookQuery Load (0.7ms) SELECT "book_queries".* FROM "book_queries" WHERE "book_queries"."id" = $1 LIMIT 1 [["id", 9]]
"--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess\nauthor_ids:\n- '2'\n- '3'\n"
=> "--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess\nauthor_ids:\n- '2'\n- '3'\n"
在 BookQuery 模型中,添加以下内容:
serialize :query, JSON
但是 rail 会将 ID 更改为字符串:
1.9.3p194 :018 > query = JSON.parse(BookQuery.find(10).query)
BookQuery Load (0.5ms) SELECT "book_queries".* FROM "book_queries" WHERE "book_queries"."id" = $1 LIMIT 1 [["id", 10]]
=> {"author_ids"=>["2", "3"]}
1.9.3p194 :019 > query["author_ids"]
=> ["2", "3"]
然后我所做的是覆盖 BookQuery 模型中的属性访问器:
必须完成以下操作,因为哈希返回字符串,而不是整数中的 id。
def query=(query)
query.each_pair do |k, v|
if query[k].first.present?
query[k].map!(&:to_i)
else
query.except!(k)
end
end
write_attribute(:query, query)
end
# just want to avoid getting nil query's
def query
read_attribute(:query) || {}
end
要使用此查询查找书籍,您只需将此函数添加到您的 Book 模型:
def self.find_by_book_query(book_query, options = {})
options[:conditions] = book_query.query
find(:all, options)
end
现在,您将获得一个基于模型定义 Book 的可自定义查询字符串,并且一切都像 Rails 方式一样工作。:)