nester attributes for has_many are not shown if the form fails validation

穿精又带淫゛_ 提交于 2019-12-11 15:14:13

问题


If form fails validation, fields for has_many association, disappears from view:

my new action looks like

def new
  @realty = Realty.new
  @realty.build_user
  @realty.build_address
  #user, address are has_one association, and they stay in view after failed validation
  6.times { @realty.realty_images.build } # and this one vanishes away 
  respond_to do |format|
    format.html # new.html.erb
    format.json { render json: @realty }
  end
end

if I add to my form this snippet

-  if @realty.realty_images.empty?
  - 6.times { @realty.realty_images.build }

fields are shown up again, but it is a little rough

I tried

6.times { @realty.realty_images.build } if @realty.realty_images.empty?

or

6.times { @realty.realty_images.build }
6.times { @realty.realty_images.build } if @realty.realty_images.empty?

in controller, but it doesn't works, and fields disappears again on failed validation.

create action:

def create
    @realty = Realty.new(params[:realty]) 
    respond_to do |format|
      if @realty.save
        format.html { redirect_to @realty, notice: 'Realty was successfully created.' }
        format.json { render json: @realty, status: :created, location: @realty }
      else
        format.html { render action: "new" }
        format.json { render json: @realty.errors, status: :unprocessable_entity }
      end
    end
 end

my -form

= simple_form_for(@realty) do |f|
  = f.error_notification

  .form-inputs
    = f.input :offer_type
    = f.input :realty_type
    = f.input :rent_type
    = f.input :price
    = f.input :description
    = f.input :state, as: :hidden
    = f.input :number_of_rooms
    = f.input :floor
    = f.input :service, as: :hidden
    = f.simple_fields_for :address do |address_f|
      = address_f.input :city, :required => true
      = address_f.input :state, :required => true
      = address_f.input :street, :required => true
    - unless user_signed_in?
      = f.simple_fields_for :user do |user_f|  
        = user_f.input :name, :autofocus => true, :required => true
        = user_f.input :phone, :required => true
        = user_f.input :email, :required => true
        = user_f.input :password, :required => true
        = user_f.input :password_confirmation, :required => true
    -  if @realty.realty_images.empty?
      - 6.times { @realty.realty_images.build } 
    = f.simple_fields_for :realty_images do |image_f|
      = image_f.input :image, as: :file

  .form-actions
    = f.button :submit

realty model

class Realty < ActiveRecord::Base
  attr_accessible :description, :floor, :user_id, :number_of_rooms, :price, :service, :state, :realty_images_attributes, :address_attributes, :user_attributes, :offer_type, :realty_type, :rent_type

  belongs_to :user, :dependent => :destroy, :class_name => "User"
  has_many :realty_images, :dependent => :destroy
  has_one :address, :dependent => :destroy

  validates :offer_type, :presence => true, :length => { :in => 1..256 }
  validates :realty_type, :presence => true, :length => { :in => 1..256 }
  validates :state, :presence => true, :length => { :in => 1..256 }
  validates :description, :length => { :maximum => 2000 }
  validates :service, :presence => true, :length => { :in => 1..256 }
  accepts_nested_attributes_for :realty_images
  accepts_nested_attributes_for :user
  accepts_nested_attributes_for :address
end

回答1:


I think you are confusing two actions: new and create. The "new" action is invoked only when accessing the blank form at /realties/new. And the "create" action is invoked when submitting the form.

You add 6 sample images only in "new" action. In create action, Realty.new will discard images that are blank (not filled in). This is why you will see them disappear. You will need to re-add them on validation error. You could extract the common setup into a method:

class RealtiesController
  def new
    @realty = Realty.new
    @realty.populate
  end

  def create
    @realty = Realty.new(params[:realty])
    if @realty.save
      # ...
    else
      @realty.populate
      render 'new'
    end
  end
end

class Realty
  # you could also place it in the controller
  # if you feel it fits better there
  def populate
    6.times{ self.realty_images.build }
  end
end



回答2:


To keep the failed object still present in #new, you can just avoid creating a new one if old one existed. Like this:

@realty = || Realty.new

This way #new will use the old failed object instead of the new.

For @realty object, it will work. But for further associations such as user and address, I have not done similar things before so you need to verify it by yourself.

p.s The code relies on the instant variable that's why I'm concerned how the obj is saved. Hope this could be of a little help :)




回答3:


@Billy Chan Thank you for your advice, you really help me. My solution of this issue, is a little rough but it works well

in create action a add

6.times { @realty.realty_images.build } if @realty.realty_images.empty?

if @realty.save return false

it looks like this

 def create
    @realty = Realty.new(params[:realty]) 
    @realty.user = current_user if user_signed_in?
    respond_to do |format|
      if @realty.save
        format.html { redirect_to @realty, notice: 'Realty was successfully created.' }
        format.json { render json: @realty, status: :created, location: @realty }
      else
        6.times { @realty.realty_images.build } if @realty.realty_images.empty?
        format.html { render action: "new" }
        format.json { render json: @realty.errors, status: :unprocessable_entity }
      end
    end
  end


来源:https://stackoverflow.com/questions/16413271/nester-attributes-for-has-many-are-not-shown-if-the-form-fails-validation

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!