How to create related objects - ruby-on-rails

So I have four models
class User
has_many :user_rows
end
class Assignment
has_many :rows
end
class Row
belongs_to :assignment
has_many :user_rows
end
class UserRow
belongs_to :user
belongs_to :row
end
On the assignment show view I want to loop through the rows and for each row have a user_row to capture user input.
My question is how to intialize the user rows. Would it be best to do this?
class AssignmentController
def show
#assignment = Assignment.include(:rows).find(params[:id])
end
end
Then in the view just use first_or_create
<%= #assignment.rows.each do |row| %>
<%= row.data %>
<%= form_for UserRow.where(row_id: row.id, user_id: current_user.id).first_or_create, remote: true do |f| %>
<%= form_fields %>
<% end %>
<% end %>
As you can see the objects need to be iterated over in the view.
Which I don't like particularly because it's initializing an object in the view. Or is it best to create all the user_rows for each row when a user signs up to the site and if a new row is created create a user_row for all users?
Or is there a better solution I've missed?

I guess, fields_for and accepts_nested_attributes_for might help you. You might check these documents.
fields_for (ActionView::Helpers::FormBuilder) - APIdock
accepts_nested_attributes_for (ActiveRecord::NestedAttributes::ClassMethods) - APIdock

Related

Associate many records to one with has_many_through

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.

Using has_many :through with fields_for and checkboxes to create associations

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 %>

Rails retrieving associated information

I fail to find the correct syntax to retrieve associated information. I have a model 'companyaccount' that belongs to a model 'company'.
class Company < ActiveRecord::base
has_many :companyaccounts
end
class Companyaccount < ActiveRecord::base
belongs_to :company
end
In the View, I can easily retrieve both the 'Companyaccount' 'number' and the 'id' of the associated company through the 'company_id' field of the 'companyaccount' table.
<%= #companyaccount.each do |companyaccount| %>
<%= companyaccount.company_id %>
<%= companyaccount.number %>
<% end %>
The View is called from this basic Controller
def index
#companyaccount = Companyaccount.all
end
Now, I do not want to have the company_id but the actual name of the company (housed in the 'company' table). I manage to do it for one specific company using a controller like this
def index
#companyaccount = Companyaccount.all
#company = Company.first
end
and then in the view change
<%= companyaccount.company_id %>
by
<%= company.name %>
but of course, this will retrieve, for each companyaccount, the same first company and not each of the company associated with the companyaccount. Could anyone provide me with the appropriate syntax to retrieve the associated company's name for each companyaccount in the "each" loop of my View? Thanks.
You can use delegate for this.
class Companyaccount < ActiveRecord::base
belongs_to :company
delegate :name, to: :company, prefix: true
end
and then you can call <%= companyaccount.company_name %>
You should be able to achieve what you want with this loop:
<%= #companyaccount.each do |companyaccount| %>
<%= companyaccount.company.name %>
<%= companyaccount.number %>
<% end %>
def index
#companyaccount = Companyaccount.all
end
and in view
<% #companyaccount.each do |companyaccount| %>
<%= companyaccount.company.name %>
<% end %>
for less db call you can change controller like
#companyaccount = Companyaccount.includes(:company).all

Clean Rails way to have a child collection linked by checkboxes

