Forced to ask for help with saving nested models on polymorphic association. I'm missing something but can't figure out what.
Everithing pretty straightforward. There is Address which can have multiple Phones.
So models are
class Address < ActiveRecord::Base
has_many :phones, as: :phoneable
accepts_nested_attributes_for :phones, allow_destroy: true
validates :city, :street, :building, :name, presence: true
end
and
class Phone < ActiveRecord::Base
belongs_to :phoneable, polymorphic: true
validates :number, :extension, presence: true
end
addresses_controller.rb
def new
#address = Address.new
#phone = #address.phones.build
authorize #address
end
def create
#address = Address.create(address_params)
authorize #address
if #address.save
binding.pry
flash[:success] = "Address #{#address.name} created"
redirect_to address_path(#address)
else
flash.now[:danger] = 'Failed'
render :new
end
end
def address_params
params.require(:address).permit(:name, :street, :building, :city, phones_attributes: [:id, :number, :extension, :details] )
end
/app/views/address.html.erb
<div class="row">
<div class="col-md-12">
<%= form_for(#address, html: {class: 'form-horizontal', role: 'form'}) do |f| %>
<%= render 'shared/errors', obj: #address, model_name: 'addresses' %>
<div id="create-form">
<div class="form-group">
<div class="control-label col-md-4">
<%= f.label :city, 'Город' %>
</div>
<div class="col-md-4">
<%= f.select(:city, options_for_select(['Moscow', 'Samara']), {}, {class: "form-control"}) %>
</div>
</div>
<div class="form-group">
<div class="control-label col-md-4">
<%= f.label :street, 'Street' %>
</div>
<div class="col-md-4">
<%= f.text_field :street, class: 'form-control' %>
</div>
</div>
<div class="form-group">
<div class="control-label col-md-4">
<%= f.label :building, 'Building' %>
</div>
<div class="col-md-4">
<%= f.text_field :building, class: 'form-control' %>
</div>
</div>
<div class="form-group">
<div class="control-label col-md-4">
<%= f.label :name, 'Place name' %>
</div>
<div class="col-md-4">
<%= f.text_field :name, class: 'form-control' %>
</div>
</div>
<div class="form-group">
<div class="control-label col-md-4">
<%= f.label :phones, 'Phone' %>
</div>
<div class="col-md-4">
<%= f.fields_for :phone do |phone_form| %>
<%= phone_form.text_field :number, class: 'form-control' %>
</div>
</div>
<div class="form-group">
<div class="control-label col-md-4">
<%= phone_form.label :extension, 'Ext.' %>
</div>
<div class="col-md-4">
<%= phone_form.text_field :extension, class: 'form-control' %>
</div>
<% end %>
</div>
<div class="form-group">
<div class='col-md-offset-4 col-md-6'>
<%= f.submit #address.new_record? ? 'Add' : 'Update', class: 'btn btn-primary btn-md' %>
</div>
</div>
</div>
<% end %>
</div>
</div>
First issue I encountered with is if I set key :phones instead :phone into the following line <%= f.fields_for :phone do |phone_form| %> my phone text fields don't render in view but they should. One user emphasized this moment here https://stackoverflow.com/a/3328041/2049476
if I use :phone everything somehow works fine but seems like it's wrong.
And the second one.
Phone object doesn't save in DB, when I create new address or edit current I succeed but phone doesn't show any validation errors if I leave all it fields blank.
Here what I have in params hash
{"utf8"=>"✓",
"authenticity_token"=>"inwXr3Ev/Aj/hZRY2IadizDHDgdSFo2zFhY9DAvysfFu3jjD9AS66esKVsTzEuKo2WC46YQt6HnOKTgInvfUEg==",
"address"=>{"city"=>"Moscow", "street"=>"ul. Tsentralnaya d. 4 kv. 220", "building"=>"1212", "name"=>"Astoria", "phone"=>{"number"=>"9215555555", "extension"=>"111"}},
"commit"=>"Add",
"controller"=>"addresses",
"action"=>"create"}
What am I missing?
Try answer for 2 issues:
The correct way is to pass :phones, and then phone as variable to field_for, like is was done here:
<%- #address.phones.each.with_index do |phone, index| %>
<%- f.fields_for :phones, phone do |phone_form| %>
<%- end %>
<%- end %>
Should be resolved as of the 1-st question, since the fields shoudl sent ot server via params not a phone hash, but phones_attributes array of hashes, in order to accepts_nested_attributes_for could accept phones:
phones_attributes: [{ ... },{ ... }]
Related
I have a model for Organisation like
class Organisation
include ActiveModel::Model
attr_accessor :orguid,
:title, :firstname, :lastname, :role, :telephone, :extension, :email,
:name, :branch, :address1, :address2, :address3, :city, :state, :country, :zip
end
In my controller I have the following actions:
# frozen_string_literal: true
require 'cgi'
require 'json'
class OrganisationsController < ApplicationController
include Secured
before_action :set_api, only: %i[dashboard create]
before_action :user_info, only: %i[dashboard register]
def dashboard
#registration = #api.registered?
end
def register
#organisation = Organisation.new
end
def create
organisation_params
register_data = params[:organisation].to_h
register_data['oruid'] = org_uid
#api.register(register_data)
end
private
def set_api
#api = CoreApi.new(org_uid)
end
def user_info
#user_info = session[:userinfo].to_h
end
def org_uid
CGI.escape(user_info['uid'])
end
def organisation_params
params.require(:organisation).permit!
end
end
in my register.html.erb I have:
<h1> Register Your Organisation</h1>
<%= form_with model: #organisation, url: org_register_path do |f| %>
<div class="container">
<h2>Your Details</h2>
<div class="form-row">
<div class="form-group col-md-2">
<%= f.label :title %>
<%= f.text_field :title, class: 'form-control' %>
</div>
<div class="form-group col-md-5">
<%= f.label :first_name %>
<%= f.text_field :firstname, class: 'form-control' %>
</div>
<div class="form-group col-md-5">
<%= f.label :last_name %>
<%= f.text_field :lastname, class: 'form-control' %>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-12">
<%= f.label :role %>
<%= f.text_field :role, class: 'form-control' %>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-4">
<%= f.label :telephone %>
<%= f.telephone_field :telephone, class: 'form-control' %>
</div>
<div class="form-group col-md-2">
<%= f.label :extension %>
<%= f.text_field :extension, class: 'form-control' %>
</div>
<div class="form-group col-md-6">
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control', readonly:'', value: #user_info['info']['name'] %>
</div>
</div>
</div>
<div class="container">
<h2>Organisation Details</h2>
<div class="form-row">
<div class="form-group col-md-6">
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
</div>
<div class="form-group col-md-6">
<%= f.label :branch %>
<%= f.text_field :branch, class: 'form-control' %>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-12">
<%= f.label :address_line_1 %>
<%= f.text_field :address1, class: 'form-control' %>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-12">
<%= f.label :address_line_2 %>
<%= f.text_field :address2, class: 'form-control' %>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-12">
<%= f.label :address_line_3 %>
<%= f.text_field :address3, class: 'form-control' %>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-4">
<%= f.label :city %>
<%= f.text_field :city, class: 'form-control' %>
</div>
<div class="form-group col-md-4">
<%= f.label :state %>
<%= f.text_field :state, class: 'form-control' %>
</div>
<div class="form-group col-md-4">
<%= f.label :country %>
<%= f.text_field :country, class: 'form-control' %>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-2">
<%= f.label :zip %>
<%= f.text_field :zip, class: 'form-control' %>
</div>
</div>
</div>
<div class="container">
<div class="form-row">
<div class="form-group col-md-12">
<%= f.button :Register, class: 'btn btn-primary' %>
</div>
</div>
</div>
<% end %>
and finally the register method in my core_api.rb is this:
def register(data)
body = data.to_json
puts ">> >> >> >> #{body.class} :: #{body}"
options = { headers: { 'Content-Type' => 'application/json' }, body: body }
response = self.class.post('/organisations', options)
#puts ">>>>>>>>>>>> #{response}"
end
and finally my routes.rb file contains:
Rails.application.routes.draw do
get '/' => 'home#show'
get '/auth/auth0/callback' => 'auth0#callback'
get '/auth/failure' => 'auth0#failure'
get '/logout', to: 'logout#logout', as: 'logout'
get '/organisations/dashboard', to: 'organisations#dashboard', as: 'org_dashboard'
get '/organisations/register', to: 'organisations#register', as: 'org_register'
post '/organisations/register', to: 'organisations#create'
root 'home#show'
end
now when I run the server and submit the form in the logs I get:
>> >> >> >> String :: {"title":"","firstname":"","lastname":"","role":"","telephone":"","extension":"","email":"alijy3#yahoo.com","name":"we","branch":"we","address1":"we","address2":"","address3":"","city":"we","state":"","country":"we","zip":"","oruid":"auth0%7C5e5388493d670c11be833bca","contact_id":0}
which to me looks like a proper json. But, since the api response was continually unsuccessful I intercepted the outgoing post with Postman to see what payload is being sent. To my surprise the payload is not flat json, but comes out like this:
I have 2 problems with this:
The api accepts items like address1, address2, city, etc. I believe I should send those rather than the currently showing organisation[address1], organisation[address2], etc.
The second problem is that I add the orguid after the form is submitted and before calling/posting to the api. But, although I can see it in the log messages, I don't see the orguid in the postman payload in any form.
I don't have any database on the server. Everything is fetch/posted/saved through the api. I've been reading about how to work with Activemodel and forms for a while and I haven't managed to get this resolved yet. Any help or explanation would be much appreciated.
No offense but this is a train wreck. You don't need to break every rails convention just because you're not using ActiveRecord in this specific case.
Start off by using ActiveModel::Attributes#attribute instead of Ruby's built in attr_accessor.
class Organisation
include ActiveModel::Model
include ActiveModel::Attributes
[:orguid, :title, :firstname, :lastname, :role, :telephone,
:extension, :email, :name, :branch,
:address1, :address2, :address3, :city, :state, :country, :zip]
.each do |name|
attribute name
end
# #todo write validations!
end
This creates attributes that act like ActiveRecord attributes and you can serialize the model properly with #organization.as_json.
Then lets just start fresh on that controller as there is just too much smell for it to be worth salvaging.
# routes.rb
resources :organisations, only: [:new, :create]
class OganizationsController < ApplicationController
# GET /organizations/new
def new
#organization = Organization.new
end
# POST /organizations
def create
# You never manually parse out incoming params - thats Rack's job.
# also since you have a model - USE IT!
#organization = Organization.new(organization_params) do |o|
o.orguid = org_uid
end
# validate the user input before you send it to an external API
if #organization.valid? && #api.register(#organization)
redirect_to '/somewhere'
else
render :new
end
end
private
# use monads here instead of callbacks!
def user_info
# Rails will serialize/deserialize hashes automatically
# from the session
session[:userinfo]
end
def org_uid
# Have no clue what the heck you're doing with CGI escape.
#org_uid ||= user_info['uid']
end
def api
#api ||= CoreApi.new(org_uid)
end
def organization_params
# You don't have any reason to use 'permit!' and give
# yourself a potential mass assignment vunerablity
params.require(:organization)
.permit(
:title, :firstname, :lastname, :role, :telephone,
:extension, :email, :name, :branch,
:address1, :address2, :address3, :city,
:state, :country, :zip
)
end
end
Rename the view /organizations/new.html.rb. At this point you should be able to stub out the API and do an integration test with valid and invalid input.
That whole session[:userinfo] thing still smells really bad - if you are taking the response from OAuth and shoving it into the session your setting yourself up for a really bad time as that can cause cookie overflows. Also in general in Rails if you're ever manually casting/serializing then its a really good sign that your doing something very wrong.
Have no clue really whats going on in your CoreApi class but if you are using HTTParty you should not do ANY manual JSON encoding.
# #fixme name is way to generic.
class CoreApi
include HTTParty
format :json # sets content type and encodes the content
# ...
def register(organization)
response = self.class.post('/organisations', #organization.as_json)
if response.success?
true
else
#organization.errors.add(:base, 'Could not be registered')
false
end
end
end
I have 2 models:
class Page < ApplicationRecord
enum page_type: STATIC_PAGE_TYPES, _suffix: true
has_one :seo_setting
accepts_nested_attributes_for :seo_setting, update_only: true
validates :title, :subtitle, length: { maximum: 50 }
validates :page_type, uniqueness: true
def to_param
"#{id}-#{page_type}".parameterize
end
end
and
class SeoSetting < ApplicationRecord
mount_uploader :og_image, SeoSettingsOgImageUploader
belongs_to :page
validates :seo_title, :seo_description, :og_title, :og_description, :og_image, presence: true
end
My Page objects are created from the seeds.rb file, and when I want to edit them, I get an error: Failed to save the new associated seo_setting.
In the form I have this:
<div class="card-body">
<%= form_for([:admin, #page]) do |f| %>
<%= render 'shared/admin/error-messages', object: #page %>
<div class="form-group">
<%= f.label :title, t('admin.shared.title') %>
<%= f.text_field :title, class: 'form-control' %>
</div>
<div class="form-group">
<%= f.label :subtitle, t('admin.shared.subtitle') %>
<%= f.text_field :subtitle, class: 'form-control' %>
</div>
<h3>SEO Settings</h3>
<%= f.fields_for :seo_setting, f.object.seo_setting ||= f.object.build_seo_setting do |form| %>
<div class="form-group">
<%= form.label :seo_title, t('admin.shared.seo_title') %>
<%= form.text_field :seo_title, class: 'form-control' %>
</div>
<div class="form-group">
<%= form.label :seo_description, t('admin.shared.seo_description') %>
<%= form.text_area :seo_description, class: 'form-control' %>
</div>
<div class="form-group">
<%= form.label :og_title, t('admin.shared.og_title') %>
<%= form.text_field :og_title, class: 'form-control' %>
</div>
<div class="form-group">
<%= form.label :og_description, t('admin.shared.og_description') %>
<%= form.text_area :og_description, class: 'form-control' %>
</div>
<div class="form-group">
<%= form.label :og_image, t('admin.shared.og_image') %>
<div class="row">
<div class="col-lg-12">
<%= image_tag(form.object.og_image.url, style: 'width: 100px') if form.object.og_image? %>
</div>
</div>
<%= form.file_field :og_image %>
<%= form.hidden_field :og_image_cache %>
</div>
<% end %>
<div class="form-group">
<%= f.submit t('admin.actions.submit'), class: 'btn btn-success' %>
<%= link_to t('admin.actions.cancel'), admin_page_path(#page) , class: 'btn btn-default' %>
</div>
<% end %>
</div>
If I remove validations from my SeoSetting model, everything is working. It seems Rails doesn't like this part: f.object.build_seo_setting, because it creates a record in my database. Any ideas of how can I solve this issue? Thanks ahead.
Just had to change this line:
<%= f.fields_for :seo_setting, f.object.seo_setting ||= f.object.build_seo_setting do |form| %>
for this one:
<%= f.fields_for :seo_setting, #page.seo_setting.nil? ? #page.build_seo_setting : #page.seo_setting do |form| %>
Looks as if the problem lies here:
accepts_nested_attributes_for :seo_setting, update_only: true
in that you only allow the updating of the seo_setting on update.
Then, when you use this code:
f.object.seo_setting ||= f.object.build_seo_setting
You're falling back to a new seo_setting if the associated object is missing.
For this to work, you'll need to either remove the update_only: true, or only render the fields for the association if the seo_setting already exists.
Accepted answer, a conditional inside fields_for line, by OP, works also with polymorphic association and nested form (fields_for). Thnx Alex.
In my project I have API and its associated front end part. I am using rails version 5 . I am creating two models. One is user and another one is qualifications. Associations are given below.
class User
include Her::Model
include Her::FileUpload
has_file_upload :image_url
has_many :videos
has_many :qualifications
attributes :name, :email, :phone, :image_url, :contact_email, :about, :password, :password_confirmation
custom_post :sign_in, :refresh
custom_get :me
accepts_nested_attributes_for :qualifications
end
And, qualification model is
class Qualification
include Her::Model
belongs_to :user
attributes :school, :degree, :grade, :start_year, :end_year
end
My strong params in users controller is
def user_params
user: params.require(:user).permit(:id, :name, :email, :phone, :contact_email, :about,:image_url,
{specialization_ids: []},
qualifications_attributes: [:id, :school, :degree, :grade, :start_year, :end_year, :_destroy]
end
My user form, where i have provided nested_form is
<div class="row">
<div class="container">
<div class="signup-bg col-md-12">
<h3 style="color:white;"> General Info </h3>
<hr>
<%= simple_nested_form_for #user do |f| %>
<%= f.input :email, input_html: {class: "input-user-form"} %>
<%= f.input :name, input_html: {class: "input-user-form"} %>
<%= f.input :phone , input_html: {class: "input-user-form"}%>
<%= f.input :contact_email, input_html: {class: "input-user-form"} %>
<%= f.input :about, as: :text, input_html: {class: "input-user-form"}%>
<label class="control-label">Selected Specializations</label>
<ul class="list-group">
<% #user.specializations.each do |sp| %>
<li class="list-group-item tags">
<%= check_box_tag "user[specialization_ids][]", sp["id"], true%>
<%= sp["name"] %>
</li>
<% end %>
</ul>
<h4 style="color:white;"><center>Select Your Specializations</center></h4>
<div class="row">
<div class="col-md-12">
<div class="input-group">
<input id="searchText" type="text" class="input-signup" placeholder="Search Specialization" style="z-index:0;">
<span class="input-group-btn">
<button class="btn btn-default btn-go" type="button">Go!</button>
</span>
</div>
<div class="col-md-12 col-sm-12 col-lg-12" id="specializationHolder" style="min-height:85px;"></div>
</div>
</div>
<h3 style="color:white;"> Qualification Info </h3>
<hr>
<div id="qualification_holder">
<%= f.simple_fields_for :qualifications, #qualification do |g| %>
<%= g.input :school,input_html: {class: "input-user-form"} %>
<%= g.input :degree, input_html: {class: "input-user-form"} %>
<%= g.input :grade, as: :integer, input_html: {class: "input-user-form"} %>
<%= g.input :start_year, required: false, as: :string, input_html: {type: :date, class: "datepicker input-user-form"} %>
<%= g.input :end_year, required: false, as: :string, input_html: {type: :date, class: "datepicker input-user-form"} %>
<% end %>
</div>
</br>
<div class="col-md-4">
<%= f.link_to_add :qualifications, data: {target: "#qualification_holder"}, class: "btn-signup-submit" do %>
<b> + </b> Add Qualifications
<% end %>
</div>
<div id="wrapper">
<label class="control-label file optional" for="user_image_url">Profile Picture</label>
<input class="file optional" type="file" name="user[image_url]" id="fileUpload">
</div>
<div class="form-group text-center">
<button type="submit" class="btn-signup-submit">Save Changes</button>
</div>
<% end %>
</div>
</div>
</div>
But, I am getting an error undefined error "reflect_on_association", pointing towards "link_to_add". I am Stuck here for a week. Is there any solutions for it.. If so, please help me.
Thanks in advance.
I had the same error. The solution is to add the class_name option on your problematic association, with no nesting on the class name.
In your case, you can try:
class User
include Her::Model
include Her::FileUpload
has_file_upload :image_url
has_many :videos
has_many :qualifications, class_name: "::Qualification"
attributes :name, :email, :phone, :image_url, :contact_email, :about, :password, :password_confirmation
custom_post :sign_in, :refresh
custom_get :me
accepts_nested_attributes_for :qualifications
end
I am trying to have my Edit form only allow for the creation of new elements. Currently it automatically displays all of the existing elements. Below is my current code. I have a standard has_many, belongs_to relationship between Orders and Documents. I only want the ability for users to add new documents and never edit the existing documents. With the current code It displays all associated Documents on the edit form.
<%= f.fields_for :documents, child_index: Time.now.to_i do |document| %>
<div class="col-md-5">
<%= document.label :doc_type, "Type", :style => "color:red;" %>
<%= document.collection_select :doc_type_id, DocType.order(:name), :id, :name, { prompt: true, class: "form-full"} %>
<br>
</div>
<div class="col-md-7">
<%= document.label :description,"Description **", :style => "color:red;" %>
<%= document.text_field :description, style:"color:black;" %>
<br>
</div>
<div class="col-md-12">
<br>
<%= document.file_field :doc, style:"color:white;" %>
</div>
<div class="col-md-12" style="background:#272727;">
<hr class="style2">
</div>
<% end %>
class Order < ActiveRecord::Base
belongs_to :user
has_many :properties, :dependent => :destroy
has_many :documents, :dependent => :destroy
accepts_nested_attributes_for :documents,
:allow_destroy => true,
:reject_if => :all_blank
accepts_nested_attributes_for :properties,
reject_if: proc { |att| att["city"].blank? }
end
class Document < ActiveRecord::Base
belongs_to :order
belongs_to :doc_type
end
With The below code I was able to have the form appear correctly (not showing existing Documents) but when adding a new Document via the Edit form, the Document is never actually created.
<% if !current_page?(action: 'edit') %>
<%= f.fields_for :documents, child_index: Time.now.to_i do |document| %>
<div class="col-md-5">
<%= document.label :doc_type, "Type", :style => "color:red;" %>
<%= document.collection_select :doc_type_id, DocType.order(:name), :id, :name, { prompt: true, class: "form-full"} %>
<br>
</div>
<div class="col-md-7">
<%= document.label :description,"Description **", :style => "color:red;" %>
<%= document.text_field :description, style:"color:black;" %>
<br>
</div>
<div class="col-md-12">
<br>
<%= document.file_field :doc, style:"color:white;" %>
</div>
<div class="col-md-12" style="background:#272727;">
<hr class="style2">
</div>
<% end %>
<% else %>
<%= f.fields_for #order.documents.build, child_index: Time.now.to_i do |document| %>
<div class="col-md-5">
<%= document.label :doc_type, "Type", :style => "color:red;" %>
<%= document.collection_select :doc_type_id, DocType.order(:name), :id, :name, { prompt: true, class: "form-full"} %>
<br>
</div>
<div class="col-md-7">
<%= document.label :description,"Description **", :style => "color:red;" %>
<%= document.text_field :description, style:"color:black;" %>
<br>
</div>
<div class="col-md-12">
<br>
<%= document.file_field :doc, style:"color:white;" %>
</div>
<div class="col-md-12" style="background:#272727;">
<hr class="style2">
</div>
<% end %>
<% end %>
Any help/advice/resources would be appreciated. Been stuck on this for a bit. Also I have not refactored into partials yet so please forgive the messiness.
THANKS!!
-Vinny
I'm trying to build a small expense tracking app using Rails 4.1. When a user submits the expense request, it's state is marked as pending by default. The admin has to approve the request. I'm using state_machine gem to do this.
I just added comment functionality using acts_as_commentable gem, which works fine on its own. I wanted to combine the approval drop down and the comment box in the same form and used the following code in the expense show page:
<div class="row">
<div class="col-md-8">
<%= form_for [#expense, Comment.new] do |f| %>
<div class="form-group">
<%= f.label :state %><br />
<%= f.collection_select :state, #expense.state_transitions, :event, :human_to_name, :include_blank => #expense.human_state_name, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :comment %><br />
<%= f.text_area :comment, class: "form-control" %>
</div>
<%= f.submit "Submit", class: "btn btn-primary" %>
<% end %>
</div>
</div>
<br>
The problem is I get the "NoMethodError in Expenses#show - undefined method `state' for #". Is there a way I can update both the approval status and comment in one go?
The updated show page with nested attributes:
<div class="row">
<div class="col-md-8">
<%= nested_form_for (#expense) do |f| %>
<div class="form-group">
<%= f.label :state %><br />
<%= f.collection_select :state, #expense.state_transitions, :event, :human_to_name, :include_blank => #expense.human_state_name, class: "form-control" %>
</div>
<%= f.fields_for :comments do |comment| %>
<div class="form-group">
<%= comment.label :comment%>
<%= comment.text_area :comment, class: "form-control" %>
</div>
<% end %>
<%= f.submit "Submit", class: "btn btn-primary" %>
<% end %>
</div>
</div>
Nested attributes are your friend. They are so easy to implement I'd recommend avoiding acts_as_commentable to remove an unnecessary dependency and so you understand what's going on.
Use them like this:
# expense.rb
class Expense < ActiveRecord::Base
has_many :comments # << if polymorphic add `, as: :commentable`
accepts_nested_attributes_for :comments
end
# expenses_controller.rb
def new
#expense = Expense.new
#expense.comments.build
end
# expenses/new.html.haml
= form_for(#expense) do |f|
- # expense inputs...
= f.nested_fields_for(:comments) do |comment|
= comment.text_area(:body)
= f.submit
There are many options, so check the docs for more details (they're quite good).