【问题标题】:Rails best practices with multiple update forms for one model [closed]一个模型的多个更新表单的 Rails 最佳实践 [关闭]
【发布时间】:2018-01-04 18:00:04
【问题描述】:

我有一个关于模型更新的一般性问题,我想这与如何以尽可能“Rails-y”的方式组织模型和控制器操作的更大问题有关。对于给定的模型Profile(以及它的关联),我有多个更新表单。例如,一种形式可能用于更新first_namelast_name 等基本信息,另一种可能用于更新agejobs 等内容。在我的例子中,有 8 种不同的形式,它们比我给出的例子要复杂一些。我想知道处理此设置的不同方式之间的权衡。过去我尝试了 3 种不同的方法:

1) 具有自定义控制器操作(在 Profiles 控制器中)来处理这些不同的更新表单。例如。

#views
<%= simple_form_for @profile, url: profile_update_name_path(@profile), method: :patch, remote: true  do |f| %>
    # the form fields
<% end %>

<%= simple_form_for @profile, url: profile_update_basics_path(@profile), method: :patch, remote: true  do |f| %>
    # other form fields
<% end %>


#profiles controller
def update_name
  if @profile.update
    # do some stuff
  end
end

def update_basics
  if @profile.update
    # do some different stuff
  end
end

2) 传入一个额外的参数作为表单 url 的一部分,以区分对表单的响应。例如。

#views
<%= simple_form_for @profile, url: profile_path(update_form: “name-form”), method: :patch, remote: true  do |f| %>
    # the form fields
<% end %>

<%= simple_form_for @profile, url: profile_path(update_form: “basics-form”), method: :patch, remote: true  do |f| %>
    # the form fields
<% end %>


#profiles controller
def update
  if params[:update_form] == "name-form"
    if @profile.update
      # do some stuff
    else
      # handle errors
    end
  elsif params[:update_form] == "basics-form"
    if @profile.update
      # do some different stuff
    else
      # handle different errors
    end
  end
end

3) 将模型分解为单独的更小的类,这些类都通过 has_one、belongs_to 关系以某种方式连接到父模型 Profile 模型。例如。

#profile.rb
has_one :name_information, dependent: :destroy
has_one :basic_information, dependent: :destroy
has_many :jobs, through: :basic_information

#name_information.rb
# has attributes: first_name, last_name
belongs_to :profile, touch: true

#basic_information.rb
# has attributes: age
belongs_to :profile, touch: true
has_many :jobs, dependent: :destroy

accepts_nested_attributes_for :jobs, allow_destroy: true

#views
# each form now points to the update action for it's own controller rather than using the profiles_controller
<%= simple_form_for [@profile, @name_information], url: profile_name_information_path(@profile, @name_information), method: :patch, remote: true  do |f| %>
    # the form fields
<% end %>

<%= simple_form_for [@profile, @name_information], url: profile_name_information_path(@profile, @name_information), method: :patch, remote: true  do |f| %>
    # the form fields
<% end %>

我在使用所有这些技巧方面取得了一些成功,但老实说,我不确定它们中的任何一个都是很好的练习。有人对处理这种设置的最佳“Rails”方式有什么想法吗?将事物分解为更小的类的第三种选择似乎对我来说可能是最好的,但它对于大型应用程序的吸引力也较小,因为更改这些基本模型将对整个应用程序产生重大影响。这也让我想知道加载一堆较小的关联对象的效率,这些对象很容易成为一个类的一部分。