This is the thing
I have a table/class called Offer
And referenced table/class called OfferDay (because an Offer is available on x days of the week)
It's just a really simple question.
Is there a Ruby on Rails convention for editing such a thing?
My Offer admin form should have 7 checkboxes, prefilled with OfferDays for this instance (or not)
Yes of course, i can loop days of the week in my .erb and check the child collection (days) of the instance, but what about auto binding to instances on postbacK? - it seems the sort of legwork we've all seen before and should have a convention.
Yes I've googled... found sort of it, but not quite... any specific to this one?
I'd handle it with a has_many relationship:
#app/models/offer.rb
Class Offer < ActiveRecord::Base
has_many :days, -> { select(:day_id) }, class_name: "OfferDay", foreign_key: "offer_id"
accepts_nested_attributes_for :days
end
Class OfferDay < ActiveRecord::Base
belongs_to :offer
end
offer_days
id | offer_id | day_id #-> put indexes on day_id & offer_id
You'll be able to call #offer.days to reveal the days on which your offer is running. It will of course return a number, which you'll then translate into a day using the Date class or similar
Editing
This will allow you to assign any number of days to each offer (and each of the days being unique). I'd update like this:
#app/controllers/offers_controller.rb
def new
#offer = Offer.new
#offer.days.build
end
def create
#offer = Offer.new(offer_params)
#offer.save
end
private
def offer_params
params.require(:offer).permit(:offer, :attributes, days_attributes: [:day_id])
end
#app/views/offers/new.html.erb
<%= form_for #offer do |f| %>
<%= f.text_field :offer %>
<%= f.text_field :attributes %>
<%= f.fields_for :days do |builder| %>
<% 7.times do |day| %>
<%= builder.check_box :day_id, day %> #-> need label for each day
<% end %>
<% end %>
<% end %>

has_many through form new method, no id, no elements

I've been hacking around with Rails 3.2.11 for a while, and am trying to do this the 'right' way.
I have three models (Reflection, Skill, Utilization) that relate to each other through has_many: through:
Utilization.rb
class Utilization < ActiveRecord::Base
attr_accessible :reflection, :skill, :used_skill #used_skill is a boolean
belongs_to :reflection
belongs_to :skill
end
Reflection.rb
class Reflection < ActiveRecord::Base
## attributes here ##
has_many :utilizations
has_many :skills, through: :utilizations
accepts_nested_attributes_for :utilizations
accepts_nested_attributes_for :skills
end
Skill.rb
class Skill < ActiveRecord::Base
## attributes here ##
has_many :utilizations
has_many :reflections, through: :utilizations
end
Within the app, skills are already defined. The user action I am trying to support is:
User gets form for new Reflection.
User sees a list of Skills and checks off which ones they have used (Utilization).
User posts to create new Reflection and create the associated Utilization objects.
Here is the new method reflection_controller.rb:
class ReflectionsController < ApplicationController
def new
#reflection = Reflection.new
Skill.all.each do |skill|
#reflection.utilizations.build(skill_id: skill.id, used_skill: false)
end
end
end
And an abbreviated _form.html.erb for Reflections
<%= form_for(#reflection) do |f| %>
<% f.fields_for :utilizations do |builder| %>
<%= builder.label :used_skill %>
<%= builder.check_box :used_skill %>
<%= builder.fields_for :skill do |skill| %>
<%= skill.label :description %>
<%= skill.text_field :description %>
<% end %>
<% end %>
<% end %>
So the problem is that even though there are multiple Skills and I .new the Utilization objects and associate them with the #reflection, they don't show up in the form. I've played with the data structures a little bit, and I can reach the point where in ReflectionController.new #reflection.utilizations contains Utilization objects, it still won't work; when I run #reflection.utilizations.count it returns 0. It looks like the problem is that since none of the objects have an id at that time, it simply will not render out in the form. But my understanding is that one should not create objects during the new method…
Is there something obvious I'm missing? Is there a better way to do this? I've seen examples, include Ryan Bates' Railscast where people just use code like:
def new
#survey = Survey.new
3.times do
question = #survey.questions.build
4.times { question.answers.build }
end
end
and supposedly this works fine.
I really appreciate the help. Trying to figure this out has been driving me crazy. This is my first question on SO, and I'm happy to add any clarifying data or additional code if you think it would help.
You forgot to use =:
<%#### Here ####%>
<%= f.fields_for :utilizations do |builder| %>
<%= builder.label :used_skill %>
<%= builder.check_box :used_skill %>
<%#### and here ####%>
<%= builder.fields_for :skill do |skill| %>

Resources