Accepts nested attributes for - ruby-on-rails

might be a simple question but i can´t get data from one form pass to another model. I am just starting so the code is real simple. I have 2 models
class Client < ActiveRecord::Base
has_many :properties
end
class Property < ActiveRecord::Base
belongs_to :client
accepts_nested_attributes_for :client
end
I am using form_for and nesting f.fields_for
<%= form_for(#property) do |f| %>
<div class="form-group">
<label for="exampleInputEmail1"> Descripcion:</label>
<%= f.text_field :descripcion, class: "form-control" %>
</div>
<%= f.fields_for :clients do |clients| %> 
<%= clients.text_field :nombre %>
<%= clients.text_field :apellido %>
<% end %>
<% end %>
The form works just for one model (properties), there is no error but the data for client is just not getting to my client model.
I am guessing that the problem might be with strong parameters of the nested form but i cannot fix the problem. Here is my controller por properties:
class PropertiesController < ApplicationController
def index
#properties = Property.all
#clients = Client.all
end
def new
#property = Property.new
end
def create
#property=Property.new(params.require(:property).permit(:direccion, :descripcion, :piezas, :precio, :banos, :superficie_total, :pisos, :piscina, :superficie_construida, :amoblado, :estacionamiento, :bodega, :estado, :casa, :departamento, :terreno, :gastos_comunes, :comentarios, :comuna, :ciudad))
if #property.save
flash[:notice] = "La Propiedad ha sido creada exitosamente =)"
redirect_to(:action => 'index')
else
render('new')
flash[:error] = "Por algun motivo no pudimos crear la propiedad =("
end
end
def show
#property = Property.all
end
Can anybody give me some help??

you made a couple of mistake.
First mistake
As mention in Documentation NestedAttributes you used wrong nested_attributes.
class Client < ActiveRecord::Base
has_many :properties
accepts_nested_attributes_for :properties
end
class Property < ActiveRecord::Base
belongs_to :client
end
Second Mistake is strong parameter. I would like to suggest you to go throw Sitepoint rails-forms-with-nested-attributes would help you.

Related

Rails nested fields creation in loop

I have three model classes related to each other.
class Student < ActiveRecord::Base
has_many :marks
belongs_to :group
accepts_nested_attributes_for :marks,
reject_if: proc { |attributes| attributes['rate'].blank?},
allow_destroy: true
end
This class describes a student that has many marks and I want to create a Student record along with his marks.
class Mark < ActiveRecord::Base
belongs_to :student, dependent: :destroy
belongs_to :subject
end
Marks are related both to the Subject and a Student.
class Subject < ActiveRecord::Base
belongs_to :group
has_many :marks
end
When I try to create the nested fields of marks in loop labeling them with subject names and passing into in it's subject_id via a loop a problem comes up - only the last nested field of marks is saved correctly, whilst other fields are ignored. Here's my form view code:
<%= form_for([#group, #student]) do |f| %>
<%= f.text_field :student_name %>
<%=f.label 'Student`s name'%><br>
<%= f.text_field :student_surname %>
<%=f.label 'Student`s surname'%><br>
<%=f.check_box :is_payer%>
<%=f.label 'Payer'%>
<%= f.fields_for :marks, #student.marks do |ff|%>
<%#group.subjects.each do |subject| %><br>
<%=ff.label subject.subject_full_name%><br>
<%=ff.text_field :rate %>
<%=ff.hidden_field :subject_id, :value => subject.id%><br>
<%end%>
<% end %>
<%= f.submit 'Add student'%>
<% end %>
Here`s my controller code:
class StudentsController<ApplicationController
before_action :authenticate_admin!
def new
#student = Student.new
#student.marks.build
#group = Group.find(params[:group_id])
#group.student_sort
end
def create
#group = Group.find(params[:group_id])
#student = #group.students.new(student_params)
if #student.save
redirect_to new_group_student_path
flash[:notice] = 'Студента успішно додано!'
else
redirect_to new_group_student_path
flash[:alert] = 'При створенні були деякі помилки!'
end
end
private
def student_params
params.require(:student).permit(:student_name, :student_surname, :is_payer, marks_attributes: [:id, :rate, :subject_id, :_destroy])
end
end
How can I fix it?
#student.marks.build
This line will reserve an object Mark.
If you want multi marks, May be you need something like this in new action :
#group.subjects.each do |subject|
#student.marks.build(:subject=> subject)
end
Hope useful for you.

has_one and belongs to form

I'm rather new to rails and I'm stuck with this has_one and belongs_to form. I'm trying to create a team that has two speakers (from class 'User') through a form ,in the following manner:
class Team<ActiveRecord::Base
belongs_to :league
belongs_to :seed
has_many :speakers do
def user(level="1")
find_by(level: level).user
end
end
end
my user model looks like this :
class User < ActiveRecord::Base
belongs_to :team
end
user model:
class User
speaker model:
class Speaker < ActiveRecord::Base
belongs_to :team
belongs_to :user
end
my issue is (i think ) primarily in my controllers and form.controller looks like:
class TeamsController<ApplicationController
def new
#seed=Seed.find_by_id(params[:seed_id])
#league=current_admin.league
#team=current_admin.league.teams.build(:seed_id=>#seed,:approved=>false)
#usernames= #mca.connections.connected.each do |x| x.user end
end
def create
#league=current_admin.league
#team = #league.teams.build(team_params)
if #team.save
flash[:notice] = "Team Request Sent!."
redirect_to '/'
else
flash[:error] = "Unable to request team."
redirect_to :back
end
end
form looks like:
<div class="panel-body">
<div class="container">
<%= form_for #team do |f| %>
<%= f.hidden_field :seed_id, :value => #seed.id %>
<%= f.hidden_field :league_id, :value => #league.id %>
<div class="row">
<!-- <div class="col-md-8"> -->
<div class="form-group">
<%= f.collection_select :speaker, #usernames,:user,:fullname, multiple:true %>
</div>
<!-- </div> -->
</div>
<div class="actions">
<%= f.submit "Create" , class:"btn btn-primary" %>
</div>
<% end %>
</div>
</div>
I would really appreciate some help because it keeps throwing the following error:
NoMethodError in TeamsController#create
undefined method `each' for "2":String
The surface issue you have is that you're passing a string when Rails is expecting an object:
User(#69980837338020) expected, got String(#69980808947560)
This means you should be sending #user rather than "username" etc.
The error will likely be on this line:
#team = #league.teams.build team_params
... which means that you're passing :speaker (which Rails needs as an object) when you should be passing the speaker_id foreign key. Yury Lebedev's answer explains how to do this.
There is a deeper issue.
I don't see how each User can only belong to a Team:
class AddFieldsToUser < ActiveRecord::Migration
def change
add_column :users, :speaker_id, :integer
add_column :users, :speaker2_id, :integer
end
end
For this to work, your users can only be a member of one team.
Whilst this might work for a smaller scale product, I personally feel it to be an incorrect schema setup.
If anything, you'd expect the team to have speaker_1 and speaker_2, which would mean those two options being stored in the teams database (not user).
I think this is the cause of your problem (you're trying to set the speaker_1 and speaker_2 params when they don't exist in the teams db).
-
I would recommend the following:
#app/models/user.rb
class User < ActiveRecord::Base
has_many :speaking_engagements, class_name: "Speaker"
has_many :teams, through: :speaking_engagements
end
#app/models/speaker.rb
class Speaker < ActiveRecord::Base
#columns team_id | user_id | level | created_at | updated_at
belongs_to :team
belongs_to :user
end
#app/models/team.rb
class Team < ActiveRecord::Base
has_many :speakers do
def user(level="1")
find_by(level: level).user
end
end
end
This will give you the ability to call:
#team = Team.find params[:id]
#speakers = #team.speakers
#user.speaking_engagements.where(team: #team)
To save it, you'll be able to use the following:
#app/controllers/teams_controller.rb
class TeamsController < ApplicationController
def new
...
#team = current_admin.league.teams.build seed: #seed, approved: false
end
def create
#league = current_admin.league
#team = #league.teams.build team_params
if #team.save
...
end
private
def team_params
params.require(:team).permit(:name, :speakers) #-> not sure about "speakers"
end
end
This should allow you to define the following:
#app/views/teams/new.html.erb
<%= form_for #team do |f| %>
<%= f.collection_select :speakers, #usernames, :id, :name, multiple: true %>
<%= f.submit %>
<% end %>

Virtual attributes in nested forms are not recorded

You'll see in my code I've got a "has_many => belongs_to" models association and a nested form in the new actions's view.
Plus, I've used Using two separate fields for the same parameter in a Rails form handler?
The problem is the "prix" attribute (in the "Projet" model) is recorded in the database, but not the "nom" attribute (in the "Activite" model).
Maybe the problem is around the strong parameters, but I thinks it's all good in my code...
Or maybe in the code I've found on the other Stackoverflow question I've linked.
There is french words in my code : activite is activity, projet is project, nom is name, prix is price, very easy :)
Thank's for your help !
app/model/projet.rb :
class Projet < ActiveRecord::Base
has_many :activites
accepts_nested_attributes_for :activites,
reject_if: lambda {|attributes| attributes['nom'].blank?}
end
app/models/activite.rb :
class Activite < ActiveRecord::Base
belongs_to :projet
def acttext=(value)
#acttext = value if value
prepare_act
end
def actselect=(value)
#actselect = value if value
prepare_act
end
def acttext
#acttext || self.nom
end
def actselect
#actselect || self.nom
end
private
def prepare_act
self.nom = acttext if acttext
self.nom = actselect if actselect
end
end
app/controllers/projets_controller.rb :
class ProjetsController < ApplicationController
def new
#projet = Projet.new
#activites_options = Activite.pluck(:nom).uniq
2.times { #projet.activites.new}
end
def create
#projet = Projet.new(projet_params)
if #projet.save
redirect_to #projet
else
render 'new'
end
end
private
def projet_params
params.require(:projet).permit(:prix, activites_attributes: [:id, :nom, :heures, :minutes, :acttext, :actselect])
end
end
app/views/projets/new.html.erb :
<div>
<%= form_for #projet do |f| %>
<%= f.label :prix %><br>
<%= f.text_area :prix %><br>
<ul>
<%= f.fields_for :activites do |activites_form| %>
<li>
<%= activites_form.label :choisissez_un_type_dactivité %>
<%= activites_form.select :actselect, #activites_options, {include_blank: true} %>
<%= activites_form.label :ou_créez_en_un_nouveau %>
<%= activites_form.text_field :acttext %><br>
<%= activites_form.label :heures %>
<%= activites_form.text_field :heures %>
<%= activites_form.label :minutes %>
<%= activites_form.text_field :minutes %>
</li>
<br>
<% end %>
</ul>
<p><%= f.submit "Créer le projet" %></p>
<% end %>
</div>
In order for virtual attributes to work, you'll also need to define a setter and a getter. This can be done by either defining the getters and setters explicitly yourself or by using attr_accessor.
Example using attr_accessor:
class Activite < ActiveRecord::Base
attr_accessor :nom
....
end
Example defining both setter and getter manually:
class Activite < ActiveRecord::Base
....
def nom=(value)
super
end
def nom
#nom
end
end
For the second issue with your class naming due to them being French, you'd also want specify the class_name option on both has_many and belongs_to to specify your non english class names as follows:
class Projet < ActiveRecord::Base
has_many :activites, class_name: 'Activite'
end
Similarly, for Activite model:
class Activite < ActiveRecord::Base
belongs_to :projet, class_name: 'Projet'
end

Rails 4, saving attributes nested

I have a model Job. A job require many skills. But when I'm trying to save my job it fails. I'm not sure I understand what I'm doing wrong.
My models:
class Skill < ActiveRecord::Base
has_many :job_skills
has_many :jobs, through: :job_skills
end
class JobSkill < ActiveRecord::Base
belongs_to :skill
belongs_to :job
end
class Job < ActiveRecord::Base
has_many :job_skills, :inverse_of => :job
has_many :skills, through: :job_skills
accepts_nested_attributes_for :job_skills
end
My view:
<%= form_for #job do |f| %>
<div class="row">
<div class="col-md-8">
<h4>General informations</h4>
<br />
<div class="form-group">
<%= f.label :title %>
<%= f.text_field :title, :autofocus => true, class:'form-control' %>
</div><br />
<%= f.fields_for :job_skills do |s| %>
<%= s.text_field :id %>
<% end %>
</div>
</div>
<div class="submit" style="position:relative;">
<%= f.submit "Save", class: 'button button-small' %>
</div>
<% end %>
My controller:
class JobsController < ApplicationController
before_filter :authenticate_user!, :has_company?, :except => [:index, :show]
def create
#job = Job.new(job_params)
#job.company_id = current_user.company_id
#job.user_id = current_user.id
if #job.save
flash[:notice] = "This job offer has been saved."
return redirect_to job_path(#job)
else
flash[:error] = #job.errors.full_messages.to_sentence
render action: :new
end
end
def new
if current_user.company.present?
#job = Job.new(email:current_user.email)
#job.job_skill.build
else
flash[:error] = "You need to create a company before posting a job"
return redirect_to new_company_path()
end
end
private
def job_params
params.require(:job).permit(:status, :title, :description, :remote ,:job_type, :visa_sponsor, :email, :salary_max, :salary_min, :country, :state, :city, job_skill_attributes: [:id])
end
end
So, I'm not sure what I'm doing wrong, when I'm trying to save I get the following error:
#job = Job.new(job_params)
ActiveRecord::RecordNotFound Exception:
Couldn't find Skill with ID=4 for Job with ID= nil
Your pieces of code are a bit confusing for me.
It seems, that you want to create a job and define, what skills are needed for this job.
Why do you need nested attributes?
Normally, you
either edit a list of all the skills, that are probably needed for a job and then assign the propper skills to that job, than you have a has_and_belongs_to_many relationship and can use form helpers for collections. In this case, you don't need a model JobSkill (but a table jobs_skills to store the relationship and is handles transparently by Rails)
or add random skills to a job, then your job has_may :skills and every skill belongs to exactly one job. Here you can use nested attributes. And then you need a way to add nested skill instances i.e. with cocoon. Again, you don't need a model JobSkill.
Which one is your usecase, so I can explain it in more detail.

Ruby on Rails: create records for multiple models with one form and one submit

I have a 3 models: quote, customer, and item. Each quote has one customer and one item. I would like to create a new quote, a new customer, and a new item in their respective tables when I press the submit button. I have looked at other questions and railscasts and either they don't work for my situation or I don't know how to implement them.
quote.rb
class Quote < ActiveRecord::Base
attr_accessible :quote_number
has_one :customer
has_one :item
end
customer.rb
class Customer < ActiveRecord::Base
attr_accessible :firstname, :lastname
#unsure of what to put here
#a customer can have multiple quotes, so would i use has_many or belongs_to?
belongs_to :quote
end
item.rb
class Item < ActiveRecord::Base
attr_accessible :name, :description
#also unsure about this
#each item can also be in multiple quotes
belongs_to :quote
quotes_controller.rb
class QuotesController < ApplicationController
def index
#quote = Quote.new
#customer = Customer.new
#item = item.new
end
def create
#quote = Quote.new(params[:quote])
#quote.save
#customer = Customer.new(params[:customer])
#customer.save
#item = Item.new(params[:item])
#item.save
end
end
items_controller.rb
class ItemsController < ApplicationController
def index
end
def new
#item = Item.new
end
def create
#item = Item.new(params[:item])
#item.save
end
end
customers_controller.rb
class CustomersController < ApplicationController
def index
end
def new
#customer = Customer.new
end
def create
#customer = Customer.new(params[:customer])
#customer.save
end
end
my form for quotes/new.html.erb
<%= form_for #quote do |f| %>
<%= f.fields_for #customer do |builder| %>
<%= label_tag :firstname %>
<%= builder.text_field :firstname %>
<%= label_tag :lastname %>
<%= builder.text_field :lastname %>
<% end %>
<%= f.fields_for #item do |builder| %>
<%= label_tag :name %>
<%= builder.text_field :name %>
<%= label_tag :description %>
<%= builder.text_field :description %>
<% end %>
<%= label_tag :quote_number %>
<%= f.text_field :quote_number %>
<%= f.submit %>
<% end %>
When I try submitting that I get an error:
Can't mass-assign protected attributes: item, customer
So to try and fix it I updated the attr_accessible in quote.rb to include :item, :customer but then I get this error:
Item(#) expected, got ActiveSupport::HashWithIndifferentAccess(#)
Any help would be greatly appreciated.
To submit a form and it's associated children you need to use accepts_nested_attributes_for
To do this, you need to declare it at the model for the controller you are going to use (in your case, it looks like the Quote Controller.
class Quote < ActiveRecord::Base
attr_accessible :quote_number
has_one :customer
has_one :item
accepts_nested_attributes_for :customers, :items
end
Also, you need to make sure you declare which attributes are accessible so you avoid other mass assignment errors.
If you want add info for diferent models i suggest to apply nested_model_form like this reference: http://railscasts.com/episodes/196-nested-model-form-part-1?view=asciicast.
This solution is very simple and cleanest.

Resources