如果select存在,则不要创建嵌套表单

时间:2018-01-06 02:05:16

标签: ruby-on-rails ruby postgresql

目前我正在开发一个药房系统,因此我想制定一个具有特定专业,患者和药物的处方。为此,我使用了一些像simple_form,chosen_rails和cocoon这样的宝石。但是,当我创建一个新的处方而没有填写专业领域,但选择一个专业,我得到以下错误:

> Started POST "/prescriptions" for 127.0.0.1 at 2018-01-05 21:08:42 -0300
Processing by PrescriptionsController#create as JS
  Parameters: {"utf8"=>"✓", "prescription"=>{"professional_id"=>"1", "professional_attributes"=>{"first_name"=>"", "last_name"=>"", "dni"=>"", "enrollment"=>"", "sector_id"=>"", "sector_attributes"=>{"sector_name"=>"", "description"=>"", "complexity_level"=>""}}, "patient_id"=>"1", "patient_attributes"=>{"first_name"=>"", "last_name"=>"", "dni"=>"", "patient_type_id"=>""}, "quantity_medications_attributes"=>{"0"=>{"medication_id"=>"", "quantity"=>"", "_destroy"=>"false"}}, "quantity_supplies_attributes"=>{"0"=>{"supply_id"=>"", "quantity"=>"", "_destroy"=>"false"}}, "observation"=>"", "date_received"=>"01/05/2018 9:08 PM"}, "commit"=>"Cargar y dispensar"}
  User Load (0.4ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2  [["id", 1], ["LIMIT", 1]]
  Patient Load (0.5ms)  SELECT  "patients".* FROM "patients" WHERE "patients"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
  Professional Load (0.4ms)  SELECT  "professionals".* FROM "professionals" WHERE "professionals"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
  PrescriptionStatus Load (0.4ms)  SELECT  "prescription_statuses".* FROM "prescription_statuses" WHERE "prescription_statuses"."name" = $1 LIMIT $2  [["name", "Dispensada"], ["LIMIT", 1]]
   (0.3ms)  BEGIN
  PatientType Load (0.5ms)  SELECT  "patient_types".* FROM "patient_types" WHERE "patient_types"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
   (0.3ms)  ROLLBACK
Completed 422 Unprocessable Entity in 36ms (ActiveRecord: 2.7ms)
> 
> 
>ActiveRecord::RecordInvalid - Validation failed: Professional first name can't be blank, Professional last name can't be blank, Professional dni can't be blank, Professional sector must exist:
  app/controllers/prescriptions_controller.rb:82:in `block in create'
  app/controllers/prescriptions_controller.rb:81:in `create' 

我的处方控制器是:

class PrescriptionsController < ApplicationController
  before_action :set_prescription, only: [:show, :edit, :update, :destroy]
  def new
    @prescription = Prescription.new
    @professionals = Professional.all
    @medications = Medication.all
    @supplies = Supply.all
    @patients = Patient.all
    @sectors = Sector.all
    @patient_types = PatientType.all
    @prescription.build_professional
    @prescription.professional.build_sector
    @prescription.build_patient
    @prescription.quantity_medications.build
    @prescription.quantity_supplies.build
  end

  def create
    @prescription = Prescription.new(prescription_params)
    if dispensing?
      @prescription.prescription_status = PrescriptionStatus.find_by_name("Dispensada")
      @prescription.date_dispensed = DateTime.now
    end
    @prescription.prescription_status = PrescriptionStatus.find_by_name("Pendiente") if loading?

    date_r = prescription_params[:date_received]
    @prescription.date_received = DateTime.strptime(date_r, '%d/%M/%Y %H:%M %p')

    respond_to do |format|
      if @prescription.save!
        flash.now[:success] = "La prescripción de "+@prescription.professional.full_name+" se ha creado correctamente."
        format.js
      else
        flash.now[:error] = "La prescripción no se ha podido crear."
        format.js
      end
    end
  end

  def prescription_params
      params.require(:prescription).permit(
  :observation, :date_received, 
  :professional_id, :patient_id, :prescription_status_id,                                             
  quantity_medications_attributes: [:id, :medication_id, :quantity, 
  :_destroy],
  quantity_supplies_attributes: [:id, :supply_id, :quantity, :_destroy],
  patient_attributes: [:id, :first_name, :last_name, :dni, :patient_type_id],
  professional_attributes: [:id, :first_name, :last_name, :dni, :enrollment, :sector_id,
  sector_attributes: [:id, :sector_name, :description, :complexity_level]                        ])
  end
end

我的模特是:

class Prescription < ApplicationRecord
  validates_presence_of :patient
  validates_presence_of :prescription_status
  validates_presence_of :professional

  belongs_to :professional
  belongs_to :patient
  belongs_to :prescription_status

  has_many :quantity_medications, :as => :quantifiable, dependent: :destroy, inverse_of: :quantifiable
  has_many :medications, :through => :quantity_medications
  has_many :quantity_supplies, :as => :quantifiable, dependent: :destroy, inverse_of: :quantifiable
  has_many :supplies, :through => :quantity_supplies


  accepts_nested_attributes_for :quantity_medications,
          :reject_if => :all_blank,
          :allow_destroy => true
  accepts_nested_attributes_for :quantity_supplies,
          :reject_if => :all_blank,
          :allow_destroy => true
  accepts_nested_attributes_for :medications
  accepts_nested_attributes_for :patient,
          :reject_if => :all_blank
  accepts_nested_attributes_for :professional,
          :reject_if => :all_blank
  def dispensed?
    self.prescription_status.is_dispense?
  end
end

class Professional < ApplicationRecord
  validates :first_name, presence: true
  validates :last_name, presence: true
  validates :dni, presence: true

  has_many :prescriptions
  belongs_to :sector

  accepts_nested_attributes_for :sector,
    :reject_if => :all_blank
end

class Sector < ApplicationRecord
  validates_presence_of :sector_name, presence: true
  validates_presence_of :complexity_level, presence: true  

  has_many :professionals
end

我的表格是:

  create_table "prescriptions", force: :cascade do |t|
    t.text "observation"
    t.datetime "date_received"
    t.datetime "date_dispensed"
    t.integer "prescription_status_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.bigint "professional_id"
    t.bigint "patient_id"
    t.index ["patient_id"], name: "index_prescriptions_on_patient_id"
    t.index ["professional_id"], name: "index_prescriptions_on_professional_id"
  end

  create_table "professionals", force: :cascade do |t|
    t.string "first_name"
    t.string "last_name"
    t.integer "dni"
    t.string "enrollment"
    t.string "address"
    t.string "email"
    t.string "phone"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.bigint "sector_id"
    t.index ["sector_id"], name: "index_professionals_on_sector_id"
  end

  create_table "sectors", force: :cascade do |t|
    t.string "sector_name"
    t.text "description"
    t.integer "complexity_level"
    t.string "applicant"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

最后,我的表单是:

_form.html.erb

<%= simple_form_for @prescription, remote: true, html: {class: "form-inline"} do |f| %>
  <%= f.error_notification %>
  <%= f.input :professional_id,
                    label: 'Doctor',
                    label_method: :full_name,
                    value_method: :id,
                    collection: @professionals,
                    include_blank: false,
                    input_html: { class: 'chosen-select', id: 'professional_id' },
                    prompt: 'Seleccione un doctor o cree uno'
  %><br><br>
  <%= f.hint 'No seleccione ningún doctor para crear uno nuevo.', id: 'professional-hint', style: "display: none" %>

  <%= f.simple_fields_for :professional, html: {class: "form-inline"} do |form_professional| %>
    <%= render "professional_fields", :f => form_professional %>
  <% end %><br>

  <%= f.input :patient_id,
                    label: 'Paciente',
                    label_method: :full_info,
                    value_method: :id,
                    collection: @patients,
                    include_blank: false,
                    input_html: { class: 'chosen-select', id: 'patient_id' },
                    prompt: 'Seleccione un paciente o cree uno'
  %><br><br>
  <%= f.hint 'No seleccione ningún paciente para crear uno nuevo.', id: 'patientHint', style: "display: none" %>

  <%= f.simple_fields_for :patient, html: { class: 'form-inline' } do |form_patient| %>
    <%= render "patient_fields", :f => form_patient %>
  <% end %><br>

  <div class='row'>
    <div class='col-md-6'>
      <label>Medicación</label>
      <div id="quantity-medications">
          <%= f.simple_fields_for :quantity_medications, html: {class: "form-inline"} do |form_quantity| %>
            <%= render "quantity_medication_fields", :f => form_quantity %>
          <% end %>
          <div class="links">
            <%= link_to_add_association f, :quantity_medications, class: 'btn btn-default btn-sm' do %>
                <%= content_tag(:span, '', class: 'glyphicon glyphicon-plus') %>
            <% end %>
          </div>
      </div>
    </div>
    <div class='col-md-6'>
      <label>Suministro</label>
      <div id="quantity-supplies">
          <%= f.simple_fields_for :quantity_supplies, html: {class: "form-inline"} do |form_quantity| %>
            <%= render "quantity_supply_fields", :f => form_quantity %>
          <% end %>
          <div class="links">
            <%= link_to_add_association f, :quantity_supplies, class: 'btn btn-default btn-sm' do %>
                <%= content_tag(:span, '', class: 'glyphicon glyphicon-plus') %>
            <% end %>
          </div>
      </div>
      <br>
    </div>
  </div>
  <div class='row'>
    <div class='col-md-6'>
      <%= f.input :observation, label: 'Observaciones', as: :text,
          :input_html => { :cols => 46  , :rows => 2 } %>
    </div>
    <div class='col-md-6'>
      <%= f.input :date_received, label: 'Fecha recibida',
                                  as: :string,
                                  :placeholder => "Seleccionar fecha",
                                  input_html: {class: "form-control", required: true,
                                  value: "#{@prescription.date_received.strftime("%d/%m/%y %H:%M") unless @prescription.new_record?}"},
                                  html5: false
                                  %><br><br>
    </div>
  </div>
<% end %>

_professional_fields.html.erb

<div id='form-professional'>
  <%= f.input :first_name, placeholder: 'Nombre', required: true, label: false %>
  <%= f.input :last_name, placeholder: 'Apellido', required: true, label: false %>
  <%= f.input :dni, placeholder: 'DNI', required: true, label: false, input_html: { min: '10000', max:'99999999' } %>
  <%= f.input :enrollment, placeholder: 'Matrícula', label: false, input_html: { min: '1000', max:'9999' } %>
  <%= f.input :sector_id,
                    label: false,
                    label_method: :sector_name,
                    value_method: :id,
                    collection: @sectors,
                    include_blank: false,
                    input_html: { class: 'chosen-select', id: 'sector_id', onchange: "" },
                    prompt: 'Seleccione un sector o cree uno nuevo'
  %>
  <br><br>
  <%= f.hint 'No seleccione ningun sector para crear uno nuevo', id: 'sector-hint', style: "display: none" %>

  <%= f.simple_fields_for :sector, html: { class: 'form-inline' } do |fs| %>
    <div id='form-sector'>
      <label>Nuevo sector</label>
      <%= fs.input :sector_name, placeholder: 'Nombre sector', required: true, label: false %>
      <%= fs.input :description, as: :string, placeholder: 'Descripción', required: true, label: false %>
      <%= fs.input :complexity_level, label: false, required: true, input_html: { min: '0', max:'10' } %>
    </div>
  <% end %>
</div>

我想创建一个新的处方,只选择所选择的专业人士,避免创建新的专业人士。我有另一个名为Medication的模型,其中嵌套形式如MedicationBrand和子嵌套形式名为Laboratory,我想做的工作完美无缺。

我在这个模型中做错了什么?

1 个答案:

答案 0 :(得分:1)

您拥有:reject_if => :all_blank设置,但由于您的专业属性中的子嵌套,这并不像您预期​​的那样有效。 http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html的文档中解释了:all_blank选项:

REJECT_ALL_BLANK_PROC   =   proc { |attributes| attributes.all? { |key, value| key == "_destroy" || value.blank? } }

您的专业属性如下所示进入控制器:

"professional_attributes"=>{"first_name"=>"", "last_name"=>"", "dni"=>"", "enrollment"=>"", "sector_id"=>"", "sector_attributes"=>{"sector_name"=>"", "description"=>"", "complexity_level"=>""}}

sector_attributes值不是空白,因为它是一个子嵌套哈希,恰好碰巧拥有所有空白值。因此,professional_attributes不会被拒绝,并会尝试处理它们。

对于:reject_if,您希望使用自己的自定义proc来检查professional_attributes的值,并将任何子哈希处理为空白。

这适用于您的输入属性:

:reject_if => proc { |attributes| attributes.all? { |key, value| key == "_destroy" || value.blank? || (value.is_a?(Hash) && value.values.all?(&:blank?)) } }

如果你想进一步使用哈希的子嵌套,你需要考虑一个递归函数,虽然我建议你可能需要重新考虑你的设计,为什么你在单一形式中有如此多的嵌套水平,是否真的有必要。

希望有所帮助!