Rails - передать коллекцию объекту ActiveModel
Я использую рельсы для создания таблицы данных, которая разбивается на страницы с помощью Ajax, и для этого я использую Railscast #340.
Этот эпизод использует обычный класс ActiveModel, называемый ProductsDatatable
или в моем случае OrdersDatatable
создать и настроить таблицу. Мой вопрос касается синтаксиса ruby в этом классе. Я пытаюсь передать коллекцию заказов OrdersDatatable
объект, от контроллера. Я хочу получить доступ к этой коллекции в fetch_orders
метод.
Я создаю объект таблицы следующим образом в order.rb:
@datatable = OrdersDatatable.new(view_context)
@datatable.shop_id = @current_shop.id
@datatable.orders_list = @orders # which is Order.in_process
И мой OrdersDatatable
класс выглядит так: (важными частями, которые, вероятно, нужно изменить, является вторая строка initialize
и первая строка в fetch_orders
)
class OrdersDatatable
include Rails.application.routes.url_helpers
include ActionView::Helpers::DateHelper
include ActionView::Helpers::TagHelper
delegate :params, :h, :link_to, :number_to_currency, to: :@view
attr_accessor :shop_id, :orders_list
def initialize(view)
@view = view
@orders_list = self.orders_list
end
def current_shop
Shop.find(shop_id)
end
def as_json(options = {})
{
sEcho: params[:sEcho].to_i,
iTotalRecords: orders.count,
iTotalDisplayRecords: orders.count,
aaData: data
}
end
private
def data
orders.map do |order|
[
order.id,
order.name,
h(time_tag(order.date_placed.in_time_zone)),
order.state,
order.source,
order.payment_status,
h(order.delivered? ? 'shipped' : 'unshipped'),
h(number_to_currency order.final_total, unit: order.currency.symbol),
h(link_to 'details', edit_admin_shop_order_path(current_shop, order)),
h(link_to 'delete', admin_shop_order_path(current_shop, order), method: :delete, data: { confirm: 'Are you sure?' } ),
]
end
end
def orders
@orders ||= fetch_orders
end
def fetch_orders
orders = orders_list.order("#{sort_column} #{sort_direction}")
orders = orders.page(page).per_page(per_page)
if params[:sSearch].present?
orders = orders.where("title like :search", search: "%#{params[:sSearch]}%")
end
orders
end
def page
params[:iDisplayStart].to_i/per_page + 1
end
def per_page
params[:iDisplayLength].to_i > 0 ? params[:iDisplayLength].to_i : 10
end
def sort_column
columns = %w[id name date_placed state source payment_status delivered final_total]
columns[params[:iSortCol_0].to_i]
end
def sort_direction
params[:sSortDir_0] == "desc" ? "desc" : "asc"
end
end
Когда я меняю первую строку в fetch_orders
к этому
orders = Order.in_process.order("#{sort_column} #{sort_direction}")
который является жестко запрограммированным эквивалентом, он действительно работает. Так что мне просто нужен правильный синтаксис
2 ответа
Краткий ответ: Если у вас есть массив, и вы хотите отсортировать его, используйте sort_by
метод:
orders = orders_list.sort_by{|order| "#{order.sort_column} #{order.sort_direction}"}
Длинный ответ: причина, по которой ваш оригинальный код не работает, в том, что в этом случае
Order.in_process.order("#{sort_column} #{sort_direction}")
Вы строите запрос. in_process
является именованной областью (проходящей в некоторых условиях), и .order
говорит рельсам, что заказывать запрос Затем, когда у него заканчиваются цепочечные методы, запрос выполняется (запускает некоторый sql) и получает записи из БД для создания коллекции объектов.
Как только вы работаете с коллекцией объектов, вы не можете вызвать .order
метод, так как он просто используется для сборки SQL-запроса. Вам нужно использовать Array#sort_by
вместо. sort_by
принимает блок кода, в который передается каждый объект в коллекции (как order
в моем примере, но вы можете назвать это как угодно, это просто имя переменной).
Кстати, если вы просто хотите вызвать метод для всех объектов, чтобы отсортировать их, вы можете использовать "синтаксис быстрого доступа", как .sort_by(&:methodname)
, Это использует маленький трюк рубина под названием Symbol#to_proc
( http://railscasts.com/episodes/6-shortcut-blocks-with-symbol-to-proc).
Так, например, если был метод в порядке как
def sort_string
"#{self.sort_column} #{self.sort_direction}"
end
тогда вы могли бы изменить свой код на
orders = orders_list.sort_by(&:sort_string)
который опрятен.
Если у вас есть массив, то вы можете сортировать следующим образом.
orders = orders_list.sort! {|a,b| a.sort_column <=> b.sort_direction}