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.
Related
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
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 cancan and devise, I can update delete and show but I can't create profile.
why I can't create new profile ("ActiveModel::ForbiddenAttributesError")
class Ability
include CanCan::Ability
def initialize(user)
if user.is_a?(Admin)
can :manage, :all
elsif user.is_a?(User)
can :read, Profile do |profile|
profile.try(:user) == user
end
can :update, Profile do |profile|
profile.try(:user) == user
end
can :destroy, Profile do |profile|
profile.try(:user) == user
end
can :create, Profile do |profile|
profile.try(:user) == user
else
can :read, :all
end
end
end
class ProfilesController < ApplicationController
before_action :set_profile, only: [:show, :edit, :update, :destroy]
load_and_authorize_resource
# GET /profiles
# GET /profiles.json
def index
user = User.find(params[:user_id])
#profiles = user.profiles
respond_to do |format|
format.html
format.xml {render :xml => #profiles}
end
end
# GET /profiles/1
# GET /profiles/1.json
def show
user = User.find(params[:user_id])
#profiles = user.profiles.find(params[:id])
respond_to do |format|
format.html
format.xml {render :xml => #profile}
end
end
# GET /profiles/new
def new
user = User.find(params[:user_id])
#profile = user.profiles.build
respond_to do |format|
format.html
format.xml {render :xml => #profile}
end
end
# GET /profiles/1/edit
def edit
user = User.find(params[:user_id])
#profiles = user.profiles.find(params[:id])
end
# POST /profiles
# POST /profiles.json
def create
user = User.find(params[:user_id])
#profile = user.profiles.create(profile_params)
respond_to do |format|
if #profile.save
format.html { redirect_to user_profiles_url, notice: 'Profile was successfully created.' }
format.json { render action: 'show', status: :created, location: #profile }
else
format.html { render action: 'new' }
format.json { render json: #profile.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /profiles/1
# PATCH/PUT /profiles/1.json
def update
user = User.find(params[:user_id])
#profiles = user.profiles.find(params[:id])
respond_to do |format|
if #profile.update(profile_params)
format.html { redirect_to user_profile_url, notice: 'Profile was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #profile.errors, status: :unprocessable_entity }
end
end
end
# DELETE /profiles/1
# DELETE /profiles/1.json
def destroy
user = User.find(params[:user_id])
#profiles = user.profiles.find(params[:id])
#profile.destroy
respond_to do |format|
format.html { redirect_to job_hunters_path }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_profile
#profile = Profile.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def profile_params
params.require(:profile).permit(:full_name, :phone_number, :email, :position, :years_of_experiance, :cover_letter, :resume, :reference)
end
end
If you already have load_and_authorize_resource in your controller code, you need to take another step in sanitizing your inputs via a create_params method call in your controller.
Here's a link to a useful resource
Firstly: your CanCan user ability can be re-written as follows:
can :create, :read, :update, :destroy, Profile, user_id: user.id
Secondly: your admin abilities should be written after your normal user abilities, thus overriding them more successfully:
def initialize(user)
# I prefer to alias CRUD actions to keep my ability files more succint
alias_action :create, :read, :update, :destroy, to: :crud
cannot :manage, :all #Failsafe
can :crud, Profile, user_id: user.id
... #additional abilities for user
if user.admin?
can :manage, :all #Override previous failsafe
Lastly: if your Profile class belongs_to a User, you ought to rewrite it as such. Thus, your profile_params would contain a user_id field.
If you were to follow that (correct) paradigm, your ProfilesController's #create action would look something like:
class ProfilesController < ApplicationController
load_and_authorize_resource
def create
#profile = Profile.new(profile_params)
if #profile.save
...
else
...
end
end
private
def profile_params
params.require(:profile).permit(:user_id, ...)
end
end
would someone be able to help me understand this error. I am trying to create a contact form in rails following the building web apps tutorial. I followed the steps to generate a message scaffold. I then amended my routes. Next it said to put this into the messages controller show action.
if #message.save
flash[:notice] = 'Thanks for Your Message'
format.html { redirect_to root_path }
I have done this and i am getting the following error
ActiveModel::ForbiddenAttributesError in MessagesController#create
ActiveModel::ForbiddenAttributesError
This is my message controller file
class MessagesController < InheritedResources::Base
def show
if #message.save
flash[:notice] = 'Thanks for Your Message'
format.html { redirect_to root_path }
end
end
end
My routes file is as follows
# devise_for :users
resources :products do
resources :orders, only: [:new, :create]
#tells rails needs product id number
end
# get 'pages/payment'
get 'home/about'
get 'messages/new'
get 'seller' => "products#seller"
get 'sales' => "orders#sales"
get 'static_pages/productlanding'
get "content/veg"
get "content/fruit"
get "content/mix"
get 'subscriptions/new'
root 'static_pages#home'
Why are you saving in the show action?
--
Params
The ForbiddenAttributes error stems from the strong_params functionality of Rails.
When saving data, you're meant to pass the params through to your model through a strong_params method. This is typically achieved with the following setup:
#app/controllers/messages_controller.rb
class MessagesController < ApplicationController
def show
#message = Message.find(params[:id])
end
def new
#message = Message.new
end
def create
#message = Message.new(message_params)
#message.save
end
private
def message_params
params.require(:message).permit(:your, :message, :params)
end
end
This is how your controller should really be constructed. Your error, I believe, is caused by your lack of params to pass through to the attributes in your model (hence your call to #save resulting in trying to populate your model with non-data).
Strange. You execute saving method in "show" method of controller which responsible for showing up the content on the separate page.
You should replace as following:
def create
if #message.save
flash[:notice] = 'Thanks for Your Message'
format.html { redirect_to root_path }
end
end
i have managed to sort this with the following! Thanks for all the help
class MessagesController < ApplicationController
before_action :set_message, only: [:show, :edit, :update, :destroy]
# GET /messages
# GET /messages.json
def index
#messages = Message.all
end
# GET /messages/1
# GET /messages/1.json
def show
end
# GET /messages/new
def new
#message = Message.new
end
# GET /messages/1/edit
def edit
end
# POST /messages
# POST /messages.json
def create
#message = Message.new(message_params)
respond_to do |format|
if #message.save
flash.now[:notice] = 'Thank you for your message!'
format.html { redirect_to root_path }
format.json { render :show, status: :created, location: #message }
else
format.html { render :new }
format.json { render json: #message.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /messages/1
# PATCH/PUT /messages/1.json
def update
respond_to do |format|
if #message.update(message_params)
format.html { redirect_to #message, notice: 'Message was successfully updated.' }
format.json { render :show, status: :ok, location: #message }
else
format.html { render :edit }
format.json { render json: #message.errors, status: :unprocessable_entity }
end
end
end
# DELETE /messages/1
# DELETE /messages/1.json
def destroy
#message.destroy
respond_to do |format|
format.html { redirect_to messages_url, notice: 'Message was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_message
#message = Message.find(params[:id])
end
.
def message_params
params.require(:message).permit(:name, :email, :company, :phone, :subject, :body)
end
end
I was facing this same error. The fix was to make the params function name same as the root tag of the post json like below
Post json
{"jobseeker_certificate":{"id":-1,"name":"First Class Medical Certificate","institute":"GACA","attachment":null}}
In Controller i changed jobseeker_aircraft_type_ratings_params to jobseeker_certificate_params
def jobseeker_certificate_params
params.require(:jobseeker_certificate).permit(:aircraft, :total_time, :pilot_in_command,
:co_pilot, :rating_expiry_date, :from, :to, :jobseeker_id, :grade, :institute, :attachment, :name,
:from, :to, :jobseeker_id, :grade, :institute, :attachment, :name, :sector_id, :certificate_type,
:details, :certificate_type, :details)
end
I've got an app where:
1. user is on a page viewing their profile information
2. user presses button to email someone from this page
3. after the email is sent, user is sent back to view their profile information again and a notice flashes to tell them if the email worked or not.
I'm having with no. 3. I'm not sure how to set up a redirect (or something else appropriate) that will send a user to view their profile info again
Controller:
class ProfilesController < ApplicationController
before_action :set_profile, only: [:show, :edit, :update, :destroy, :email]
# GET /profiles
# GET /profiles.json
def index
#profiles = Profile.all
end
# GET /profiles/1
# GET /profiles/1.json
def show
end
# GET /profiles/new
def new
#profile = Profile.new
end
# GET /profiles/1/edit
def edit
#profile = Profile.find_by user_id: current_user.id
end
# POST /profiles
# POST /profiles.json
def create
#profile = Profile.new(profile_params)
respond_to do |format|
if #profile.save
format.html { redirect_to #profile, notice: 'Profile was successfully created.' }
format.json { render :show, status: :created, location: #profile }
else
format.html { render :new }
format.json { render json: #profile.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /profiles/1
# PATCH/PUT /profiles/1.json
def update
respond_to do |format|
if #profile.update(profile_params)
format.html { redirect_to #profile, notice: 'Profile was successfully updated.' }
format.json { render :show, status: :ok, location: #profile }
else
format.html { render :edit }
format.json { render json: #profile.errors, status: :unprocessable_entity }
end
end
end
# DELETE /profiles/1
# DELETE /profiles/1.json
def destroy
#profile.destroy
respond_to do |format|
format.html { redirect_to profiles_url, notice: 'Profile was successfully destroyed.' }
format.json { head :no_content }
end
end
def email_profile
destination = params[:to]
share = Share.profile(#profile, destination)
if destination =~ /#/ && share.deliver
redirect_to #profile, notice: 'email sent'
else
redirect_to #profile, notice: 'email failed'
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_profile
#profile = Profile.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def profile_params
params.require(:profile).permit(:user_id, :first_name, :last_name, :dob, :email, :mobile, :address, :suburb, :postcode, :city, :state, :country)
end
end
Share Mailer:
class Share < ActionMailer::Base
default_url_options[:host] = "localhost:3000"
default from: "from#example.com"
def profile(profile, destination)
#profile = profile
mail(to: destination, subject: "sent you stuff")
end
end
Current error:
ActionController::ActionControllerError in ProfilesController#email_profile
Cannot redirect to nil!
I think it has something to do with the :id parameter not being passed through after the email is sent.. but I'm a newbie so I don't really know what I'm talking about.. appreciate any guidance so I can fix this and also better understand ROR :)
You probably need to find a #profile first. I guess something like Profile.find(params[:profile_id]) is missing.