How to get Rails build and fields_for to create only a new record and not include existing?

后端 未结 4 751
时光取名叫无心
时光取名叫无心 2020-12-13 04:41

I am using build, fields_for, and accepts_nested_attributes_for to create a new registration note on the same form as a new registrati

相关标签:
4条回答
  • 2020-12-13 05:16

    EDIT: My previous answer (see below) was bugging me because it's not very nice (it still loops through all the other registration_notes needlessly). After reading the API a bit more, the best way to get the behaviour the OP wanted is to replace:

    <%= r.fields_for :registration_notes do |n| %>
    

    with:

    <%= r.fields_for :registration_notes, @registration.registration_notes.build do |n| %>
    

    fields_for optionally takes a second parameter which is the specific object to pass to the builder (see the API), which is built inline. It's probably actually better to create and pass the new note in the controller instead of in the form though (just to move the logic out of the view).


    Original answer (I was so close):

    Just to clarify, you want your edit form to include a new nested registration note (and ignore any other existing ones)? I haven't tested this, but you should be able to do so by replacing:

      <%= r.fields_for :registration_notes do |n| %>
    

    with:

      <%= r.fields_for @registration.registration_notes.build do |n| %>
    

    EDIT: Okay, from a quick test of my own that doesn't work, but instead you can do:

    <%= r.fields_for :registration_notes do |n| %>
      <%= n.text_area :content if n.object.id.nil? %>
    <% end %>
    

    This will only add the text area if the id of the registration note is nil (ie. it hasn't been saved yet).

    Also, I actually tested this first and it does work ;)

    0 讨论(0)
  • 2020-12-13 05:20

    Thank you so much for your help as I said in my post the only problem with the approach from "Zaid Crouch"(I don't know how to make a reference to a user hehe) is that if the form has error fields the form will be clear and boom after the page reloading you'll have nothing filled in your form and can you imagine if you form is like 20 or 30 fields that would be a terrible user experience of course

    Here is my solution that works with validation models:

    class Registration < ActiveRecord::Base
      attr_accessible :foo, :bar, :registration_notes_attributes
      has_many :registration_notes
      has_one :new_registration, class_name: 'RegistrationNote'
      accepts_nested_attributes_for :new_registration
    end
    
    class RegistrationsController < ApplicationController
      def edit
       @registration = Registration.find(params[:id])
       @registration.build_new_registration
      end
    end
    
    <%= form_for @registration do |r| %>
      <%= r.text_field :foo %>
      <%= r.text_field :bar %>
      <%= r.fields_for :new_registration do |n| %>
        <%= n.text_area :content %>
      <% end %>
    <% end %>
    

    I'm using simple_form in my example if you want to see the same working with validations and transaction take a look at the complete post here: http://elh.mx/ruby/using-simple_form-for-nested-attributes-models-in-a-has_many-relation-for-only-new-records/

    0 讨论(0)
  • 2020-12-13 05:23

    If you want to create a new registration form on your edit action, you can just instantiate a new registration_note object. Right now, your form is for the existing registration object.

    I believe this is what you want:

    class RegistrationsController < ApplicationController
      def edit
        @new_registration_note = RegistrationNote.new
        @registration = Registration.find(params[:id])
        @registration.registration_notes.build
      end
    end
    

    In your view, you should pass a hidden param that references the registration record id:

    <%= form_for @new_registration_note do |r| %>
      <%= r.hidden_field :registration_id, :value => @registration.id  %>
      <%= r.text_area :content  %>
    <% end %>
    

    Now, you can create your new registration note that belongs to @registration. Make sure you have a column in your registration_notes table to point to the registration. You can read more about associations here: http://guides.rubyonrails.org/association_basics.html

    0 讨论(0)
  • 2020-12-13 05:28

    As Heriberto Perez correctly pointed out the solution in the most upvoted answer will simply discard everything if there's a validation error on one of the fields.

    My approach is similar to Heriberto's but nevertheless a bit different:

    Model:

    class Registration < ActiveRecord::Base
      has_many :registration_notes
      accepts_nested_attributes_for :registration_notes
    
      # Because 0 is never 1 this association will never return any records.
      # Above all this association don't return any existing persisted records.
      has_many :new_registration_notes, -> { where('0 = 1') }
                                      , class_name: 'RegistrationNote'
      accepts_nested_attributes_for :new_registration_notes
    end
    

    Controller:

    class RegistrationsController < ApplicationController
      before_action :set_registration
    
      def edit        
        @registration.new_registration_notes.build
      end
    
      private
    
      def set_registration
        @registration = Registration.find(params[:id])
      end
    
      def new_registration_params
        params.require(:registration).permit(new_registrations_attributes: [:content])
      end
    end
    

    View:

    <%= form_for @registration do |r| %>
      <%= r.text_field :foo %>
      <%= r.text_field :bar %>
      <%= r.fields_for :new_registration_notes do |n| %>
        <%= n.text_area :content %>
      <% end %>
    <% end %>
    
    0 讨论(0)
提交回复
热议问题