【问题标题】:Rails: collection based on paramsRails:基于参数的集合
【发布时间】:2014-10-01 02:04:31
【问题描述】:

我有一个 index 方法,看起来非常糟糕,像这样:

  def index
    if params[:brand]
      @users = User.includes(:brand).where(brand_id: params[:brand]).order("#{sort_column} #{sort_direction}").page(params[:page]).per(10)
    elsif params[:search]
      @user = User.includes(:brand).find_by_client_code(params[:search])
      redirect_to edit_user_path(@user)
    elsif params[:page] == 'all'
      @users = User.includes(:brand).order("#{sort_column} #{sort_direction}").all
    elsif params[:state]
      @users = User.includes(:brand).where(state: params[:state]).page(params[:page]).per(10)
    else
      @users = User.includes(:brand).order("#{sort_column} #{sort_direction}").page(params[:page]).per(10)
    end
  end

相当混乱,我知道,但它有效。现在我正在尝试对其进行重构,但我想不出在不使路线复杂化的情况下将其拆分为更小的集合的最佳方法。

  def index
    [:brand, :page, :search, :state].each do |param|
      if params[:page] == 'all'
        @users = User.includes(:brand).order(column + ' ' + direction)
      elsif params.key?(param)
        param
      else
        @users = User.includes(:brand).order(column + ' ' + direction)
                     .page(params[:page]).per(10)
      end
    end
  end

  def brand
    @users = User.includes(:brand).where('brand_id in (?)', params[:brand])
                 .order(column + ' ' + direction).page(params[:page]).per(10)
  end

  def state
    @users = User.includes(:brand).where(state: params[:state])
                 .page(params[:page]).per(10)
  end

  def search
    @user = User.includes(:brand).find_by_client_code(params[:search])
    redirect_to edit_user_path(@user)
  end

上述方法不起作用,但您明白了。有人知道处理这种情况的好方法吗?干杯。

【问题讨论】:

  • 我只是走上了同样的学习道路。我曾经将所有 AJAX 请求放入模板的操作中,但现在将它们放入自己的方法中。我在互联网上找不到关于这个主题的大量指导。我希望更有经验的人能加入进来,但我将大多数 AJAX 请求分解为他们自己的操作和路由,并对结果感到满意。逻辑更容易理解,我的行动也不会混乱。我的路线文件更多,但通过适当的组织,它看起来仍然很干净。好问题!
  • 您使用的是哪个版本的 ruby​​?
  • 我正在使用 Ruby 2.1.2
  • 我在这里也看不到响应块。你是用 HTML 还是 js 格式请求这个?
  • 抱歉,是的,它已被删除。 HTML。

标签: ruby-on-rails ruby-on-rails-3 model-view-controller routing


【解决方案1】:

我可能会这样做 -

首先,在您定义 sort_columnsort_direction 方法的地方更新此代码,使其具有默认值:

def sort_column
  colum_name = params[:colum_name]
  colum_name ||= 'id'
end

def  sort_direction
  direction = params[:direction]
  direction ||= 'ASC'
end

添加一个新方法以使per_page(在与sort_columnsort_direction 相同的位置)来自参数或User 类的默认值:

def per_page
  per = params[:per_page]
  per ||= User.per_page
end

app/models/user.rb

scope :with_brand_id, ->(id) { where(brand_id: id) }
scope :with_state,    ->(state) { where(state: state) }
scope :order_with,    ->(column_name, direction) { order("#{sort_column} #{sort_direction}") }

# never use/avoid magic numbers in your application at multiple places as they gets unmanageable as your application grows
# single place to manage your per page entries for users.
def self.per_page
  10
end

# easy to use key:value based arguments since you're using Ruby 2, cheers!
def self.fetch_with_brand(brand: nil, state: nil, page: nil, sort_column: 'id', sort_direction: 'ASC', per_page: User.per_page)
  user_scope, pagination_scope_applies =  if brand.present?
    [self.with_brand_id(brand), true]
  elsif state.present?
    [self.with_state(state), true]
  else
    [self.scoped, (page != 'all')]
  end

  user_scope.merge(pagination_scope(page, per_page)) if pagination_scope_applies
  user_scope.includes(:brand).order_with(sort_column, sort_direction)
end

# since I am not sure about your implementation of `page` and `per` methods, I'd create a class method, otherwise you can create a `scope` for this, too
def self.pagination_scope(page_number, per_page)
  self.page(page_number).per(per_page)
end

你在上面提到的代码中看到:[self.scoped, (page != 'all')] 吗?这里self.scoped 等于self.all(评估时),但我们必须使用scoped 而不是all,因为在Rails 3 中它给出了一个Array,而在Rails 4 中它将是一个 ActiveRecord::Relation 对象,因此如果您在 Rails 4 上,您可以使用 self.all。注意:scoped 在 Rails 4 中已弃用,取而代之的是 all

另外,我想在这里指出一个问题。在您的代码中,您优先考虑params[:page] == 'all' 条件,然后是params[:search]。在我上面提到的代码中优先考虑search,然后是page,但你明白了吧?

现在,让我们在控制器中添加用户特定的参数方法:

def user_params
  params.slice(:brand, :page, :state).merge!({sort_column: sort_column, sort_direction: sort_direction, per_page: per_page })
end

但是,在 Rails 4 中,使用强参数更容易,例如:params.require(:user).permit(:search,..) 等。

现在,您的控制器的 index 方法可能如下所示:

def index
  if params[:search].present?
    @user = User.find_by_client_code(params[:search])
    redirect_to edit_user_path(@user)
  else
    @users = User.fetch_with_brand(user_params)
  end
end

如果您倾向于将用户重定向到更多位置的编辑页面,您可以进一步重构它:

before_filter :redirect_to_edit, only: [:index, :some_other_method_name]

def index
  @users = User.fetch_with_brand(user_params)
end

def redirect_to_edit
  if params[:search].present?
    @user = User.find_by_client_code(params[:search])
    redirect_to edit_user_path(@user)
  end
end

你的瘦控制器现在已经启动了。

【讨论】:

  • 哇,感谢您提供非常详细的答案。好多了!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-17
  • 1970-01-01
  • 2021-08-17
  • 1970-01-01
  • 2019-06-12
相关资源
最近更新 更多