如何在控制器中使用两个版本的def更新?

时间:2015-04-15 10:35:59

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

如何在控制器中使用两个版本的def更新?

我有一个表单,应该允许管理员编辑比常规用户更多的变量。两者都使用相同的编辑视图。目前,我在控制器中有一个表单和def update,但无法区分管理员和非管理员。

所以现在我已经向控制器添加了两个不同的strong_params版本。但是如何根据用户的类型应用这些不同的版本?

其中一种方法是使用if else语句重写def update,如果是admin,则需要一个stong_params版本,如果是非admin,则需要另一个strong_params版本。但是,这里的问题是,我无法在此def更新上调用before_action,以测试它是否为管理员。出于安全原因,这可能是必要的。

所以我认为我需要在控制器中使用两种不同的def更新方法:1为管理员调用一个版本的strong_params并为其添加before_action。另一个非管理员使用另一个版本的strong_params(以及不同的before_action)。

我知道如何在控制器中构建它,但不知道如何在编辑视图中告诉表单使用哪个def更新(或者应该在def编辑中指定在控制器?)。 (事实上​​,我甚至不知道Rails如何知道它应该调用def更新(可能通过资源:路由中的用户?)。)我应该以某种方式编辑编辑视图中的<%= f.submit "Save changes"行吗? / p>

4 个答案:

答案 0 :(得分:2)

我建议在这种情况下使用表格。表单是表示模型的特殊抽象层。这是php-frameworks的术语(如Zend等)。在我的项目中,我通常称它为“类型”。例如:

型号:

class Post < ActiveRecord::Base

  validates :body, presence: true
  validates :title, presence: true

end

类型:

module BaseType
  extend ActiveSupport::Concern

  module ClassMethods

    def model_name
      superclass.model_name
    end

    def name
      superclass.name
    end
  end
end

class PostEditAdminType < Post # model Post is parent class for type
  include BaseType

  attr_accessor :current_user
  attr_accessible :title, :body # can edit both field title and body
end

class PostEditRegularType < Post
  include BaseType

  attr_accessor :current_user
  attr_accessible :body # can edit body field only
end

控制器

def update
  post = Post.find(params[:id])
  @post = if current_user.admin?
    post.becomes PostEditAdminType
  else
    post.becomes PostEditRegularType
  end

  if @post.update_attributes(params[:post])
    redirect_to past_path(@past)
  else
    f(:error)
    render :edit
  end
end

在我看来,这是非常好的做法和相同案例中的最佳架构。

答案 1 :(得分:1)

form_for只能发送到一个控制器动作。因此,您将需要form_for发送的第三个操作,并且此操作首先确定您是否为admin / non-admin,然后相应地将您发送到两个“def update methods”中的任何一个(使用参数)。例如,(这完全是伪代码)

def receive_form

# create variable 'type' that returns whether you are an admin or non-admin

   if type = "Admin"
      # send to your 'def update' for admins, whatever you've named this
      redirect_to admin_update_path
   else
      # send to your 'def update' for non-admins
      redirect_to non_admin_update_path
   end

end

答案 2 :(得分:1)

你可以使用强参数gem,它只允许一些属性。所以你可以写一些与

相关的代码
if current_user.admin?
  params.require(:user).allow :name, :role
else
  params.require(:user).allow :name
end

因此,如果您是管理员用户,则可以更改用户的角色,否则,您只能编辑该名称。

如果您希望普通用户只能编辑他/她的帐户,请结帐cancancan gem。它更有意义,对吗?如果您是普通用户,则只应更新您的帐户。

使用cancancan的示例代码:

class Ability
  include CanCan::Ability

  def initialize(user)
    user ||= User.new
    if user.admin?
      can :manage, :all
    else
      can :manage, User, id: user.id
    end
  end
end

如果您使用带有强参数的rails 4,请不要忘记sanitize your params

如果您想了解更多详情,请与我们联系。

答案 3 :(得分:0)

我的解决方案将有两个单独的模型,一个用于User,另一个用于Role用户。您可能在某些时候拥有的课程多于常规用户和管理员,因此这种方法可以简化长期添加更多角色的过程。

如何做到这一点:

  1. 创建具有一列Roles
  2. name模型
  3. User模型将具有has_and_belongs_to_many :roles关系
  4. Role模型将具有has_and_belongs_to_many :users关系
  5. 由于多对多关系,您应该创建一个连接表。连接表应以字母数字顺序命名。因此,您的迁移应该如下rails g migration CreateRolesUsersTable
  6. has_and_belongs_to_many语句创建自动使用连接表查找其值的变量。你可以看看这些。
  7. 您可以通过以下方式检查是否已正确设置:

    admin_role = Role.find_by(name: “admin”) user.roles.include?(admin_role)

  8. 您还可以将自己的方法添加到User模型中供自己使用。这是我的一个样本:

    
    def admin?
        self.is_role_by_name?("admin")
    end 

    def add_role(role) roles << role end

    def add_role_by_name(role_name) role = Role.find_by(name: role_name) add_role(role) end

    def remove_role(role) roles.delete(role) end

    def remove_role_by_name(role_name) role = Role.find_by(name: role_name) remove_role(role) end

    def is_role?(role) roles.to_a.include?(role) end

    def is_role_by_name?(role_name) role = Role.find_by(name: role_name) is_role?(role) end

  9. 现在,在您的视图中,您可以使用以下内容显示表单的某些字段:

    <% if @user.admin? %>
       input whatever here...
    <% end %>