I have a form where a student can enroll in a class. When the user submits the form, he is enrolled in the class and his payment information is saved. In other words, an Enrollment object is created and the Student object is updated... except I cannot get the Student to update. Is this possible? If so, how?
My models...
class Student < ActiveRecord::Base
has_many :enrollments
end
class Enrollment < ActiveRecord::Base
belongs_to :student
accepts_nested_attributes_for :student
end
My (abbreviated) form...
<%= form_for :enrollment, html: { id: "enrollment_form" } do |f| %>
<%= f.fields_for :student_attributes do |student_builder| %>
<%= student_builder.hidden_field :payment_name %>
<% end %>
<%= f.hidden_field :payment_token %>
<div class="field terms">
<%= f.check_box :agreed_to_terms %>
<%= f.label :agreed_to_terms, "I agree to the terms and conditions." %>
</div>
<% end %>
My controller...
class EnrollmentsController < ApplicationController
def create
#enrollment = Enrollment.new(enrollment_params)
#enrollment.clazz_id = #clazz.id
#enrollment.student_id = #student.id
#enrollment.save
end
private
def enrollment_params
params.require(:enrollment).permit(:payment_token, :agreed_to_terms, student_attributes: [:payment_name])
end
end
The POST parameters...
{
"enrollment"=> {
"student_attributes"=> {
"payment_name"=> "MasterCard ending in 9840"
},
"payment_token"=> "CC11ho86XxVqsUW7Cn9YjCHg?1376007969212",
"agreed_to_terms"=> "1"
},
"clazz_id"=> "7"
}
I've tried every permutation of student, students, _attributes in the form builder but none of them seem to work.
Ok, there are a few things I see:
Nested attributes, as described in the API, "allow you to save attributes on associated records through the parent." This means that
class Enrollment < ActiveRecord::Base
belongs_to :student
accepts_nested_attributes_for :student
end
inherently won't work because you're trying to accept nested atributes from the parent. So you need to start by rethinking your Active Record assications.
If we pretend that's all squared away, then more syntactical errors are:
Replace
<%= f.fields_for :student_attributes do |student_builder| %>
with
<%= f.fields_for :students do |student_builder| %>
This can be confusing, but passing :students to the fields_for helper calls the nested student object, whereas :student_attributes is the hash key from the POST parameters that fields_for produces.
In your strong parameters, you also need to permit the student :id so that your updating action has a reference. Otherwise, it will just create a new student. So change it to
private
def enrollment_params
params.require(:enrollment).permit(:payment_token, :agreed_to_terms, student_attributes: [:payment_name, :id])
end
I'm not sure if that's everything, but hopefully it's a start. Good luck.
Related
I'm trying to associate several dinners to a meal with a has_many: through relationship when the user hits "save". My question is not with the mechanics of has_many: through. I know how to set that up and I have it working in the Rails console, but I just don't know how to set up the view to associate several records at once.
I have models set up like this:
class Dinner < ApplicationRecord
has_one :user
has_many :meals
has_many :meal_plans, through: :meals
end
class MealPlan < ApplicationRecord
belongs_to :user
has_many :meals
has_many :dinners, through: :meals
end
class Meal < ApplicationRecord
belongs_to :dinner
belongs_to :meal_plan
end
With a meal plan controller:
def create
#meal_plan = current_user.meal_plans.build(meal_plan_params)
respond_to do |format|
if #meal_plan.save
format.html { redirect_to root_path, notice: 'Dinner was successfully created.' }
end
end
end
private
def meal_plan_params
params.require(:meal_plan).permit(dinners: [])
end
My question is about the view, in the new view, I create a #meal_plan and I want to pass several different dinners into the meal plan. Below the value: #dinners is just 7 random dinners pulled from the Dinners table.
<%= form_with model: #meal_plan do |f| %>
<%= f.hidden_field(:dinners, value: #dinners)%>
<%= f.submit 'Save'%>
<% end %>
Again, I've gotten this to work by running something like `usr.meal_plans.create(dinners: [d1, d2])`` in the Rails console but I don't
You can use the form option helpers to generate selects or checkboxes:
<%= form_with model: #meal_plan do |f| %>
<%= f.collection_select :dinner_ids, Dinner.all, :id, :name, multiple: true %>
<%= f.collection_checkboxes :dinner_ids, Dinner.all, :id, :name %>
<%= f.submit 'Save'%>
<% end %>
_ids is a special setter / getter generated by ActiveRecord for has many assocations. You pass an array of ids and AR will take care of inserting/removing the join table rows (meals).
You also need to change the name in your params whitelist:
def meal_plan_params
params.require(:meal_plan).permit(dinner_ids: [])
end
If you want to to pass an array through hidden inputs you can do it like so:
<% #dinners.each do |dinner| >
<%= hidden_field_tag "meal_plans[dinner_ids][]", dinner.id %>
<% end %>
See Pass arrays & objects via querystring the Rack/Rails way for an explaination of how this works.
I can't figure this out for the life of me but here are my models:
class User < ApplicationRecord
has_many :user_stores
has_many :stores, through: :user_stores
end
class UserStore < ApplicationRecord
belongs_to :user
belongs_to :store
end
class Store < ApplicationRecord
has_many :user_stores
has_many :users, through: :user_stores
end
So I have a join table, I'm trying to make a form, which would have selected checkboxes next to the store names that the user has selected (this information would come from the join table relationship) and open checkboxes for the remaining stores (coming from the Store model). How do I show that in the view/make it work in the controller as well. Would I use collections instead? ( I am using Devise and Simple Form gem )
This is what I have so far:
<h1>Add Favorite Stores</h1>
<%= simple_form_for(#user, html: { class: 'form-horizontal' }) do |f| %>
<%= f.fields_for :stores, #user.stores do |s| %>
# not sure if this is the right way or not
<% end %>
<%= f.button :submit %>
<% end %>
Store Controller:
class StoresController < ApplicationController
...
def new
#user = current_user
#stores = Store.all
# #user.stores => shows user's stores (from join table)
end
end
When you set up a one or many to many relationship in rails the model gets a _ids setter:
User.find(1).store_ids = [1,2,3]
This would for example setup a relation between user 1 and the stores with ids 1,2 and 3.
The built in Rails collection form helpers make use of this:
<%= form_for(#user) do |f| %>
<% f.collection_check_boxes(:store_ids, Store.all, :id, :name) %>
<% end %>
This creates a list of checkboxes for each store - if an association exists it will already be checked. Note that we are not using fields_for since it is not a nested input.
SimpleForm has association helpers which add even more sugar.
<h1>Add Favorite Stores</h1>
<%= simple_form_for(#user, html: { class: 'form-horizontal' }) do |f| %>
<%= f.association :stores, as: :check_boxes %>
<%= f.button :submit %>
<% end %>
That title's a mouthful.
So I have something like this:
class Company < ActiveRecord::Base
has_many :company_partner_associations
has_many :partners, through: :company_partner_associations
end
class CompanyPartnerAssociation
belongs_to :company
belongs_to :partner
end
class Partner
has_many :company_partner_associations
has_many :companies, through: :company_partner_associations
end
And on a company form, i'm trying to make a list of all Partners with a checkbox next to them. If I check one, it creates association. If I uncheck it destroys.
<%= f.fields_for :company_partner_associations, Partner.all do |p| %>
<%= f.check_box :partner_id %>
<% end %>
fails because the object getting passed is a Partner, so getting undefined partner_id on Partner
I'm sure there's a nifty solution out there! Thank you!
Do this:
<%= f.collection_check_boxes :partner_ids, Partner.all, :id, :name %>
No fields_for.
This will have to be accompanied in the controller with the following params:
params.require(:company).permit(:company, :params, partner_ids: [])
This should set the partner_ids in your #company model.
With HABTM, you can declare associative data by populating the "collection_singular_ids" method; HMT has the same method appended with the has_many relation:
Although this will replace the current associated objects, it is much simpler than calling f.fields_for - especially for picking partners.
--
You can also use collection_check_boxes which is meant for this purpose :)
Not totally sure this is the problem here, but I think it could be that your controller is just not permitting the array of partner ids. So in your company_partner_params in your company controller needs to permit something like partner_attributes: [:id]. The syntax might not be perfectly correct there, but if that's something you're missing, you should look around for that.
This is what I think the form should look like:
<%= form_for #company do |f| %>
<%= f.fields_for :partners, Partner.all do |partner| %>
...
<% end %>
<% end %>
I'm trying to build a rather complex nested form in rails and am stuck.
Basically, I have three models - Applicant, DataPoint, ApplicantDataPointValue .
The user can create a new DataPoint, give it a name ("gender" etc.) select it's type ("string","integer" etc.). The type determines what column the data will eventually be saved in in the ApplicantDataPointValue table.
I then want the user, when they're creating a new Applicant, to be able to add a value for each DataPoint into the ApplicantDataPointValue table
My models look like the following:
Applicant:
class Applicant < ActiveRecord::Base
has_many :applicant_data_point_values, dependent: :destroy
has_many :data_points, :through => :applicant_data_point_values
accepts_nested_attributes_for :data_points
accepts_nested_attributes_for :applicant_data_point_values
attr_accessible :data_points_attributes, :applicant_data_point_values_attributes
end
DataPoint:
class DataPoint < ActiveRecord::Base
has_many :applicant_data_point_values
has_many :applicants, :through => :applicant_data_point_values
accepts_nested_attributes_for :applicant_data_point_values
end
ApplicantDataPointValue:
class ApplicantDataPointValue < ActiveRecord::Base
belongs_to :data_point
belongs_to :applicant
end
But I'm at a loss to what to do in the 'new' and 'create' sections of my controller or how to construct the form.
Any insight would be greatly appreciated.
From what I understand, the form for the User will also have multiple ApplicantDataPointValue fields. (but that form won't allow creating of new DataPoint fields, right?)
In the controller new action, you'll want to set up your model with associated data point values:
def new
#user = User.new
DataPoint.all.each do |data_point|
applicant_data_point_value = #user.applicant_data_point_values.build
applicant_data_point_value.data_point = data_point
end
end
And then, display a text box for each data point value.
<%= form_for #user do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<% #user.applicant_data_point_values.each do |data_point_value| %>
<%= f.fields_for :applicant_data_point_values, data_point_value do |fields| %>
<%= fields.label :value, data_point_value.data_point.type %>
<%= fields.text_field :value %>
<% end %>
<% end %>
Reference: http://railscasts.com/episodes/196-nested-model-form-part-1
I have 2 models:
class Store < ActiveRecord::Base
attr_accessible :prices_attributes, :business_name
has_many :prices
accepts_nested_attributes_for :prices
end
class Price < ActiveRecord::Base
attr_accessible :price, :product_name, :purchase_date
belongs_to :store
end
I'm creating a Store and Prices together making it a nested form:
class StoresController < ApplicationController
def new
#store = Store.new
3.times {#store.prices.build }
end
end
Nested form:
<%= form_for #store do |f| %>
<%= f.text_field :business_name %>
<%= date_select("price", "purchase_date") %>
<%= f.fields_for :prices do |up| %>
<%= up.text_field :product_name %>
<%= up.text_field :price %>
<% end %>
<% end %>
I want to place the purchase_date outside of my f.fields_for so users only have to choose one date_select for all prices made. This doesn't work though. The purchase_date does not show up on the form. How can I get it do this?
If the child forms display the purchased_date and purchased inputs - and you want to set the values of those inputs based on the input values in the parent form - you're going to have to script that on the client side (javascript/jquery) so the children's values get set in response to the parent input's change event (or similar).
If the child forms do not show these inputs, you will have to set the children's attributes on the server side (model or controller) based on the parent's attributes (if you choose to add them to the parent) or by using the params sent on the request.