My Models:
class Event < ActiveRecord::Base
belongs_to :organization
has_many :directors
has_many :vips, :through => :directors
end
class Vip < ActiveRecord::Base
belongs_to :organization
has_many :directors
has_many :events, :through => :directors
end
class Director < ActiveRecord::Base
belongs_to :vip
belongs_to :event
end
My New Event form:
<%= form_for [#organization, #event] do |f| %>
<p>
<%= f.label :when %>
<%= f.date_select :when %>
</p>
<p>
<%= f.label :name %>
<%= f.text_field :name %>
</p>
<p>
<%= f.label :vip %>
<%= f.select :vip_id, options_for_select(#organization.vips.all.map {|v| [v.name, v.id]}) %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
And my Events Controller:
def new
#organization = Organization.find(params[:organization_id])
#event = #organization.events.new
#director = Director.new
end
def create
#organization = Organization.find(params[:organization_id])
#event = #organization.events.build(event_params)
if #event.save
redirect_to organization_path(#organization)
else
render 'new'
end
end
So, my Vips are created earlier in the process and sit in the vips table. When I submit the new event form, I get a new entry to my events table just fine. What I want to do is have my directors table be populated with a new entry when I submit the new events form. The directors table would need the event_id of the event I just created, and the vip_id from the select tag in the form.
I thought about adding this to the create action
def create
#director = Director.new
if #event.save
redirect_to organization_path(#organization)
#director.vip_id = #event.vip_id
#director.event_id = #event.id
But that didn't create an entry into the director table. Is there a way to do this that I'm not seeing?
#director.vip_id = #event.vip_id
#director.event_id = #event.id
So, you have these two assignments, #director.event_id = #event.id this one is fine because you just created the event and you have the #event.id.
But, the first one won't work. Looking at your Model associations, #event does not have a vip_id, so you can't call #event.vip_id. You have to go to the vip through the organization, something like:
#event.organization.vips.first.vip_id
or, #event.vips.first.vip_id this would work too as you have :through => :directors association.
These two are the only way I see to get a valid vip_id corresponding to the event. Although, this will only get the first vip_id or you can specify some criteria in a where clause to get a particular vip_id for that event.
If that works for you, good. Otherwise, you may have to re-think your associations among the models.
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'm kinda new to ruby on rails, I've been reading documentation on assosiations and I've been having an easy time (and usually a quick google search solves most of my doubts) however recently I'm having problems with a seemingly easy thing to do.
What I'm trying to do is to create an Event, linked to an existing Category.
Event model
class Event < ApplicationRecord
has_many :categorizations
has_many :categories, through: :categorizations
accepts_nested_attributes_for :categorizations
.
.
.
end
Category model
class Category < ApplicationRecord
has_many :categorizations
has_many :events, through: :categorizations
end
Categorization model
class Categorization < ApplicationRecord
belongs_to :event
belongs_to :category
end
Event controller
class EventsController < ApplicationController
def new
#event = Event.new
end
def create
#user = User.find(current_user.id)
#event = #user.events.create(event_params)
if #event.save
redirect_to root_path
else
redirect_to root_path
end
end
private
def event_params
params.require(:event).permit(:name, category_ids:[])
end
Here is the form, which is where I think the problem lies:
<%= form_for #event, :html => {:multipart => true} do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.fields_for :categorizations do |categories_fields|%>
<% categories = [] %>
<% Category.all.each do |category| %>
<% categories << category.name %>
<% end %>
<%= categories_fields.label :category_id, "Category" %>
<%= categories_fields.select ( :category_id, categories) %>
<% end %>
.
.
.
<%= f.submit "Create"%>
<% end %>
I previously populate the Category db with some categories, so what's left to do is to while creating an event, also create a categorization that is linked both to the new event and the chosen Categorization. but the things I've tried don't seem to be working.
Other things seem to be working ok, whenever I try to submit the event all things are populated as expected except the categorization.
As you mentioned that you are new to rails, you'll find this cocoon gem very interesting. You can achieve what you wanted. And the code will cleaner.
I don't have the points to comment, that's why I am giving this as an answer.
I have two models, Event and Vip, each associated with HABTM relationship.
I have a events_vips table with columns for vip_id, and event_id
My new event form:
<%= form_for [#organization, #event] do |f| %>
<p>
<%= f.label :when %>
<%= f.date_select :when %>
</p>
<p>
<%= f.label :name %>
<%= f.text_field :name %>
</p>
<p>
<%= f.label :vip %>
<%= f.select :vip_id, options_for_select(#organization.vips.all.map {|v| [v.name, v.id]}) %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
My events controller:
def new
#organization = Organization.find(params[:organization_id])
#event = #organization.events.new
end
def create
#organization = Organization.find(params[:organization_id])
#event = #organization.events.build(event_params)
if #event.save
redirect_to organization_path(#organization)
else
render 'new'
end
end
I want my events_vips table to get populated with a new event/vip relationship when I create an event. Or is that the wrong way to do it?
Edit:
My models looks like:
class Vip < ActiveRecord::Base
belongs_to :organization
has_and_belongs_to_many :events
end
class Event < ActiveRecord::Base
belongs_to :organization
has_and_belongs_to_many :vips
end
I would use has_many :events, :through => :some_table_name and has_many :organizations, :through => :some_table_name rather than a habtm. The reason for this is that, with the habtm, you are limited in several ways. First, the join table name is restricted to events_organizations because of the alphabetical nature of the habtm construct, and with has_many :through you can use an arbitrary table name that can be more descriptive.
More importantly, you cannot store any other information about the relationship in a habtm join table. In the has_many :through, for example, you could store the table number, arrival time, dock scheduling, or other information that applies to a specific organization for a specific event in the join table, where it belongs. This way, the join table stores not only the relationship, but information about the nature of that relationship.
In either case, the answer to your question is to use accepts_nested_attributes_for :your_join_table_model in the model that you will be manipulating the data from (which could be both).
Here is part 1 of a railscast dedicated to the subject.
Here is a link to the HABTM checkboxes railscast, if you wanted to go that route.
You need to find the vip from your params and it to the event#vips.
def create
#organization = Organization.find(params[:organization_id])
#event = #organization.events.build(event_params)
if #event.save
vip = Vip.find(event_params[:vip_id])
#event.vips << vip # this will create the association
redirect_to organization_path(#organization)
else
render 'new'
end
end
now its always a good idea to add validation checks, for instance insuring that a vip param came in the form, but the above is how to actually forge the association. once the event is saved, its vip association can be made.
I am making a model where users can belong to multiple teams and teams have multiple people.
I have checkboxes but they don't pass the value onto the object.
class User < ActiveRecord::Base
attr_accessible :email, :name
has_many :teams
accepts_nested_attributes_for :teams
end
class Team < ActiveRecord::Base
has_many :users
attr_accessible :name
end
Here is the code in my controller
def create
#users = User.all
#user = User.new
#teams = Team.all
#user.attributes = {:teams => []}.merge(params[:user] || {})
end
Here is the code in my view file
<%= form_for #user, url: {action: "create"} do |f| %>
<%= f.label :teams%>
<% for team in #teams %>
<%= check_box_tag team.name, team.name, false, :teams => team.name%>
<%= team.name -%>
<% end %>
<%= submit_tag "Create User" %>
I am trying to show it into
<%= user.teams.name %>
But the only output is "Team"
Can someone tell me what I am doing wrong?
Actually, you can't do a many-to-many relationship that way... you need to do has_many :through or alternatively has_and_belongs_to_many Nice explanation here...
http://guides.rubyonrails.org/association_basics.html
I am currently working on a nested model form.
I have a subject model.
This subject model has lessons of 3 different types - tutorial, lecture and laboratory.
I am able to get the nested form working with https://github.com/ryanb/nested_form.
But I want to fix it such that in the form only 3 forms for the child(lesson model) will be produced and that their first field (lesson_type field) will be automatically filled in and fixed.
I am not too sure on how to model such a situation on Rails.
These are the codes I have so far.
Any advice on what I could try out or point out the mistakes I have made would be appreciated.
This is the form.
Right now I could get the form to show up three times on my controller but I am not sure how I could generate different values for the fields. They are all showing lecture as of now.
<%= nested_form_for(#subject, :remote=>true) do |f| %>
<div class="field">
<%= f.label :subject_code %><br />
<%= f.text_field :subject_code %>
</div>
<%= f.fields_for :lessons do |lesson_form| %>
<%= lesson_form.label :lesson_type %><br/>
<%= lesson_form.text_field :lesson_type, :value=> "lecture"%><br/>
<%= lesson_form.label :name %><br/>
<%= lesson_form.text_field :name %><br/>
<%= lesson_form.fields_for :lesson_groups do |lesson_group_form| %>
<%= lesson_group_form.label :group_index %><br/>
<%= lesson_group_form.text_field :group_index %>
<%= lesson_group_form.link_to_remove "Remove this task" %>
<% end %>
<p><%= lesson_form.link_to_add "Add a lesson_group",:lesson_groups,:id=>"open-lesson"%></p>
<% end %>
<% end %>
This is the controller. The creation will happen on the index page.
def index
#subjects = Subject.all
#subject = Subject.new
lecture = #subject.lessons.build
lecture.lesson_groups.build
lecture.destroy
tutorial = #subject.lessons.build
tutorial.lesson_groups.build
tutorial.destroy
laboratory = #subject.lessons.build
laboratory.lesson_groups.build
laboratory.destroy
respond_to do |format|
format.html # index.html.erb
format.json { render json: #subjects }
format.js
end
end
The subject model
class Subject < ActiveRecord::Base
attr_accessible :subject_code, :lessons_attributes
has_many :lessons, :dependent => :destroy
accepts_nested_attributes_for :lessons, :allow_destroy => :true, :reject_if => lambda { |a| a[:lesson_type].blank? }
end
And the lesson model
class Lesson < ActiveRecord::Base
belongs_to :subject
attr_accessible :lesson_type, :name, :subject, :lesson_groups_attributes
has_many :lesson_groups, :dependent => :destroy
accepts_nested_attributes_for :lesson_groups, :allow_destroy => true
end
Okay, I am not sure if this is to the Rails convention but I got it working according to what I want. Added the following lines in the subject model: Basically assigning the lesson type field in the model.
lecture = #subject.lessons.build
lecture.lesson_type = "lecture"
lecture.lesson_groups.build
lecture.destroy
tutorial = #subject.lessons.build
tutorial.lesson_type = "tutorial"
tutorial.lesson_groups.build
tutorial.destroy
laboratory = #subject.lessons.build
laboratory.lesson_type = "laboratory"
laboratory.lesson_groups.build
laboratory.destroy
And to make it such that they can't change the lesson type I made it read only
<%= lesson_form.text_field :lesson_type, :readonly=>true%><br/>