How do I associate data with each other using a join table? - ruby-on-rails

I have three models: Lesson, Situation, Fate(join table).
In this app, A situation can have many lessons and a lesson can belong to multiple situations.
I would essentially like the tables to look like this.
Situation
id.....name.....................description
1.....Ordering Food......You go into a restaurant and order food.
2.....Introduce yourself.You meet someone for the first time and you introduce yourself.
Lesson
id.....name............description..............lesson_text
1......Order food....How to order food..Blah blah blah, this is how you order food.
2......Call the waiter.How to call the waiter Blah blah blah, this is how you call the waiter
3 Pay for food How to pay for food Blah blah blah, this is how you pay for food.
4 Greet a person How to greet a person Blah blah blah, this is how you greet a person.
5 Ask a question How to ask a question Blah blah blah, this is how you ask a question.
Fate
situation_id lesson_id required
1.................1...............yes
1.................2...............yes
1.................3...............no
2.................3...............yes
2.................4...............yes
2.................5...............yes
I have the tables set up but I'm not sure how I would associate a lesson to a situation.
This is what my application looks like currently
Situations controller
class SituationsController < ApplicationController
def index
#situations = Situation.all
end
def new
#situation = Situation.new
end
def create
#situation = Situation.new(params[:situation])
respond_to do |format|
if #situation.save
format.html { redirect_to #situation }
end
end
end
def show
#situation = Situation.find(params[:id])
#lesson = #situation.lessons.new
end
def edit
#situation = Situation.find(params[:id])
end
def update
#situation = Situation.find(params[:id])
respond_to do |format|
if #situation.update_attributes(params[:situation])
format.html { redirect_to #situation }
end
end
end
def destroy
#situation = Situation.find(params[:id])
#situation.destroy
respond_to do |format|
format.html { redirect_to situations_path }
end
end
end
Lessons controller
class LessonsController < ApplicationController
def index
#lessons = Lesson.all
end
def new
#lesson = Lesson.new
end
def create
#lesson = Lesson.new(params[:lesson])
respond_to do |format|
if #lesson.save
format.html { redirect_to #lesson }
end
end
end
def show
#lesson = Lesson.find(params[:id])
end
def edit
#lesson = Lesson.find(params[:id])
end
def update
#lesson = Lesson.find(params[:id])
respond_to do |format|
if #lesson.update_attributes(params[:lesson])
format.html { redirect_to #lesson }
end
end
end
def destroy
#lesson = Lesson.find(params[:id])
#lesson.destroy
respond_to do |format|
format.html { redirect_to lessons_path }
end
end
end
Routes
root :to => 'situations#index'
resources :situations do
resources :lessons
end
resources :difficulties
Situation.rb
class Situation < ActiveRecord::Base
attr_accessible :name, :description
has_many :fates
has_many :lessons, :through => :fates
end
Lesson.rb
class Lesson < ActiveRecord::Base
attr_accessible :name, :description, :difficulty_id, :lesson_text
has_many :fates
has_many :situations, :through => :fates
end
Fate.rb
class Fate < ActiveRecord::Base
belongs_to :lesson
belongs_to :situation
end
Thanks for the help! and I'm really sorry about the messy formatting.

So if you are creating a new situation and want to also create new lessons that will be associated with it..
app/models/situation.rb
attr_accessible :name, :description, :difficulty_id, :lesson_text, :lessons_attributes
accepts_nested_attributes_for :lessons
app/controllers/situations_controller.rb
def new
#situation = Situation.new
2.times do{#situation.lessons.build}
respond_to do |format|
format.html
end
end
app/views/lessons/_form.html.erb
<% form_for #situation do |f| %>
<%= f.text_field :name %>
<%= f.text_field :description %>
<% f.fields_for :situations do |lesson_field| %>
<%= lesson_field.text_field :name %>
<%= lesson_field.text_field :lesson_text %>
<% end %>
<%= f.submit %>
<% end %>
Essentially you need a nested form (there is plenty examples). I typed this off the iPhone, hope it works )

Related

Rails nested resource params in reverse order

I've spent a while trying to debug this behavior unsuccessfully, so I'm hoping for help figuring out why my nested resource parameters appear to be getting included in the URL in the wrong order. For some reason, adding and deleting lessons for a course works, but editing a lesson crashes because ActiveRecord tries to find a lesson using the course ID and vice versa.
Course and lesson models
class Course < ApplicationRecord
has_many :lessons, dependent: :destroy
extend FriendlyId
friendly_id :title, use: :slugged
class Lesson < ApplicationRecord
belongs_to :course
extend FriendlyId
friendly_id :title, use: :slugged
Lessons controller
class LessonsController < ApplicationController
before_action :set_lesson, only: [:show, :edit, :update, :destroy ]
def index
#lessons = Lesson.all
end
def show
end
def new
#lesson = Lesson.new
#course = Course.friendly.find(params[:course_id])
authorize #lesson
end
def edit
authorize #lesson
end
def create
#lesson = Lesson.new(lesson_params)
#course = Course.friendly.find(params[:course_id])
#lesson.course_id = #course.id
if #lesson.save
redirect_to course_lesson_path(#course, #lesson), notice: "Lesson was successfully created."
else
render :new, status: :unprocessable_entity
end
end
def update
authorize #lesson
if #lesson.update(lesson_params)
redirect_to course_lesson_path(#course, #lesson), notice: "Lesson was successfully updated."
else
render :edit
end
end
def destroy
authorize #lesson
#lesson.destroy
redirect_to course_path(#course), notice: "Lesson was successfully destroyed."
end
private
def set_lesson
#course = Course.friendly.find(params[:course_id])
#lesson = Lesson.friendly.find(params[:id])
end
def lesson_params
params.require(:lesson).permit(:title, :content, :course_id)
end
end
Routes
resources :courses do
resources :lessons
end
And what shows up when I do rails routes:
edit_course_lesson GET /courses/:course_id/lessons/:id/edit(.:format)
However, when I actually edit a lesson, the parameters seem to get switched, which causes a crash. See below for an example of where it thinks the fourth lesson is the course.
URL: /courses/fourth-lesson/lessons/forensic-science-344/edit
ActiveRecord::RecordNotFound in LessonsController#edit
can't find record with friendly id: "fourth-lesson"
private
def set_lesson
#course = Course.friendly.find(params[:course_id]) <- Crashes on this line
#lesson = Lesson.friendly.find(params[:id])
end
Update: here's the form for editing a lesson.
.container
= simple_form_for([#course, #lesson]) do |f|
= f.error_notification
= f.error_notification message: f.object.errors[:base].to_sentence if f.object.errors[:base].present?
.form-inputs
= f.input :title
= f.input :content
%small
= f.error :content, class: 'text-danger'
.form-actions
= f.button :submit, class: 'btn btn-primary my-4'
It turns out that I didn't pass in the course parameter on the edit link. I had link_to 'Edit', edit_course_lesson_path(lesson).
It should have been edit_course_lesson_path(#course, lesson).
Thanks for everyone's patience, and many thanks to Deepesh for finally getting it into my head to look at the edit link.

Rails how to hide form after save?

this is the case:
models/product.rb
belongs_to :brand
models/brand.rb
has_many :products
controllers/products_controller.rb
class ProductsController < ApplicationController
def new
#product = Product.new
#brands = Brand.all
end
def create
#product = Product.new(params[:product])
if #product.save
redirect_to :show
else
render :new, format: :html
end
end
end
On product create the user can add a brand name and if the user add a brand name on next time to create a product the form for the brand did not show again.
Someone please have a idea how to do something like that on rails?
That's something to add in your view.
Use a condition around your brand form :
form #product do |f|
f.text_field :name
if not #product.new_record?
f.select_field :brand_id, #brands, :id
end
end

Ruby on rails - undefined method in show.html.erb

I am trying to display the name of the faculty instead of just the ID.
Everything else works except that part where I try to display the name of the faculty. I get an error saying "undefined method" .
I know I am doing something wrong, but I cannot figure it out at all even though I have been looking at this for hours. I am a completely beginner and I would really appreciate your help.
Thank you.
show.html.erb
<p id="notice"><%= notice %></p>
<!-- notice is a ruby method, and its results comes here inside the tags
used when you want the errow page to show on the next page -->
<p>
<strong>Name:</strong>
<%= #student.name %>
</p>
<p>
<strong>Faculty:</strong>
<%= #student.faculty_id %>
<%= #name.faculty_id %>
</p>
<strong>Grade:</strong>
<%= #student.grade%>
</p>
<%= link_to 'Edit', edit_student_path(#student) %> |
<%= link_to 'Back', students_path %>
student.rb
class Student < ActiveRecord::Base
belongs_to :faculty
end
class Name < ActiveRecord::Base
belongs_to :faculty
end
faculty.rb
class Faculty < ActiveRecord::Base
has_many :student
# belongs_to :faculty
has_many :name
end
This is my students_controler.rb
class StudentsController < ApplicationController
before_action :set_student, only: [:show, :edit, :update, :destroy]
# GET /students
# GET /students.json
def index
#students = Student.all
end
# GET /students/1
# GET /students/1.json
def show
end
# GET /students/new
def new
#student = Student.new
end
# GET /students/1/edit
def edit
end
# POST /students
# POST /students.json
def create
#student = Student.new(student_params)
respond_to do |format|
if #student.save
format.html { redirect_to #student, notice: 'Student was successfully created.' }
format.json { render :show, status: :created, location: #student }
else
format.html { render :new }
format.json { render json: #student.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /students/1
# PATCH/PUT /students/1.json
def update
respond_to do |format|
if #student.update(student_params)
format.html { redirect_to #student, notice: 'Student was successfully updated.' }
format.json { render :show, status: :ok, location: #student }
else
format.html { render :edit }
format.json { render json: #student.errors, status: :unprocessable_entity }
end
end
end
# DELETE /students/1
# DELETE /students/1.json
def destroy
#student.destroy
respond_to do |format|
format.html { redirect_to students_url, notice: 'Student was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_student
#student = Student.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def student_params
params.require(:student).permit(:name, :faculty_id)
end
end
The error is undefined method `faculty_id' for nil:NilClass
I am trying to display the name of the faculty instead of just the ID
Since you're a beginner, let me explain it for you...
--
You're currently calling #student.faculty_id
This is the foreign_key of the #student object -- the identifier which links this student object to the appropriate faculty object.
In short, it means that this attribute is a part of the student schema -- you want one which is part of the faculty schema. Thus, you either need to use delegate to call the name attribute from faculty, or just call it directly:
#student.faculty.name
There are deeper problems with your model associations.
The above is how they should be set up:
#app/models/student.rb
class Student < ActiveRecord::Base
belongs_to :faculty
end
#app/models/faculty.rb
class Faculty < ActiveRecord::Base
has_many :students
end
The above will allow you to call the following:
#app/controllers/students_controller.rb
class StudentsController < ApplicationController
def show
#student = Student.find params[:id]
end
end
#app/views/students/view.html.erb
<%= #student.faculty.name %>
You must remember that Rails works on top of a relational database. This works by allowing you to call related objects by virtue of their foreign key.
I can explain more if required.
<%= #name.faculty_id %> won't work.
In your controller eager load the faculty
def show
#student = Student.includes(:faculty)
end
Either do
<% if #student.faculty.present? %>
<%= #student.faculty.name %>
<% end %>
or you could get the faculty in the controller and assign it to a variable
def show
#student = Student.includes(:faculty)
#faculty = #student.faculty
end
Then you can use that
<% if #faculty.present? %>
<%= #faculty.name %>
<% end %>
Error which I see is
class Faculty < ActiveRecord::Base
has_many :student
# belongs_to :faculty
has_many :name
end
Should be
class Faculty < ActiveRecord::Base
has_many :students
# belongs_to :faculty
has_many :names
end
I don't know your error due to this is or not but has_many is not with singular form

Ruby on Rails Saving in two tables from one form

I have two models Hotel and Address.
Relationships are:
class Hotel
belongs_to :user
has_one :address
accepts_nested_attributes_for :address
and
class Address
belongs_to :hotel
And I need to save in hotels table and in addresses table from one form.
The input form is simple:
<%= form_for(#hotel) do |f| %>
<%= f.text_field :title %>
......other hotel fields......
<%= f.fields_for :address do |o| %>
<%= o.text_field :country %>
......other address fields......
<% end %>
<% end %>
Hotels controller:
class HotelsController < ApplicationController
def new
#hotel = Hotel.new
end
def create
#hotel = current_user.hotels.build(hotel_params)
address = #hotel.address.build
if #hotel.save
flash[:success] = "Hotel created!"
redirect_to #hotel
else
render 'new'
end
end
But this code doesn't work.
ADD 1
Hotel_params:
private
def hotel_params
params.require(:hotel).permit(:title, :stars, :room, :price)
end
ADD 2
The main problem is I don't know how to render form properly. This ^^^ form doesn't even include adress fields (country, city etc.). But if in the line
<%= f.fields_for :address do |o| %>
I change :address to :hotel, I get address fields in the form, but of course nothing saves in :address table in this case. I don't understand the principle of saving in 2 tables from 1 form, I'm VERY sorry, I'm new to Rails...
You are using wrong method for appending your child with the parent.And also it is has_one relation,so you should use build_model not model.build.Your new and create methods should be like this
class HotelsController < ApplicationController
def new
#hotel = Hotel.new
#hotel.build_address #here
end
def create
#hotel = current_user.hotels.build(hotel_params)
if #hotel.save
flash[:success] = "Hotel created!"
redirect_to #hotel
else
render 'new'
end
end
Update
Your hotel_params method should look like this
def hotel_params
params.require(:hotel).permit(:title, :stars, :room, :price,address_attributes: [:country,:state,:city,:street])
end
You should not build address again
class HotelsController < ApplicationController
def new
#hotel = Hotel.new
end
def create
#hotel = current_user.hotels.build(hotel_params)
# address = #hotel.address.build
# the previous line should not be used
if #hotel.save
flash[:success] = "Hotel created!"
redirect_to #hotel
else
render 'new'
end
end
Bottom line here is you need to use the f.fields_for method correctly.
--
Controller
There are several things you need to do to get the method to work. Firstly, you need to build the associated object, then you need to be able to pass the data in the right way to your model:
#app/models/hotel.rb
Class Hotel < ActiveRecord::Base
has_one :address
accepts_nested_attributes_for :address
end
#app/controllers/hotels_controller.rb
Class HotelsController < ApplicationController
def new
#hotel = Hotel.new
#hotel.build_address #-> build_singular for singular assoc. plural.build for plural
end
def create
#hotel = Hotel.new(hotel_params)
#hotel.save
end
private
def hotel_params
params.require(:hotel).permit(:title, :stars, :room, :price, address_attributes: [:each, :address, :attribute])
end
end
This should work for you.
--
Form
Some tips for your form - if you're loading the form & not seeing the f.fields_for block showing, it basically means you've not set your ActiveRecord Model correctly (in the new action)
What I've written above (which is very similar to that written by Pavan) should get it working for you

Trying to add lists to my collections (form_for, routing)

Hey guys I am trying to be able to add lists to my collections. I am new to rails any help would be appreciated. Currently I am trying to build a form to make a new list, but the new/create actions seem to be messed up.
The lists will end up living in the collections show view later via ajax.
Ultimately the goal is for each user to own multiple collections and in each collection there will be multiple lists, in each lists multiple items.
Collections
class CollectionsController < ApplicationController
def index
#user = User.find(current_user)
#collection = Collection.where(:user_id => current_user.id)
end
def new
#collection = Collection.new
end
def show
#collection = Collection.find(params[:id])
#list = List.all
end
def create
#collection = Collection.new(collection_params)
#collection.user_id = current_user.id
# render :text => CGI.escapeHTML(#collection.inspect)
if #collection.save
redirect_to root_path(#user)
else
render 'new'
end
end
def edit
#collection = Collection.find(params[:id])
end
def update
if #collection.update(collection_params)
redirect_to root_path(#user)
else
render 'edit'
end
end
def destroy
#collection.destroy
redirect_to root_path(#user)
end
private
def collection_params
params.require(:collection).permit(:alias, :notes, :visibility)
end
def find_collection
#collection = #user.collection.find(params[:id])
end
end
Lists
class ListsController < ApplicationController
def index
#list = List.all
end
def new
#list = List.new
end
def create
#collection = Collection.find(params[:collection_id])
#list =
#collection.lists.create(comments_params)
if #collection.lists.save
redirect_to root_path(#user)
else
render 'new'
end
end
end
Users
class UsersController < ApplicationController
def index
#user = User.find(current_user)
#collection = Collection.where(:user_id => current_user.id)
#user.collection = Collection.where(:user_id => current_user.id)
# render :text => CGI.escapeHTML(#collection.inspect)
end
end
The link I was trying
<%= link_to '<i class="fa fa-plus-square"></i> Add Subcategory'.html_safe, new_collection_list_path(#collection.id) %>
Current routes
devise_scope :user do
authenticated :user do
root 'collections#index', as: :authenticated
resources :collections do
resources :lists
end
end
Failed form_for
<%= form_for([#collection, #collection.lists.build]) do |f| %>
<% end %>
Models
Users has_many :collections
Collections belong_to :user
has_many :lists
Lists belong_to :collection
Change your ListsController new and create actions to the following:
def new
#collection = Collection.find(params[:collection_id])
end
def create
#collection = Collection.find(params[:collection_id])
#list = #collection.lists.build(params[:list])
if #list.save
redirect_to root_path(#user)
else
render 'new'
end
end

Resources