I'm trying to figure out shallow nesting in Rails but I get RecordNotFound for certain actions. A student has many checklists, a checklist belongs to a student, and I made sure routes.rb was setup correctly and form_with partials were getting both the student and checklist passed in.
I think something in my controller is messed up:
class ChecklistsController < ApplicationController
before_action :get_student
before_action :set_checklist, only: %i[show edit update destroy]
def index
#checklists = #student.checklists
end
def show
#student = #checklist.student
end
def new
#checklist = #student.checklists.build
end
def edit
#student = #checklist.student
end
def create
#checklist = #student.checklists.build(checklist_params)
if #checklist.save
redirect_to #checklist, notice: 'Checklist was successfully created.'
else
render :new, status: :unprocessable_entity
end
end
def update
#student = #checklist.student
if #checklist.update(checklist_params)
redirect_to #checklist, notice: 'Checklist was successfully updated.'
else
render :edit, status: :unprocessable_entity
end
end
def destroy
#student = #checklist.student
#checklist.destroy
redirect_to student_checklists_url(#student), notice: 'Checklist was successfully destroyed.'
end
private
def set_checklist
#checklist = #student.checklists.find(params[:id])
end
def get_student
#student = Student.find(params[:student_id])
end
# Only allow a list of trusted parameters through.
def checklist_params
params.require(:checklist).permit(:title, :date, :setting)
end
end
When using shallow nesting you need to separate the callbacks for the collection actions (new, create, index) which are nested from the member actions (show, edit, update, destroy) which are not:
class ChecklistsController < ApplicationController
before_action :set_student, only: %i[new, index, create]
before_action :set_checklist, only: %i[show edit update destroy]
def index
#checklists = #student.checklists
end
# Defining this empty method is actually optional - Rails will implicitly render `show.html.erb` anyways
def show
end
def new
#checklist = #student.checklists.build
end
def create
#checklist = #student.checklists.build(checklist_params)
if #checklist.save
redirect_to #checklist, notice: 'Checklist was successfully created.'
else
render :new, status: :unprocessable_entity
end
end
def edit
end
def update
if #checklist.update(checklist_params)
redirect_to #checklist, notice: 'Checklist was successfully updated.'
else
render :edit, status: :unprocessable_entity
end
end
def destroy
#checklist.destroy
redirect_to student_checklists_url(#student), notice: 'Checklist was successfully destroyed.'
end
private
def set_student
#student = Student.find(params[:student_id])
end
def set_checklist
# this lookup will never be based off a student record
# since the route is not nested
#checklist = Checklist.eager_load(:student)
.find(params[:id])
#student = #checklist.student
end
# Only allow a list of trusted parameters through.
def checklist_params
params.require(:checklist)
.permit(:title, :date, :setting)
end
end
Related
So in my project Project is parent and Medium is Child.
I want to delete one of the childs asynchronously (ajax / js.erb).
When clicking on this link (turbo_method is supposed to imply the asynchronous part of the request but I'm not sure about how it works.) something goes wrong.
<%= link_to "Delete Medium", project_medium_path(medium.project, medium), data: { turbo_method: :delete, turbo_confirm: 'are you sure ?' } %>
Here are my logs.
My problem is that after deleting my media entity, it also deletes my project entity. The fact that deleting my project entity implies a ForeignKeyViolation error is normal, I haven't setup my model correctly yet. Either way, it shouldn't delete my project entity.
Here are my controllers, models and my views disposition :
Projects controller
class ProjectsController < ApplicationController
before_action :set_project, only: %i[ show edit update destroy ]
# GET /projects
def index
#projects = policy_scope(Project).order(date: :desc)
authorize Project
end
# GET /projects/1
def show
#medium = Medium.new(project: #project)
end
# GET /projects/new
def new
#project = Project.new
# Set active user as project owner
#project.user = current_user
authorize #project
end
# GET /projects/1/edit
def edit
end
# POST /projects
def create
#project = Project.new(project_params)
# Set active user as project owner
#project.user = current_user
authorize #project
respond_to do |format|
if #project.save
format.html { redirect_to project_url(#project), notice: "Project was successfully created." }
else
format.html { render :new, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /projects/1
def update
respond_to do |format|
if #project.update(project_params)
format.html { redirect_to project_url(#project), notice: "Project was successfully updated." }
else
format.html { render :edit, status: :unprocessable_entity }
end
end
end
# DELETE /projects/1
def destroy
#project.destroy
respond_to do |format|
format.html { redirect_to projects_url, notice: "Project was successfully destroyed." }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_project
#project = Project.find(params[:id])
authorize #project
end
# Only allow a list of trusted parameters through.
def project_params
params.require(:project).permit(:title, :slug, :subject, :category, :description, :location, :date, :user_id)
end
end
Media controller
class MediaController < ApplicationController
before_action :set_medium, only: %i[ edit update destroy ]
before_action :set_project, only: %i[ create edit update ]
# GET /media
# def index
# end
# GET /media/1
#def show
#end
# GET /media/new
def new
#medium = Medium.new
authorize #medium
end
# GET /media/1/edit
def edit
end
# POST /media
def create
#medium = #project.media.create(medium_params)
authorize #medium
respond_to do |format|
if #medium.save
format.html { redirect_to project_url(#medium.project), notice: "Medium was successfully created." }
format.js
else
format.html { render :new, status: :unprocessable_entity }
format.js
end
end
end
# PATCH/PUT /media/1
def update
respond_to do |format|
if #medium.update(medium_params)
format.html { redirect_to project_url(#medium.project), notice: "Medium was successfully updated." }
else
format.html { render :edit, status: :unprocessable_entity }
end
end
end
# DELETE /media/1
def destroy
#project = #medium.project
#medium.destroy
respond_to do |format|
format.html { redirect_to #project, notice: "Medium was successfully destroyed." }
format.js
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_project
#project = Project.find(params[:project_id])
authorize #project
end
# Use callbacks to share common setup or constraints between actions.
def set_medium
#medium = Medium.find(params[:id])
authorize #medium
end
# Only allow a list of trusted parameters through.
def medium_params
params.require(:medium).permit(:title, :description, :author, :location, :date, :priority_index, :project_id, :visual)
end
end
Project model
Medium model
Thanks for the help !
I found a solution through this post.
Using status: 303 inside Media#Destroy for the redirect_to method.
My ruby application is throwing an error which has appeared all of a sudden. the error thrown is NoMethodError in JobsDevsController # listing=> undefined method `user_id' for nil:NilClass
The part of my code that throws this error in my controller is
def is_authorised
redirect_to root_path, alert: "You don't have permission..." unless current_user.id == #job.user_id
end
My Controller
class JobsDevsController < ApplicationController
before_action :set_jobs_dev , except: [:index, :new, :create, :show, :edit, :listing]
before_action :authenticate_user!, except: [:show, :listing]
before_action :is_authorised, only: [:listing, :budget, :description, :photo_upload, :location, :update, :show ]
# GET /jobs_devs
def index
#jobs_devs = JobsDev.all
end
# GET /jobs_devs/1
def show
end
# GET /jobs_devs/new
def new
#jobs_dev = current_user.jobs_devs.build
end
# def listing
# #jobs_dev = current_user.jobs_dev
# end
# GET /jobs_devs/1/edit
def edit
end
def budget
end
# POST /jobs_devs
def create
#jobs_dev = current_user.jobs_devs.build(jobs_dev_params)
if #jobs_dev.save!
redirect_to listing_jobs_dev_path(#jobs_dev), notice: 'Jobs dev was successfully created.'
else
render :new
end
end
# PATCH/PUT /jobs_devs/1
# def update
# if #jobs_dev.update(jobs_dev_params)
# redirect_to #jobs_dev, notice: 'Jobs dev was successfully updated.'
# else
# render :edit
# end
# end
def update
respond_to do |format|
if #jobs_dev.update(jobs_dev_params)
format.html { redirect_to #jobs_dev, notice: 'Post was successfully updated.' }
format.json { render :show, status: :ok, location: #jobs_dev }
else
format.html { render :edit }
format.json { render json: #jobs_dev.errors, status: :unprocessable_entity }
end
end
end
# DELETE /jobs_devs/1
def destroy
#jobs_dev.destroy
redirect_to jobs_devs_url, notice: 'Jobs dev was successfully destroyed.'
end
private
# Use callbacks to share common setup or constraints between actions.
def set_jobs_dev
#jobs_dev = JobsDev.find(params[:id])
end
def is_authorised
redirect_to root_path, alert: "You don't have permission..." unless current_user.id == #jobs_dev.user_id
end
# Only allow a trusted parameter "white list" through.
def jobs_dev_params
params.require(:jobs_dev).permit(:job_category, :job_type, :job_title, :job_description, :recurrence,
:budget, images: []
)
end
end
Please can you help with this senario
Make sure you set_job for listing actiton
You may need to add to the listing action directly
#job = current_user.job
or the better way to add it to before action of listing action and take order into consideration
Looks like your is_authorised method is looking for #job, which isn't set in your controller; rather, you assign #jobs_dev.
Update the method to the following:
def is_authorised
redirect_to root_path, alert: "You don't have permission..." unless current_user.id == #jobs_dev.user_id
end
I'm not sure that's sufficient, as you're skipping setting this in your before_action:
before_action :set_jobs_dev , except: [:index, :new, :create, :show, :edit, :listing]
It looks as if you'll need to remove :listing from the except clause there.
Try both of these things and it should work again. Let me know if you've any questions or have any issues with this :)
im trying to have a put a voting system (likes and dislikes) using the Ruby gem "act_as_votable" but i want to use that in my reviews model. keep in mind that the Reviews are nested inside the Post Model in my app. i tried to add that in my routes.rb file but its not working. does anyone know how to add a voting system for a nested model ? here's my code
Routes.rb
Rails.application.routes.draw do
devise_for :users
resources :posts do
resources :reviews , except: [:show,:index]
member do
get "like" , to: "reviews#upvote"
get "dislike" , to: "reviews#downvote"
end
end
get 'pages/help'
get 'pages/blog'
get 'pages/contact'
get 'pages/tour'
resources :posts
root 'posts#index'
end
reviewsController.rb
class ReviewsController < ApplicationController
before_action :set_review, only: [ :edit, :update, :destroy, :upvote,:downvote]
before_action :set_post
before_action :authenticate_user!
respond_to :html
def new
#review = Review.new
respond_with(#review)
end
def edit
#review = Review.find(params[:id])
#post = Post.find(params[:post_id])
end
def create
#review = Review.new(review_params)
#review.user_id = current_user.id
#review.post_id = #post.id
#review.save
redirect_to post_path(#post)
end
def update
#review.update(review_params)
respond_with(#review)
end
def destroy
#review.destroy
respond_with(#review)
end
def upvote
#review.upvote_bu current_user
redirect_to :back
end
def downvote
#review.downvote_bu current_user
redirect_to :back
end
private
def set_review
#review = Review.find(params[:id])
end
def set_post
unless #post = Post.where(id: params[:post_id]).first
redirect_to posts_path, flash: {alert: "Post doesn't exists"}
end
end
def review_params
params.require(:review).permit(:comment)
end
end
PostsController.rb
class PostsController < ApplicationController
before_action :set_post, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user! , only: [:edit,:update,:destroy,:new]
# GET /posts
# GET /posts.json
def index
#posts = Post.all
end
# GET /posts/1
# GET /posts/1.json
def show
#reviews = Review.where(post_id: #post.id)
end
# GET /posts/new
def new
#post = Post.new
end
# GET /posts/1/edit
def edit
#post = Post.find(params[:id])
end
# POST /posts
# POST /posts.json
def create
#post = Post.new(post_params)
respond_to do |format|
if #post.save
format.html { redirect_to #post, notice: 'Post was successfully created.' }
format.json { render :show, status: :created, location: #post }
else
format.html { render :new }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /posts/1
# PATCH/PUT /posts/1.json
def update
respond_to do |format|
if #post.update(post_params)
format.html { redirect_to root_path, notice: 'Post was successfully updated.' }
format.json { render :show, status: :ok, location: #post }
else
format.html { render :edit }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
# DELETE /posts/1
# DELETE /posts/1.json
def destroy
#post.destroy
respond_to do |format|
format.html { redirect_to posts_url, notice: 'Post was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_post
#post = Post.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def post_params
params.require(:post).permit(:title, :description)
end
end
I implemented multiple uploads with Carrierwave and I'm unable to update my post correctly. Here is my controller:
class PostsController < ApplicationController
before_action :find_posts, only: [:show, :edit, :update, :destroy, :upvote, :downvote]
before_action :authenticate_user!, except: [:index, :show, :home]
def home
end
def index
if params[:category].blank?
#posts = Post.all.order("created_at DESC")
else
#category_id = Category.find_by(name: params[:category]).id
#posts = Post.where(category_id: #category_id).order("created_at DESC")
end
end
def show
#inquiries = Inquiry.where(post_id: #post).order("created_at DESC")
#random_post = Post.where.not(id: #post).order("RANDOM()").first
#post_attachments = #post.post_attachments.all
end
def new
#post = current_user.posts.build
#post_attachment = #post.post_attachments.build
end
def create
#post = current_user.posts.build(post_params)
respond_to do |format|
if #post.save
params[:post_attachments]['image'].each do |a|
#post_attachment = #post.post_attachments.create!(:image => a)
end
format.html { redirect_to #post, notice: 'Post was successfully created.' }
else
format.html { render action: 'new' }
end
end
end
def update
if #post.update(post_params)
flash[:notice] = "Post successfully updated!"
redirect_to #post
else
flash[:notice] = "Something went wrong...give it another shot!"
render 'edit'
end
end
def edit
end
def destroy
#post.destroy
respond_to do |format|
format.html { redirect_to posts_url, notice: 'Post was successfully destroyed.' }
format.json { head :no_content }
end
end
def upvote
#post.upvote_by current_user
redirect_to #post
end
def downvote
#post.downvote_by current_user
redirect_to #post
end
private
def find_posts
#post = Post.find(params[:id])
end
def post_params
params.require(:post).permit(:title, :price, :description, :location, :category_name, :contact_number, :image)
end
end
Basically, the post is creating just fine, but when I attempt to update the images, the new images doesn't replace the old ones. I'm not sure how to go about changing my update to have it accept new images. NOTE: Other fields work when I update, just not my images.
What do I change in my controller to make it work? If you need to see my form or anything else, let me know, but I'm quite certain it's in the controller.
Since you said creating a post is working fine. But updating a post is not. Then after looking at your code, it seems as I expected, that your def update is missing some code that you have in your def create specifically the params[:post_attachments]['image'] part.
Just in case you do not know yet, def create is called when you click the submit button in the new-post form, while def update is called when you click the submit button in the edit-post form.
I use devise for authentication, I want to associate user_id to the comment model. It works when I send user_id as a parameter, But I want to set the current logined user's user_id automatically to the each comment. How can I do that?
#model/post.rb
class Post < ActiveRecord::Base
has_many :comments
belongs_to :category
end
#model/comment.rb
class Comment < ActiveRecord::Base
belongs_to :post
end
#model/category.rb
has_many :posts
#controllers/comments_controller
class CommentsController < ApplicationController
before_action :set_comment, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!
# GET /comments
# GET /comments.json
def index
#comments = Comment.all
end
# GET /comments/1
# GET /comments/1.json
def show
end
# GET /comments/new
def new
#comment = Comment.new
end
# GET /comments/1/edit
def edit
end
# POST /comments
# POST /comments.json
def create
#comment = Comment.new(comment_params)
respond_to do |format|
if #comment.save
format.html { redirect_to #comment, notice: 'comment was successfully created.' }
format.json { render :show, status: :created, location: #comment }
else
format.html { render :new }
format.json { render json: #comment.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /comments/1
# PATCH/PUT /comments/1.json
def update
respond_to do |format|
if #comment.update(comment_params)
format.html { redirect_to #comment, notice: 'comment was successfully updated.' }
format.json { render :show, status: :ok, location: #comment }
else
format.html { render :edit }
format.json { render json: #comment.errors, status: :unprocessable_entity }
end
end
end
# DELETE /comments/1
# DELETE /comments/1.json
def destroy
#comment.destroy
respond_to do |format|
format.html { redirect_to comments_url, notice: 'comment was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_comment
#comment = Comment.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def comment_params
params.require(:comment).permit(:rating, :comment, :post_id, :user_id)
end
end
use associations user has_many comments and
comments belongs_to user
def new
#comment = current_user.comments.build
end
def create
#comment = current_user.comments.build(comment_params)
end
Provided you have taken the correct steps to setup Devise:
#comment.user = current_user