Rails accepts_nested_attributes_for + HABM returns 一个包含空字符串的数组
Rails accepts_nested_attributes_for + HABM returns an array with empty string
查看我的问题:
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
我的表单显示 select 个具有多个属性的字段。
<%= 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 %>
当我不select任何'edema'时,表单发送一个包含一个空字符串的数组。因此,reject_if
returns false
和我需要填写描述字段。在这种情况下,reject_if
应该 return true
。
我该怎么做才能让它也起作用?
非常感谢
您可能想要编写自定义 reject_if
方法。如果您提供符号,ActiveRecord 会在当前 class 中查找具有该名称的方法,并为 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?
为假 "".blank?
为真,这就是我获取数组的第一个对象并检查其 blank
-ness 的原因。
您可以查看 the Rails API 以查看有关如何在 reject_if
上完成自定义行为的更多示例。
如果您的目标只是让 :edema_ids
数组中没有空字符串,您可以将 include_hidden: false
传递给 collection_select
。
也就是说,如果您最终想要 "deselect" 现有评估的所有水肿,这将导致问题,因为如果 :edema_ids
数组的值确实为空,浏览器根本不会发送它(因为它是多个 select),出于大致相同的原因,它不会发送未选中的复选框值。在原本为空的数组中包含空字符串是表单助手处理此浏览器行为的方式。
我相信允许HTML表单提交空字符串数组元素是明智的(这样浏览器和Rails都可以按预期运行在这种情况下),并在 :evaluations
关联上使用 ActiveRecord 的 reject_if: :all_blank
(假设这确实是您正在寻找的行为)。
您可以通过在将 :edema_ids
传递到控制器之后但在对其求值之前的某个时刻从 :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
到 return true
如果 :evaluation
truly is all blank — 导致将一个真正的空数组传递给评估的 edema_ids=()
方法;由于一些 ActiveRecord 关联魔法,这将导致从该 Evaluation
实例中删除所有 :edemas
。
查看我的问题:
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
我的表单显示 select 个具有多个属性的字段。
<%= 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 %>
当我不select任何'edema'时,表单发送一个包含一个空字符串的数组。因此,reject_if
returns false
和我需要填写描述字段。在这种情况下,reject_if
应该 return true
。
我该怎么做才能让它也起作用?
非常感谢
您可能想要编写自定义 reject_if
方法。如果您提供符号,ActiveRecord 会在当前 class 中查找具有该名称的方法,并为 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?
为假 "".blank?
为真,这就是我获取数组的第一个对象并检查其 blank
-ness 的原因。
您可以查看 the Rails API 以查看有关如何在 reject_if
上完成自定义行为的更多示例。
如果您的目标只是让 :edema_ids
数组中没有空字符串,您可以将 include_hidden: false
传递给 collection_select
。
也就是说,如果您最终想要 "deselect" 现有评估的所有水肿,这将导致问题,因为如果 :edema_ids
数组的值确实为空,浏览器根本不会发送它(因为它是多个 select),出于大致相同的原因,它不会发送未选中的复选框值。在原本为空的数组中包含空字符串是表单助手处理此浏览器行为的方式。
我相信允许HTML表单提交空字符串数组元素是明智的(这样浏览器和Rails都可以按预期运行在这种情况下),并在 :evaluations
关联上使用 ActiveRecord 的 reject_if: :all_blank
(假设这确实是您正在寻找的行为)。
您可以通过在将 :edema_ids
传递到控制器之后但在对其求值之前的某个时刻从 :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
到 return true
如果 :evaluation
truly is all blank — 导致将一个真正的空数组传递给评估的 edema_ids=()
方法;由于一些 ActiveRecord 关联魔法,这将导致从该 Evaluation
实例中删除所有 :edemas
。