I have an app in which i want create nested form
my models:
class Abonent < ActiveRecord::Base
belongs_to :town
has_many :numbers, :dependent => :destroy
accepts_nested_attributes_for :numbers
end
class Number < ActiveRecord::Base
belongs_to :abonent
end
Abonents controller:
class AbonentsController < ApplicationController
def new
#abonent = Abonent.new
end
def create
#abonent = Abonent.new abonents_params
if #abonent.save
redirect_to :towns
else
render action: "new"
end
end
private
def abonents_params
params.require(:abonents).permit( :fullname, :work_position, :department, :email, :town_id, numbers_attributes: [ :phone ] )
end
end
And abonents view
<hr><br>
<%= form_for :abonents, url: abonents_path do |f| %>
<%= f.label :fullname %>:
<%= f.text_field :fullname %><br /> <br />
<%= f.label :work_position %>:
<%= f.text_field :work_position %><br /><br />
<%= f.label :department %>:
<%= f.text_field :department %><br /><br />
<%= f.label :email %>:
<%= f.text_field :email %><br /><br />
<%= f.label :town_id %>:
<%= f.select :town_id, Town.all.collect { |p| [ p.ru_name, p.id ] } %><br /><br />
<%= f.fields_for :numbers do |phones|%>
<%= phones.label :phone %>:
<%= phones.text_field :phone %><br /><br />
<% end %>
<%= f.submit %>
<% end %>
The problem is when i submit form, it creates abonent, but doesn't create number for this abonent.
I saw many different manuals and couldn't find the error in my code.
Please help.
Thank you.
UPD I added a repo on github for this problem.
You need to build the associated records
def new
#abonent = Abonent.new
#abonent.numbers.build
end
Related
Here personaldetails belongs_to user and the relation given is has_many which is wrong.I want to convert the has_many relation to has_one relation i.e. User has_one personaldetails. When I change the relation directly I am getting an error "uninitialized constant User::Personaldetails. Please guide me how to convert the relation .
Personaldetail.rb
class Personaldetail < ApplicationRecord
belongs_to :user
end
User.rb
class User < ApplicationRecord
has_many :personaldetails, dependent: :destroy
accepts_nested_attributes_for :personaldetails, reject_if: :all_blank, allow_destroy: true
end
routes.rb
resources :users, except: [:new] do
resources :personaldetails
end
user_steps_controller.rb
class UserStepsController < ApplicationController
include Wicked::Wizard
steps : :personaldetails
def show
#user = current_user
#personaldetails = #user.personaldetails.build
render_wizard
end
def update
#user = current_user
#user.update!(user_params)
render_wizard #user
end
private
def user_params
params.require(:user).permit(:name, :password, :password_confirmation, :user_id,
personaldetails_attributes: [:id,:first_name, :last_name, :gmail, :mobile_no, :city, :state, :pin_code, :_destroy])
end
end
personaldetails.html.erb
<%= form_with(model: #user, url: wizard_path, local: true) do |form| %>
<%= form.fields_for :personaldetail,Personaldetail.new do |info| %>
<%= render 'personaldetails_field', form: info %>
<% end %>
<%= form.submit %>
<% end %>
_personaldetails_field.html.erb
<div class="field">
<%= form.label :First_name %><br />
<%= form.text_field :first_name %>
</div>
<div class="field">
<%= form.label :Last_name %><br />
<%= form.text_field :last_name %>
</div>
<div class="field">
<%= form.label :email %><br />
<%= form.text_field :gmail %>
</div>
<div class="field">
<%= form.label :Mobile_number %><br />
<%= form.text_field :mobile_no %>
</div>
<div class="field">
<%= form.label :City %><br />
<%= form.text_field :city %>
</div>
<div class="field">
<%= form.label :State %><br />
<%= form.text_field :state %>
</div>
<div class="field">
<%= form.label :Pincode %><br />
<%= form.text_field :pin_code %>
</div>
So the solution is:
Personaldetail.rb
class Personaldetail < ApplicationRecord
belongs_to :user
end
User.rb
class User < ApplicationRecord
has_one :personaldetails, dependent: :destroy
accepts_nested_attributes_for :personaldetails, reject_if: :all_blank, allow_destroy: true
end
routes.rb
resources :users, except: [:new] do
resources :personaldetail
end
user_steps_controller.rb
class UserStepsController < ApplicationController
include Wicked::Wizard
steps : :personaldetails
def show
#user = current_user
render_wizard
end
def update
#user = current_user
#user.update!(user_params)
render_wizard #user
end
private
def user_params
params.require(:user).permit(:name, :password, :password_confirmation, :user_id,
personaldetails_attributes: [:id,:first_name, :last_name, :gmail, :mobile_no, :city, :state, :pin_code, :_destroy])
end
end
personaldetail.html.erb
<%= form_with(model: #user, url: wizard_path, local: true) do |form| %>
<%= form.fields_for :personaldetail,#user.personaldetail || #user.build_personaldetail do |info| %>
<%= render 'personaldetail_field', form: info %>
<% end %>
<%= form.submit %>
<% end %>
_personaldetail_field.html.erb
<div class="field">
<%= form.label :First_name %><br />
<%= form.text_field :first_name %>
</div>
<div class="field">
<%= form.label :Last_name %><br />
<%= form.text_field :last_name %>
</div>
<div class="field">
<%= form.label :email %><br />
<%= form.text_field :gmail %>
</div>
<div class="field">
<%= form.label :Mobile_number %><br />
<%= form.text_field :mobile_no %>
</div>
<div class="field">
<%= form.label :City %><br />
<%= form.text_field :city %>
</div>
<div class="field">
<%= form.label :State %><br />
<%= form.text_field :state %>
</div>
<div class="field">
<%= form.label :Pincode %><br />
<%= form.text_field :pin_code %>
</div>
try with: has_one :personaldetail, dependent: :destroy
Rails are guessing class name from name AND type of association, so with has_many they will try to singularize association name (personaldetails => Personaldetail) but with has_one they will try to reach it as is (personaldetails => Personaldetails)
As in the comment by spickermann, has_many relationship wants plural form and has_one the singular form.
That is to say, you should already be able to infer the relationship from:
#user.personaldetails # user has many personal details
#user.personaldetail # user has one personal detail
Just a consideration: many weird cases arise when objects/models are not properly named. As a rule of thumb, you should use the most fitting and precise English noun for the object you need to name. That will help you hugely in cases like this. In normal English language, it is somehow strange to say "a user has a personal detail" but you would say of course "has personal details". Particularly when it comes to ActiveRecord associations, Rails syntax should be the nearest as possible to English language, to avoid later misunderstandings. I guess this confusion would not have arisen if instead of "PersonalDetail", the model was called "Account" or "Profile", for instance.
Few suggestions/comments
Keep model name as CamelCase like PersonalDetail rather than Personaldetail and association name has_one :personal_detail
Using has_one relation you can create the object using user.build_personal_detail.save
When you run the 2nd step again it will create another record in personal_details table and in that transaction it will return the new record. But, after that when you try to query it will return the 1st created personal_details record rather than new one. That's because ActiveRecord by default sorts by id and limit 1 for has_one relation
I use Ruby v 3. I want to display data from 2 tables into one form.
My models:
class Address < ActiveRecord::Base
attr_accessible :city, :number, :street
validates :city, :presence => true
validates :number, :presence => true
validates :street, :presence => true
has_many :users
end
class User < ActiveRecord::Base
belongs_to :address
attr_accessible :name, :phone, :surname, :address_attributes
accepts_nested_attributes_for :address
end
My form looks alike:
<%= form_for(#user) do |f| %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :surname %><br />
<%= f.text_field :surname %>
</div>
<div class="field">
<%= f.label :phone %><br />
<%= f.text_field :phone %>
</div>
<%= f.fields_for :address_attributes do |p| %>
<div class="field">
<%= p.label :city %><br />
<%= p.text_field :city %>
</div>
<div class="field">
<%= p.label :street %><br />
<%= p.text_field :street %>
</div>
<div class="field">
<%= p.label :number %><br />
<%= p.text_field :number %>
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
As you can see I use fields_for method. And my controller is here:
def edit
#user = User.find(params[:id])
#user.address_attributes = #user.address
end
It does not working and I totally don't know why. When I click edit on address list i've got an error:
undefined method `with_indifferent_access'
Anyone can help me figure it out?
Check out Episodes 196 and 197 on RailsCasts:
http://railscasts.com/episodes/196-nested-model-form-part-1
http://railscasts.com/episodes/197-nested-model-form-part-2
There is a revised episode for 196, for which you will need to subscribe to RailsCasts.
I would highly recommend subscribing to learning sites like RailsCasts and CodeSchool, to lear RoR faster and in the right way.
Try to do:
def edit
#user = User.find(parmas[:id])
end
and on your view:
<%= f.fields_for :address do |p| %>
and see if it works, you don't have to add the attributes
This is my first question. I hope you can help me. I have two models.
class Cliente < ActiveRecord::Base
attr_accessible :cedula, :direccion, :nombres, :telefono
validates :cedula, :direccion, :nombres, :telefono, :presence => true
validates :cedula, :uniqueness => { :message => "Cedula ya en uso" }
has_many :facturas
class Factura < ActiveRecord::Base
attr_accessible :cliente_attributes, :iva, :numero, :subtotal, :total, :created_at
belongs_to :cliente
accepts_nested_attributes_for :cliente
I want in the facturas#new view can create or edit Cliente. If exists update or if not exists create. I am using nested attributes. If exists I uses javascript to fill text fields. If not exists I fill text field and save when Factura save. This is facturas#new view.
<h1>Nueva Factura</h1>
<%= form_for #factura do |f| %>
<% if #factura.errors.any? %>
<h2>Errores:</h2>
<ul>
<% #factura.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
<% end %>
<p>
<div class = "contenedor_factura">
<%= f.label :numero %><br />
<%= f.number_field :numero %><br />
<%= f.label :fecha %><br />
<%= f.date_select :created_at %><br />
</div>
<div class = "contenedor_cliente">
<%= f.fields_for #cliente do |builder| %>
<%= builder.label :cedula, "Cédula" %>
<%= builder.text_field :cedula %>
<%= builder.label :nombres, "Nombres" %>
<%= builder.text_field :nombres %>
<%= builder.label :direccion, "Dirección" %><br />
<%= builder.text_field :direccion %>
<%= builder.label :telefono, "Teléfono" %><br />
<%= builder.text_field :telefono %>
<%= builder.hidden_field :id%>
<% end %>
</div>
<div class = "contenedor_productos">
</div>
<%= f.label :subtotal %><br />
<%= f.text_field :subtotal %>
<br />
<%= f.label :iva %><br />
<%= f.text_field :iva %>
</p>
<p>
<%= f.submit "Agregar Nueva Factura" %>
</p>
<% end %>
When the Cliente is new i have no problem, it saves, but if Cliente exists i have this message
ActiveRecord::RecordNotFound in FacturasController#create
Couldn't find Cliente with ID=6 for Factura with ID=
What is my problem?
EDIT:
This is my FacturasController
def new
#factura = Factura.new
#cliente = #factura.build_cliente
end
def create
#factura = Factura.new(params[:factura])
if #factura.save
redirect_to facturas_path, :notice => "Factura Guardada"
else
render "new"
end
end
I'm not sure if nested_attrubes handles already created models. So we can just not depend on it.
This should work
def create
cliente_attrs = params[:factura].delete :cliente
#cliente = cliente_attrs[:id].present? ? Cliente.find(cliente_attrs[:id]) : User.create(user_attrs)
#factura = cliente.facturas.build(params[:factura])
if #factura.save
redirect_to facturas_path, :notice => "Factura Guardada"
else
render "new"
end
end
You can now delete the line accepts_nested_attributes_for :cliente
I am still having problems with nested forms. Here is my form code:
<%= form_for #account do |f| %>
<%= f.label :account_type %><br />
<%= f.text_field :account_type %><br />
<%= f.fields_for :organizations do |builder| %>
<%= builder.label :name %><br />
<%= builder.text_field :name %><br />
<%= builder.label :website %><br />
<%= builder.text_field :website %><br />
<%= f.fields_for :locations do |builder| %>
<%= builder.label :phone %><br />
<%= builder.text_field :phone %><br />
<%= builder.label :toll_free_phone %><br />
<%= builder.text_field :toll_free_phone %><br />
<%= builder.label :fax %><br />
<%= builder.text_field :fax %><br />
<% end %>
<% end %>
<%= f.submit "Add account" %>
<% end %>
The Account model:
class Account < ActiveRecord::Base
has_many :organizations
accepts_nested_attributes_for :organizations
end
The Organization model:
class Organization < ActiveRecord::Base
belongs_to :account
has_many :locations
accepts_nested_attributes_for :locations
end
And the Location model:
class Location < ActiveRecord::Base
belongs_to :organization
end
And lastly, the Accounts Controller:
class AccountsController < ApplicationController
def new
#account = Account.new
organization = #account.organizations.build
organization.locations.build
#header = "Create account"
end
def create
#account = Account.new(params[:account])
if #account.save
#handle success
else
render 'new'
end
end
end
Here is the error I am getting:
ActiveRecord::UnknownAttributeError in AccountsController#create
unknown attribute: locations
Rails.root: C:/Documents and Settings/Corey Quillen/My
Documents/rails_projects/shop_manager
Application Trace | Framework Trace | Full Trace
app/controllers/accounts_controller.rb:12:in `new'
app/controllers/accounts_controller.rb:12:in `create'
Request
Parameters:
{"utf8"=>"✓",
"authenticity_token"=>"xuZLSP+PSjdra3v51nIkJYZeXRM0X88iF135hPlp4sc=",
"account"=>{"account_type"=>"Company",
"organizations_attributes"=>{"0"=>{"name"=>"Atlas",
"website"=>"www.atlas.com"}},
"locations"=>{"phone"=>"555-555-5555",
"toll_free_phone"=>"800-555-5555",
"fax"=>"512-555-5555"}},
"commit"=>"Add account"}
Any help with this will be greatly appreciated. I have been looking at this now for a couple of hours.
You should use new builder in nested form for nested nested form :)) :
<%= form_for #account do |f| %>
<%= f.label :account_type %><br />
<%= f.text_field :account_type %><br />
<%= f.fields_for :organizations do |builder| %>
<%= builder.label :name %><br />
<%= builder.text_field :name %><br />
<%= builder.label :website %><br />
<%= builder.text_field :website %><br />
<%= builder.fields_for :locations do |lb| %>
<%= lb.label :phone %><br />
<%= lb.text_field :phone %><br />
<%= lb.label :toll_free_phone %><br />
<%= lb.text_field :toll_free_phone %><br />
<%= lb.label :fax %><br />
<%= lb.text_field :fax %><br />
<% end %>
<% end %>
<%= f.submit "Add account" %>
<% end %>
An even DRYer solution:
1) in your Gemfile, list nested_form as a dependency.
2) in your models do this:
class Account < ActiveRecord::Base
accepts_nested_attributes_for :organizations
end
class Organization < ActiveRecord::Base
accepts_nested_attributes_for :locations
end
3) create regular forms for Organization under ./app/view/organizations/
and for Location under./app/view/locations/`
4) in your Account form, do this: (it gets pretty short this way!)
<%= nested_form_for #account do |f| %>
<%= f.label :account_type %><br />
<%= f.text_field :account_type %><br />
<%= f.fields_for :organizations %>
<%= f.submit "Add account" %>
<% end %>
5) in your Organization form, do this: (also pretty short)
<%= nested_form_for #organization do |f| %>
<%= f.label :name %><br />
<%= f.text_field :name %><br />
<%= f.label :website %><br />
<%= f.text_field :website %><br />
<%= f.fields_for :locations %>
<%= f.submit "Add Organization" %>
<% end %>
Check these RailsCasts:
http://railscasts.com/episodes/196-nested-model-form-part-1
http://railscasts.com/episodes/197-nested-model-form-part-2
View
<%= form_for #product, :url => admin_products_path do |f| %>
<div>
<%= f.label :name %>
<%= f.text_field :name %>
</div>
<div>
<%= f.label :description %>
<%= f.text_area :description, :rows => 7 %>
</div>
<%= f.fields_for :address do |a| %>
<p><%= a.label "Address One" %><br />
<%= a.text_field :address_one %></p>
<p><%= a.label "Address Two" %><br />
<%= a.text_field :address_two %></p>
<p><%= a.label :city %><br />
<%= a.text_field :city %></p>
<p><%= a.label :postcode %><br />
<%= a.text_field :postcode %></p>
<p><%= a.label :country %><br />
<%= a.select :country_id, Country.active.collect {|p| [ p.printable_name, p.id ] } %></p>
<% end %>
<% end %>
Controller
def create
#product = Product.new(params[:product])
if #product.save
flash[:notice] = 'Product was successfully created.'
render 'show'
else
render 'new'
end
end
model
class Product < ActiveRecord::Base
has_one :address, :as => :addressable
end
but when save data it show issue:
Address(#93165130) expected,got ActiveSupport::HashWithIndifferentAccess(#79365750)
Please help me solved this problem
Try if adding accepts_nested_attributes_for :address solves this.
accepts_nested_attributes_for :address
if not work then try also adding along with accepts_nested_attributes_for :address
attr_accessor :address