Rails accepts_nested_attributes_for + HABM返回一个空字符串

时间:2015-10-20 01:07:15

标签: ruby-on-rails nested-attributes has-and-belongs-to-many

看我的问题:

class MedicalRecord < ActiveRecord::Base
  has_many :evaluations, dependent: :destroy

  accepts_nested_attributes_for :evaluations, allow_destroy: true, reject_if: :all_blank
end

class Evaluation < ActiveRecord::Base
  belongs_to :medical_record

  has_and_belongs_to_many :edemas

  validates :description, presence: true
end

我的表单显示具有多个属性的选择字段。

<%= form_for @medical_record do |f| %>
  <%= f.fields_for :evaluations do |e| %>
    <%= e.text_field :description %>

    <%= e.collection_select :edema_ids, Edema.all.order(:title), :id, :title,
      { }, multiple: true %>
  <% end %>      
<% end %>

当我没有选择任何'水肿'时,表单会发送一个带有一个空字符串的数组。因此,reject_if会返回false,我需要填写说明字段。在这种情况下,reject_if应该返回true

我可以做些什么呢?

非常感谢

2 个答案:

答案 0 :(得分:1)

如果您的目标只是在:edema_ids数组中没有空字符串,则可以将include_hidden: false传递给collection_select

那就是说,如果你最终想要取消选择&#34;这将导致问题。现有评估的所有水肿,因为如果它的值真的为空(因为它是多选),浏览器根本不会发送:edema_ids数组,原因大致相同。发送未经检查的复选框值。在空数组中包含空字符串是表单帮助程序处理此浏览器行为的方法。

相信建议允许HTML表单提交空字符串数组元素(因此浏览器和Rails可以在这种情况下按预期运行),并使用ActiveRecord& {39} reject_if: :all_blank关联:evaluations(假设您确实正在寻找的行为)。

你可以通过在传递到控制器之后的某个时刻从:edema_ids中删除空字符串元素而不会遇到空字符串数组挂起来实现这一点。由:all_blank进行评估。

类似于:

# Inside MedicalRecordsController

def create
  @medical_record = MedicalRecord.create(medical_record_params)
end

def update
  @medical_record = MedicalRecord.find(params[:id])
  @medical_record.update_attributes(medical_record_params)
end

private

  def medical_record_params
    # I assume you're using strong params to control what can be passed
    # through the controller.  If so, manipulate the params *after*     
    # calling .require() and .permit() on the params hash
    remove_empty_string_from_edema_ids(params)
  end

  def remove_empty_string_from_edema_ids(params_hash) # Use a better name than this
    params_hash[:evaluations].each do |evaluation|
      # Don't forget to use guard clause to prevent calling a
      # method on nil if :edema_ids is not present in evaluation
      evaluation[:edema_ids].reject!(&:empty) if evaluation[:edema_ids]
    end
  end

如果:all_blank确实全部为空,那么(或类似的,有很多方法可以实现相同的结果)会 - 除了正确导致true返回:evaluation之外 - 导致将真正空的数组传递给评估的edema_ids=()方法;由于一些ActiveRecord关联魔法,这将导致从:edemas实例中删除所有Evaluation

答案 1 :(得分:0)

您可能希望编写自定义reject_if方法。如果您提供符号,ActiveRecord将在当前类中查找具有该名称的方法,并传递evaluation的已提交属性哈希值。

class MedicalRecord < ActiveRecord::Base
  accepts_nested_attributes_for :evaluations, allow_destroy: true, reject_if: :essentially_blank

  def essentially_blank(attributes)
    attributes[:description].blank? && attributes[:edema_ids][0].blank?
  end
end

请注意,虽然[""].blank?为false,但"".blank?为真,这就是我获取数组的第一个对象并检查其blank的原因。

您可以查看the Rails API以查看有关如何在reject_if上完成自定义行为的更多示例。