I have three models, User, Movie, and Review. Here is the relation:
# User.rb
has_many :movies
has_many :reviews
# Movie.rb
belongs_to :user
has_many :reviews
# Review.rb
belongs_to :movies
belongs_to :users
Here is the routes:
# routes.rb
resources :movies do
resources :reviews
end
Here is the controller:
# reviews_controller.rb
class ReviewsController < ApplicationController
before_action authenticate_user!
before_action :find_movie
before_action :find_review, only: [:edit, :update, :destroy]
def new
#review = Review.new
end
def create
#review = Review.new(review_params)
if #review.save
redirect_to movie_path(#movie)
else
render 'new'
end
end
def edit
end
def update
if #review.update(review_params)
redirect_to movie_path(#movie)
end
end
private
def find_movie
#movie = Movie.find(params[:movie_id])
end
def find_review
#review = Review.find(params[:id])
end
def review_params
params.require(:review).permit(:rating, :comment)
end
end
I created a new and partial form and then in the show page of the movie, I create this line of code to show the button of creating new review for a particular movie:
# views/movies/show.html.erb
<%= link_to 'Give review', new_movie_review_path(#movie) %>
I don't want the user to create another review after they submit a review for the same movie. That's why I want to hide the "Give review" button if the user is already gave the feedback. How do I do that?
Something like:
<% unless current_user.reviews.select{|review| review.movie_id == #movie.id}.count > 0 %>
<%= link_to 'Give review', new_movie_review_path(#movie) %>
<% end %>
Could also use where instead:
Review.where(user_id: current_user.id, movie_id: #movie.id).count > 0
You should add a custom validation in the review model which checks for a preexisting review from the same user for the same movie.
If you have the current_user available to views then you can have something like the following to hide Give Review link:
# views/movies/show.html.erb
<%= link_to 'Give review', new_movie_review_path(#movie) unless current_user.movies.where(id: #movie.id).first.comments.any? %>
Related
I would like to route to the parent (journal) show page after deleting the child (habit) using the child_id.
In my app, a User can create journals, which then have multiple habits. I would like to be able to delete (and edit) a habit and then return to the journal show page, which displays all of the habits.
Getting the following and similar errors:
ActiveRecord::RecordNotFound in HabitsController#destroy
journal.rb
class Journal < ApplicationRecord
has_many :entries
has_many :habits
belongs_to :user
end
habit.rb
class Habit < ApplicationRecord
belongs_to :journal
has_many :completed_dates
end
show.html.erb
<h3>Habits</h3>
<% #journal.habits.each do |habit| %>
<div iv class="habit-list">
<div class="habit-name"><%= habit.name %></div>
<div class="habit-status">
<%= simple_form_for [#journal, #habit] do |f| %>
<%= f.input :status, label: false, :inline_label => true %>
<% end %>
</div>
<div>
<%= link_to habit_path(habit), method: :delete do %>
<i class="fas fa-trash btn-edit"></i>
<% end %>
</div>
</div>
<% end %>
habits_controller.rb
class HabitsController < ApplicationController
before_action :set_journal, only: [:new, :create, :edit, :update, :destroy]
before_action :set_habit, only: [:show, :edit, :update, :destroy]
def index
#habits = Habit.all.sort_by &:name
end
def new
#habit = Habit.new
end
def show
end
def create
#habit = #journal.habits.new(habit_params)
if #habit.save
redirect_to journal_path(#journal)
else
render :new
end
end
def edit
end
def update
if #journal.habits.update(habit_params)
redirect_to journals_path(#journal)
else
render :edit
end
end
def destroy
#habit.destroy
redirect_to journal_path(#habit, #journal)
end
private
def habit_params
params.require(:habit).permit(:name, :status, :user_id, :journal_id)
end
def set_journal
#journal = Journal.find(params[:journal_id])
end
def set_habit
#habit = Habit.find(params[:id])
end
end
Your trying to create URL to a deleted resource (#habit).
Change
def destroy
#habit.destroy
redirect_to journal_path(#habit, #journal)
end
to
def destroy
#habit.destroy
redirect_to journal_path(#journal)
end
I am new to Ruby on Rails.I am facing a problem using nested resources.
I am building a learning app where there are courses and lessons.
Every course will have many lessons and a lesson belongs to only one course.
I am unable to create a lesson for a course currently.
Example : http://localhost:3000/courses/19/lessons/new is a page where i want to create and display lessons for course 19.
Routes.rb
Rails.application.routes.draw do
devise_for :users
resources :courses
resources :courses do
resources :lessons
end
resources :lessons
root 'pages#landing'
get 'pages/home' => 'pages#home' ,as: :home
get '/user/:id' => 'pages#profile',as: :profile
get '/users' => 'courses#index',as: :user_root
end
Course.rb
class Course < ActiveRecord::Base
belongs_to :user
has_many :lesson
validates :user_id , presence: true
end
Lesson.rb
class Lesson < ActiveRecord::Base
belongs_to :course
validates :course_id , presence: true
end
CourseController.rb
class CoursesController < ApplicationController
def index
#courses = Course.all;
end
def new
#course = Course.new;
end
def create
#course = Course.new(course_params);
#course.user_id = current_user.id;
if #course.save
redirect_to course_path(#course)
else
flash[:notice]="Course could not be created ! "
redirect_to new_course_path
end
end
def edit
end
def update
end
def destroy
#course = Course.find(params[:id]);
#course.destroy;
end
def show
#course = Course.find(params[:id]);
end
private
def course_params
params.require(:course).permit(:title, :description, :user_id)
end
end
LessonController.rb
class LessonsController < ApplicationController
def index
#lessons = Lesson.all;
end
def new
#lesson = Lesson.new;
end
def create
#lesson = Lesson.new(lesson_params);
#course = Course.find_by(id: [params[:course_id]]);
if #lesson.save
redirect_to new_course_lesson_path , flash[:notice] = "Lesson successfully saved !"
else
redirect_to new_course_lesson_path , flash[:notice] = "Lesson cannot be created ! "
end
end
def show
#lesson = Lesson.find(params[:id])
end
private
def lesson_params
params.require(:lesson).permit(:title,:description,:video,:course_id)
end
end
Lessonform.html.erb
<%= form_for ([#course,#lesson]) do |f| %>
<%= f.label :lesson_Title %>
<%= f.text_field :title ,placeholder: "Enter the lesson Title" ,:class=>"form-control" %><br />
<%= f.label :Description %>
<%= f.text_area :description ,placeholder: "Enter the lesson Description",rows:"8",:class=>"form-control" %><br />
<center>
<%= f.submit "Create lesson",:class =>"btn btn-lg btn-primary" %>
</center>
<% end %>
One problem i see is that you have defined route resources :lessons twice. Once, inside courses scope and second time outside.
The error seems to occur because in your view #course is nil. So, please check you set #course in a before_action inside lessons_controller#new action.
EDIT
class LessonsController < ApplicationController
before_action :set_course, only: [:new, :create]
def new
#lesson = #course.lessons.build
end
private
def set_course
#course = Course.find_by(id: params[:course_id])
end
end
Also replace has_many :lesson with has_many :lessons inside Course model.
First change you need to make in your Course model as you have singular lesson when defining many association:
has_many :lessons
Also let me know if their are any chances of lessons page being called without courses? If no then please remove:
resources :lessons
I guess also the two defining of courses in routes in creating issue. Please try removing the:
resources :courses
Let me know if you still face any issue.
I am using Devise as my authentication system and simple form. I get a NoMethodError in Groups#show and an undefined method 'name' for nil:NilClass error. I use model associations to tie the groups and posts together. When I do puts post.user.name it correctly displays in my terminal but that line causes the above error and it's referencing Groups#show for some reason. Any thoughts?
Routes
resources :groups do
resources :posts
end
Group Model
class Group < ActiveRecord::Base
validates :user_id, presence: true
belongs_to :user
has_many :posts
has_many :comments
has_many :attachments
end
Post Model
class Post < ActiveRecord::Base
validates :user_id, presence: true
validates :caption, presence: true
belongs_to :user
belongs_to :group
has_many :comments, dependent: :destroy
end
Group Controller
class GroupsController < ApplicationController
before_action :authenticate_user!
def new
#group = current_user.groups.build
end
def create
#group = current_user.groups.build(group_params)
#group.user_id = current_user.id
if #group.save
redirect_to groups_path
else
render :new
end
end
...
private
def group_params
params.require(:group).permit(:group_name, :description, :user_id)
end
end
Posts Controller
class PostsController < ApplicationController
before_action :set_post, only: [:show, :edit, :update, :destroy]
before_action :owned_post, only: [:edit, :update, :destroy]
before_action :authenticate_user!
def index
#posts = Post.paginate(page: params[:page], per_page: 3).order('created_at DESC')
#post = current_user.posts.build
#attachments = Attachment.all
end
...
def new
#post = current_user.posts.build
end
def create
#post = current_user.posts.build(post_params)
#group = Group.find(params[:group_id])
#post.group_id = #group.id
if #post.save
redirect_to groups_path
else
render :new
end
end
...
private
def post_params
params.require(:post).permit(:caption, :user_id)
end
def set_post
#post = Post.find(params[:id])
end
def owned_post
unless current_user == #post.user
redirect_to root_path
end
end
end
groups/show.html.erb
<%= render "posts/index" %>
...
posts/_index.html.erb
<%= render 'posts/form' %>
<%= render 'posts/posts' %>
...
posts/_form.html.erb
<%= simple_form_for([#group, #group.posts.build]) do |f| %>
...
posts/_posts.html.erb
<% #group.posts.each do |post| %>
<%= puts post.user.name %> ISSUE
<%#<%= render 'posts/post', post: post %>
<% end %>
SOLUTION: After asking on Reddit Rails, a generous user offered a solution that works. Apparently the <%= simple_form_for([#group, #group.posts.build]) do |f| %> creates a new post and adds it to the groups.posts array and so this causes issues when it iterates over _posts.html.erb and there is no user. More information can be found here: https://www.reddit.com/r/rails/comments/4lygix/unidentified_method_for_nil_class_devise_and/
But replace the above simple_form line of code with <%= simple_form_for([#group, Post.new(group: #group)]) do |f| %> seemed to do the trick, as suggested by the generous user in the reddit link above.
I have a model pitch where i am fetching grounddetail_id. I want to show all the pitch available in the ground. How i can book pitch of ground..
grounddetails_controller.rb
class GrounddetailsController < ApplicationController
before_action :find_ground, only: [:show, :edit, :destroy, :update]
def index
#grounddetails = Grounddetail.all.order('created_at DESC')
end
def new
#grounddetail = Grounddetail.new
end
def edit
end
def show
end
def create
#grounddetail = Grounddetail.new(ground_params)
if #grounddetail.save
redirect_to #grounddetail
else
render 'new'
end
end
def update
if #grounddetail.update(ground_params)
redirect_to #grounddetail
else
render 'edit'
end
end
def destroy
#grounddetail.destroy
redirect_to root_path
end
private
def find_ground
#grounddetail = Grounddetail.find(params[:id])
end
def ground_params
params.require(:grounddetail).permit(:name, :working_hours, :end_time, :address, :contact_no, :email, :number_of_grounds, :description, :featured_ground)
end
end
routes.rb
Rails.application.routes.draw do
devise_for :users
devise_for :admins
resources :grounddetails do
resources :pitches
end
root "grounddetails#index"
end
model
grounddetail.rb
class Grounddetail < ActiveRecord::Base
has_many :pitches, dependent: :destroy
end
pitch.rb
class Pitch < ActiveRecord::Base
belongs_to :grounddetail
end
for now i just have pitch model and routes but in controller i am confused what to use. i can i book pitch of the ground. But for single ground i am able to book.
In the grounddetails#show action
def show
#pitches = #grounddetail.pitches
end
Then in the groundetails/show.html.erb, you can just use <%= #pitches %> to display the pitches of that grounddetail.
Update:
#in the show.html.erb
<% #pitches.each do |p| %>
<%= form_tag book_pitch_path(p) do %>
#your attributes here
<%= submit_tag "Book this Pitch" %>
<% end %>
<% end %>
#routes.rb
post :book_pitch/:id, to: 'pitches/book_pitch', as: 'book_pitch'
#in pitches_controller.rb
def book_pitch
#your actions here
end
I am trying to set up a 5 star rating system so users can rate other users. At the moment everything is working, (create, delete, update etc...) but only the logged in user can rate himself. I cannot rate other users. I get no errors, it just redirects to the user profile page as it should but without added a rating to that user.
user.rb
class User < ActiveRecord::Base
has_many :reviews
review.rb
class Review < ActiveRecord::Base
belongs_to :user
end
reviews_controller.rb
class ReviewsController < ApplicationController
before_action :find_user
before_action :find_review, only: [:edit, :update, :destroy]
def new
#review = Review.new
end
def create
#review = Review.new(review_params)
#review.user_id = current_user.id
if #review.save
redirect_to user_path(#user)
else
render 'new'
end
end
def edit
end
def update
if #review.update(review_params)
redirect_to user_path(#user)
else
render 'edit'
end
end
def destroy
#review.destroy
redirect_to user_path(#user)
end
private
def review_params
params.require(:review).permit(:rating, :comment)
end
def find_user
#user = User.find(params[:user_id])
end
def find_review
#review = Review.find(params[:id])
end
end
_form which then gets rendered on show page:
<%= simple_form_for([#user, #user.reviews.build]) do |f| %>
<div id="rating-form">
<label>Rating</label>
</div>
<%= f.input :comment %>
<%= f.button :submit %>
<% end %>
<script>
$('#rating-form').raty({
path: '/assets/',
scoreName: 'review[rating]'
});
</script>
Any help getting this to work would be greatly appreciated!!
Do this:
#config/routes.rb
resources :users do
resources :reviews, only: [:new, :create]
end
#app/models/review.rb
class Review < ActiveRecord::Base
belongs_to :user
belongs_to :reviewed, class_name: "User", foreign_key: :reviewed_id
end
#app/controllers/reviews_controller.rb
class ReviewsController < ApplicationController
def new
#review = current_user.reviews.new
end
def create
#review = current_user.reviews.new review_params
#review.save
end
private
def review_params
params.require(:review).permit(:rating, :comment).merge(reviewed_id: params[:user_id])
end
end
#app/views/reviews/new.html.erb
<%= form_for #review do |f| %>
<%= f.number_field :rating %>
<%= f.text_field :comment %>
<%= f.submit %>
<% end %>
This would mean you'll have to include a reviewed_id column in your reviews table.
You'll be able to access it using: url.com/users/:user_id/reviews/new
The application will automatically fill the user_id and reviewed_id fields, so the rest of your code should work with the upgrade.
The big problem you have is that you're basically recording the user_id (presumably of who created the review)... but have no way of stipulating who the review is about.
The above code fixes that for you.