I have a model called Person that owns Venue. The classes are as followed:
app/models/person.rb
class Person < ActiveRecord::Base
attr_acessible :height
has_one :venue
end
app/models/venue.rb
class Venue < ActiveRecord::Base
attr_acessible :location
end
Now, if I wanted to make a form, it'd be like this if Person didn't has_one Venue:
<%= form_for :person do |f| %>
<%= f.text_field :height %>
<% end %>
How would I do this if I wanted to create the Venue object for this Person with this form?
Figured it out. In this case, you'd use fields_for to emulate the rendering of fields of your sub model.
Related
I have a system where admins can create Exams and price them according to the Branch they are selling them in. So for example, Exam One can cost $5 in Branch One, but $10 in Branch Two.
I made a join table named ExamOffering that has the price of the exam, so each Exam can be sold at many Branches at different prices, and each Branch can have many Exams. Like this:
class Branch < ApplicationRecord
has_many :exam_offerings
has_many :exams, through: :exam_offerings
end
class Exam < ApplicationRecord
has_many :exam_offerings
has_many :branches, through: :exam_offerings
end
class ExamOffering < ApplicationRecord
# this class has a 'price' attribute
belongs_to :exam
belongs_to :branch
end
I need to be able to create a new Exam and select a Branch in the form in order to put in a price, but that attribute is not part of the Exam model, but the ExamOffering join table. I've tried some ways but failed (using accepts_nested_attributes_for :exam_offerings in the Exam model or iterating through all the Branches and creating ExamOfferings for each one in the controller). What's the 'Rails way' of doing this? I think it's a pretty common case, but I haven't found an answer that fits my case. Maybe this has a name and I don't know it.
It could be phrased like this: When I'm creating a new Exam, I want to be able to input a price for each existing Branch.
Thanks.
What you want to do is create the join model explicitly:
# config/routes.rb
resources :exam_offerings, only: [:new, :create]
class ExamOffering < ApplicationRecord
# this class has a 'price' attribute
belongs_to :exam
belongs_to :branch
accepts_nested_attributes_for :exam
validates_associated :exam
end
# app/views/exam_offerings/new.html.erb
<%= form_with(model: #exam_offering) do |f| %>
<div class="field" %>
<%= f.label(:branch_id) %>
<%= f.collection_select(:branch_id, Branch.all, :id, :name) %>
</div>
<div class="field" %>
<%= f.label(:price) %>
<%= f.number_field(:price) %>
</div>
<%= f.fields_for(:exam) do |exam_field| %>
<div class="field" %>
<%= exam_field.label(:foo) %>
<%= exam_field.text_field(:foo) %>
</div>
<% end %>
<%= f.submit %>
<% end %>
class ExamOfferingsController < ApplicationController
# GET /exam_offerings/new
def new
#exam_offering = ExamOffering.new
end
# POST /exam_offerings
def create
#exam_offering = ExamOffering.new(exam_offering_params)
if #exam_offering.save
redirect_to #exam_offering, notice: 'Offering Created'
else
render :new, notice: 'Oh No!'
end
end
private
def exam_offering_params
params.require(:exam_offering)
.permit(
:branch_id, :price,
exam_attributes: [:foo]
)
end
end
Remember that there is nothing special about join models - and they don't always have to be created implicitly. Quite often they are actually an important part of the buisness logic and not just plumbing.
But really if I was building this I would just create a normal POST /exams route where the user can create an exam and then let them create the offering after that. Use AJAX if needed to make it appear seamless.
I ended up using accepts_nested_attributes_for in the Exam model, so I didn't have to use another view to put the price, so it's something like this:
class Branch < ApplicationRecord
has_many :exam_offerings
has_many :exams, through: :exam_offerings
end
class Exam < ApplicationRecord
has_many :exam_offerings
has_many :branches, through: :exam_offerings
accepts_nested_attributes_for :exam_offerings
end
class ExamOffering < ApplicationRecord
# this class has a 'price' attribute
belongs_to :exam
belongs_to :branch
end
The important part was the view. I needed to use fields_for with a new object when creating the exam, but fetch the existing exam_offerings when editing the exam, so I have this code:
<% if exam.exam_offerings.empty? %>
<% Branch.all.each do |branch| %>
<%= form.fields_for :exam_offerings, #exam.exam_offerings.build do |offering| %>
<div class="field">
<%= offering.label "Price in #{branch.name}" %>
<%= offering.text_field :price %>
<%= offering.hidden_field :branch_id, value: branch.id %>
</div>
<% end %>
<% end %>
<% else %>
<%= form.fields_for :exam_offerings do |offering| %>
<div class="field">
<%= offering.label "Price in #{offering.object.branch.name}" %>
<%= offering.text_field :price %>
</div>
<% end %>
<% end %>
I have a model Playlist, and a model User, both of which have_many of each other, through a join model PlaylistUser.
On my playlists#show action, I want to print a list of all of a Playlist's Users, along with the first two Playlists associated with each of those Users.
Right now here's what I have:
playlists/show.html.erb
<% #playlist = Playlist.find(params[:id]) %>
<% #playlist.users.each do |user| %>
<%= user.name %>
<%= user.playlists.first.name %>
<%= user.playlists.second.name %>
<% end %>
models
class User < ActiveRecord::Base
has_many :playlist_users
has_many :playlists, :through => :playlist_users
end
class PlaylistUser < ActiveRecord::Base
belongs_to :playlist
belongs_to :user
end
class Playlist < ActiveRecord::Base
has_many :playlist_users
has_many :users, :through => :playlist_users
end
But there is an enormous change in performance when I delete the user.playlists lines and print out only the user.name, because then the database only has to make one query, as opposed to hundreds.
Does anyone know of a way to make this more efficient? Maybe I could somehow load all the associated Playlists in the original query?
You can use the includes method to tell Rails to preload associated records with just one query upfront.
Loading from the database is a controller responsibility and should not happen in the view. Therefore, add the following to your controller:
playlist = Playlist.find(params[:id])
#users = playlist.users.includes(:playlists)
And change your view to just iterate over the users array:
<% #users.each do |user| %>
<%= user.name %>
<%= user.playlists.first.name %>
<%= user.playlists.second.name %>
<% end %>
Am using rails 3.2.13 and I have models for two entities like so
class Restaurant < ActiveRecord::Base
attr_accessible :description, :menu, :restaurant_name
has_many :cuisines
end
class Cuisine < ActiveRecord::Base
attr_accessible :cuisine_name, :restaurant_id
attr_accessible :cuisine_ids
belongs_to :restaurant
end
The controller action for creating a restaurant look like this
I have a form for creating a restaurant using simple form gem like so
<%= simple_form_for #restaurant do |f| %>
<%= f.input :restaurant_name %>
<%= f.input :description %>
<%= f.input :menu %>
<%= f.association :cuisines, label_method: :cuisine_name %>
<%= f.button :submit %>
<% end %>
Am basically suppose to chose from a group of cuisines which simple form helps with. However when i select the cuisine and try to create the restaurant. It brings back the error.
ActiveModel::MassAssignmentSecurity::Error at /restaurants
Can't mass-assign protected attributes: cuisine_ids
As you can see in the model. I placed attribute as accessible but it didn't work. I even tried the singular version cuisine_id with no luck. I have no idea what is wrong? I would prefer not to tamper with the rails defaults for protecting against mass assignment. Any clues?
Cuisine doesn't have cuisine_ids, Restaurant does.
Move your attr_accessible :cuisine_ids into the Restaurant model.
I have two models. User have_many articles. How can I make one view for this models with simple_form gem? I can't find this part in wiki and readme :(
For example, something like "simple_form_for [ user, articles ] do |f|"
You can use the simple_fields_for method to create sub-forms for your main model's associations.
<%= simple_form_for #user do |f| %>
<%= f.input :name %>
<%= simple_fields_for :articles do |article_form| %>
<%= article_form.input :title %>
<% end %>
<% end %>
Also, don't forget to tell your model to accept nested attributes, or the record will be invalid.
class User < ActiveRecord::Base
has_many :articles
attr_accessible :articles_attributes
accepts_nested_attributes_for :articles
end
Newbie question here.
I have two models which are related to each other:
class Relationship < ActiveRecord::Base
...
attr_accessible :source_item_id, :target_item_id
belongs_to :target_item, :class_name => "Item"
belongs_to :source_item, :class_name => "Item"
belongs_to :user
...
end
and:
class Item < ActiveRecord::Base
...
attr_accessible :address
...
end
Now, within my form, I already know the source_item_id. I want to be able to enter an address into the form, and create both a target_item, and the associated Relationship.
<%= form_for #new_relationship do |f| %>
<% #new_relationship.source_item_id = #current_item.id %>
<%= f.hidden_field :source_item_id %>
<%= f.submit "New Relationship" %>
<% end %>
You generally do the relationship in the controller and let the form just collect the data. If you are asking how to have a form with two models, check out this post here. I hope I understood your question right !!!