【问题讨论】:

  • 我投票结束,因为这是一个非常基于意见的问题。即使对于特定情况,也没有明确的正确/错误答案,更不用说一般情况了。我的回答是:最适合你的。这取决于。
  • 但是,我认为值得一提的是选项 4:如果您只有一个 update 操作,其中包含用于更新的白名单属性,并且每个表单只发送这些属性的子集,该怎么办? patch 请求不需要是完整组参数;但仅限于您想要更改的内容。
  • @TomLord 我同意。我对在 StackOverflow 上回答有点陌生,但无论如何添加了一个答案。这种类型的问题通常在这里回答不好吗?
  • @DerekHopper 这样的问题通常以基于意见的方式结束,但我认为您的回答是您在这种情况下所能给出的最佳答案。我倾向于避免回答此类问题,但我认为您在这里的回答很有价值!
  • @TomLord 很公平。我意识到就“正确答案”而言,这可能有点边界。但我认为在一种行动方案可能比另一种更好的方面仍有待补充。同样关于您的选项 4,我确实需要至少以某种方式确定正在提交哪个表单,因为每个表单的实际 ajax 响应会略有不同。

标签: ruby-on-rails ruby forms crud


【解决方案1】:

我的猜测是对此有很多不同的看法。有些人可能会说把所有东西都放在一个模型和一个控制器中,然后就可以完成了。但是,我认为当您说:

将事物分解成更小的类的第三种选择似乎对我来说可能是最好的,但它对于大型应用程序的吸引力也较小,因为更改这些基本模型将对整个应用程序产生重大影响。

我还有一个问题你可以问自己:

  • 这些表单会有不同的验证集吗?
  • 如果遇到问题,哪种方法更容易调试?

如果他们这样做了,而您只有一个模型,您可能会发现自己在与一大堆条件验证作斗争。何时运行哪些验证可能会变得不清楚。

就个人而言,结合 1 和 3 的方法似乎不错。您可以立即获得一些好处。

  • 每个表单都有自己的路由和允许的参数。如果您需要知道正在填写哪些表格,这将很有帮助。您还可以禁止在基本信息表单上更新 first_name。
  • 拥有多个控制器操作似乎在 3 年后更容易理解。缺点是如果你不小心,可能很容易复制很多东西。
  • 当您有多个控制器操作时,调试可能会更容易。如果有人遇到问题,您或许可以毫不费力地清楚地识别出他们正在使用的表单。

可能的解决方案

有一个我喜欢的模式,类似于你在第三个想法中描述的模式。您将保留您的 Profile 模型,但为每个表单使用一个表单对象。表单对象将存储特定于每个表单的验证。每个控制器操作都将使用不同的表单对象来处理请求。

基本上,Profile 模型只能通过表单对象之一进行更改。

这是一个例子:

class Profile < ActiveRecord::Base
end

class BasicInformation
  include ActiveModel::Model
  attr_accessor :age
  validates :age, presence: true
end

def ProfilesController < ApplicationController
  before_action do
    @profile = Profile.find(params[:id])
  end

  def update_name
    name_params = params.require(:profile).permit(:first_name, :last_name)
    if NameInformation.new(name_params).valid?
      update_profile(name_params)
    end
  end

  def update_basic_information
    basic_information_params = params.require(:profile).permit(:age)
    if BasicInformation.new(basic_information_params).valid?
      update_profile(name_params)
    end
  end

  private

  def update_profile(params)
    @profile.update(params)
  end
end

我不确定我是否会使用这种确切的方式,但希望主要思想是明确的。这也不是唯一的做事方式。就像我说的,人们对此会有不同的看法。这实际上取决于您需要处理的复杂程度。

将所有东西都放在一个模型和一个控制器中可能对您来说非常适合。

如果您希望深入了解表单对象,这些年来我已经看到了许多处理表单对象的不同方法。这是一个列表:

【讨论】:

  • 非常感谢,我同意上面的用户,这绝对有价值。使用表单对象对我来说是一种全新的模式,所以我现在来看看。为了清楚起见,看起来表单对象不会(不能?)由数据库中的相应表支持?该对象将仅用于随后将数据传递到连接到数据库的 Profile 对象中?
  • @Brett 正确。它将委托给您的 ActiveRecord 对象来保存数据。表单对象是一种将复杂表单提交与模型分离的方法。当您对一个表单有一堆逻辑时,它会有所帮助,但您不需要/不希望该逻辑污染底层模型。请记住,这对于您的情况可能有点过分。你必须做出这个决定。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-01-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-06
  • 1970-01-01
相关资源
最近更新 更多