Ruby On Rails [Carrierwave]: Cannot delete image - ruby-on-rails

I'm a newbie in rails and trying to implement image uploading to ftp with 'carrierwave-ftp' gem. For image uploading, I have two controllers. First one is 'events_controller' while the second one is 'events_pictures_controller'.
Pictures are getting uploading to ftp. But the problem is that when I'm deleting a single picture, it is destroying the entire event. Please help!
Here is my Events Model:
class Event < ActiveRecord::Base
has_many :event_pictures, dependent: :destroy
accepts_nested_attributes_for :event_pictures, allow_destroy: true
validates_presence_of :name, :date
end
Here is my EventPictures Model:
class EventPicture < ActiveRecord::Base
mount_uploader :picture_title, EventPicturesUploader
validates_presence_of :picture_title
belongs_to :event, dependent: :destroy
end
Events Controller:
def index
#events = Event.all.order('date DESC')
end
def show
#event_pictures = #event.event_pictures.all
end
def new
#event = Event.new
#event_picture = #event.event_pictures.build
end
def edit
end
def create
#event = Event.new(event_params)
respond_to do |format|
if #event.save
params[:event_pictures]['picture_title'].each do |a|
#event_picture = #event.event_pictures.create!(:picture_title => a, :event_id => #event.id)
end
format.html { redirect_to #event, notice: 'Event was successfully created.' }
else
format.html { render :new }
end
end
end
def destroy
#event = Event.find params[:id]
#event.destroy
redirect_to events_url
end
private
def set_event
#event = Event.find(params[:id])
end
def event_params
params.require(:event).permit(:name, :date, event_pictures_attributes: [:id, :event_id, :picture_title])
end
This is the Destroy method in EventPictures Controller
def destroy
#event_picture = EventPicture.find params[:id]
#event_picture.destroy
redirect_to "events_url"
end
Meanwhile in the events.show.html.erb, I have this:
<% #event_pictures.each do |p| %>
<%= link_to image_tag(p.picture_title_url, :class => 'event-img'), image_path(p.picture_title_url) %>
<%= link_to 'Delete', p, method: :delete, data: { confirm: "Are you sure?" } %>
<% end %>

In your EventPicture model you have dependent: :destroy on the association which means that when the picture will deleted the corresponding events too. So just edit the association and make it:
belongs_to :event
And you have dependent destroy on the Event model so when a event will be deleted the corresponding pictures too will get deleted which is correct.
Hope this helps.

I believe your error lies with this line
belongs_to :event, dependent: :destroy
This is telling the EventPicture model to delete its parent model Event when it is deleted.
Replace with
belongs_to :event

Related

Can not create or build nested model in Rails 6

I have issue when create nested model in Rails 6:
post.rb
class Post < ApplicationRecord
has_many :post_votes, dependent: :destroy
end
post_vote.rb
class PostVote < ApplicationRecord
belongs_to :posts
end
routes.rb
resources :posts do
resources :post_votes
end
views:
<%= button_to post_post_votes_path(post), method: :post, remote: true, form_class: "post_vote" do%>
<%= bootstrap_icon "arrow-up-circle", width: 20, height: 20, fill: "#333" %>
<%end%>
PostVost Controller
def create
#post = Post.find(params[:post_id])
#post_vote = PostVote.new
if already_voted?
# flash[:notice] = "You can't vote more than once"
redirect_to root_path
else
#post_vote = #post.post_votes.build(user_id: current_user.id)
end
# redirect_to post_path(#post)
respond_to do |format|
format.html {}
format.js
end
end
def already_voted?
PostVote.where(user_id: current_user.id, post_id: params[:post_id]).exists?
end
I check the log file, no record was update in database
Any one known why i can not create new post_vote model?
Thank you so much!
On this line:
#post_vote = #post.post_votes.build(user_id: current_user.id)
.build only creates the object in memory. It does not persist it to the database.
Try:
#post_vote = #post.post_votes.create(user_id: current_user.id)
or
#post_vote = #post.post_votes.create!(user_id: current_user.id)
if you want an exception to be thrown if persistence fails.
The problem is using belong_to without optional
class PostVote < ApplicationRecord
belongs_to :posts
end
After:
class PostVote < ApplicationRecord
belongs_to :posts, optional: true
end

How do I create a page without a model?

I'm working on an app which has many 'Activities'. Each 'Activity' has many 'Ranks'. I'd like each 'Activity' to have a page called grading, where the user can see a list of all of that activity's ranks and conveniently update them. I imagine the URL would be something like http://localhost:3000/activities/21/grading
I'm already using http://localhost:3000/activities/21/edit for its intended purpose.
I don't need a model for gradings, as I don't need to save any grading records.
I know exactly what to put in the view, I'm just unsure what to add to the controller and routes files. Other people have worked on this app but I'm unable to contact them.
Routes
resources :activities do
collection do
get 'scheduled_classes'
end
end
resources :ranks
end
activities_controller
class ActivitiesController < ApplicationController
def new
#activity = Activity.new
#activity.timeslots.build
#activity.ranks.build
end
def create
#activity = current_club.activities.new(activity_params)
if #activity.save
flash[:success] = "New class created!"
redirect_to activity_path(#activity)
else
render 'new'
end
end
def edit
#activity = current_club.activities.find_by(id: params[:id])
#active_ranks = #activity.ranks.where(active: true)
if !#activity.active?
redirect_to activities_path
else
#activity.timeslots.build
end
end
def update
#activity = current_club.activities.find_by(id: params[:id])
if #activity.update_attributes(activity_params)
flash[:success] = "Class updated!"
redirect_to edit_activity_path(#activity)
else
render 'edit'
end
end
def show
#activity = current_club.activities.find_by(id: params[:id])
#active_ranks = #activity.ranks.where(active: true)
if #activity.nil?
redirect_to root_url
elsif !#activity.active?
redirect_to activities_path
end
end
def index
#activities = current_club.activities.all
end
def destroy
#activity = current_club.activities.find_by(id: params[:id])
if #activity.nil?
redirect_to root_url
else
#activity.destroy
flash[:success] = "Class deleted"
redirect_to activities_path
end
end
end
private
def activity_params
params.require(:activity).permit(:name, :active,
:timeslots_attributes => [:id,
:time_start,
:time_end,
:day,
:active,
:schedule],
:ranks_attributes => [:id,
:name,
:position,
:active])
end
end
activity
class Activity < ApplicationRecord
belongs_to :club
has_many :timeslots, dependent: :destroy
accepts_nested_attributes_for :timeslots,:allow_destroy => true
has_many :ranks, dependent: :destroy
has_many :attendances, dependent: :destroy
accepts_nested_attributes_for :ranks
validates :club_id, presence: true
validates :name, presence: true, length: { maximum: 50 }
end
Your routes don't need to have an associated model or resource.
resources :activities do
collection do
get 'scheduled_classes'
end
member do
get :grading
end
end
will match to activities#grading
See https://guides.rubyonrails.org/routing.html#adding-member-routes for more info.
As you want to add a route on a particular activity, you should add member route on the activity like below,
resources :activities do
collection do
get 'scheduled_classes'
end
get :grading, on: :member
end
Apart from this, you have to add method in ActivitiesController for this route like below,
def grading
#activity = Activity.find_by(id: params[:id])
# do more here
end
In view files, you can create grading.html.erb under activities resources and put your view code there.

How to associate a subscriber to an event

I'm trying to make that a subscriber, sub to an certain event
with the following url per example:
http://localhost:3001/events/1/subscribers/new
but I don't know how to associate event_id when creating a new subscriber
for the moment i'm getting this error:
Couldn't find Event with 'id'=
in the routes:
resources :events do
resources :subscribers #url/events/:events_id/subscribers/new
end
resources :events
root 'events#index'
in the subscribers controller:
def show
end
# GET /subscribers/new
def new
#puts "Look for me in console\n"
#puts params.inspect
#event = Event.find(params[:events_id])
#subscriber = #event.Subscriber.new
end
# GET /subscribers/1/edit
def edit
end
# POST /subscribers
# POST /subscribers.json
def create
#event = Event.find(params[:order_id])
#subscriber = #event.Subscriber.new order_params
##subscriber = Subscriber.new(subscriber_params)
respond_to do |format|
if #subscriber.save
SubsMailer.new_subscriber(#subscriber).deliver
format.html { redirect_to #subscriber, notice: 'Subscriber was successfully created.' }
format.json { render :show, status: :created, location: #subscriber }
else
format.html { render :new }
format.json { render json: #subscriber.errors, status: :unprocessable_entity }
end
end
end
in the new.html.erb:
<h1>New Subscriber</h1>
<%= render 'form', subscriber: #subscriber %>
<%= link_to 'Back', subscribers_path %>
model association:
event.rb:
class Event < ApplicationRecord
has_many :subscribers, dependent: :destroy
end
subscriber.rb:
class Subscriber < ApplicationRecord
belongs_to :event
validates :email, presence: true,
format: /\A\S+#\S+\z/,
uniqueness: { case_sensitive: false }
end
Well, I think this documentation will help you to understand what you need to do.
If briefly at first you need to change your models. You could have many to many for Event -> Subscriber association or one to many. One to many is the simplest to show so you need to add this to your Subscriber model:
belongs_to :event
And this to your Event model:
has_many :subscribers
Add new migration:
def change
remove_column :subscribers, :events_id
remove_column :subscribers, 'Event_id'
add_column :subscribers, :event_id, :integer
end
Then in your controller, you should change method calls, as Subscriber is a class, not the method.
def new
#event = Event.find(params[:event_id])
#subscriber = #event.subscribers.build
end
And you should be sure that in your database you have Event with this id.
To check it you can try to debug your controller code:
def new
puts "Event ids: " + Event.all.map(&:id).inspect
#event = Event.find(params[:event_id])
#subscriber = #event.subscribers.build
end
In your logs you should have something like:
Event ids: [1]
I think you just have a typo in your new method. You call params[:eventS_id] when it should be params[:event_id]. Also you don't properly reference your association. it should be event.subscribers.new:
def new
#puts "Look for me in console\n"
#puts params.inspect
#event = Event.find(params[:event_id])
#subscriber = #event.subscribers.build
end
Migration:
def up
change_table :subscribers do |t|
t.remove :Event_id
t.references :event
end
end
def down
change_table :subscribers do |t|
t.remove :event_id
t.add :Event_id, :integer
end
end
Keep me posted whether this helps and if you have any additional issues

Ruby on Rails : Updating multiple models in a single form

I have 3 models User, House and Order.
Order Model
class Order < ActiveRecord::Base
belongs_to :from_house, :class_name => "House"
belongs_to :to_house, :class_name => "House"
belongs_to :user
accepts_nested_attributes_for :from_house, :to_house, :user
end
My House Model.
class House < ActiveRecord::Base
belongs_to :user
belongs_to :place
belongs_to :city
end
My user model.
class User < ActiveRecord::Base
has_many :orders
has_many :houses
end
In my order form I have something like this
<%= form_for #order do |f| %>
... # order fields
<%= f.fields_for :user do |i| %>
... # your from user forms
<% end %>
<%= f.fields_for :from_house do |i| %>
... # your from house forms
<% end %>
<%= f.fields_for :to_house do |i| %>
... # your to house forms
<% end %>
...
<% end %>
I haven't changed much in controller from the default. The controller code
def create
#order = Order.new(order_params)
respond_to do |format|
if #order.save
format.html { redirect_to #order, notice: 'Order was successfully created.' }
format.json { render action: 'show', status: :created, location: #order }
else
format.html { render action: 'new' }
format.json { render json: #order.errors, status: :unprocessable_entity }
end
end
def order_params
params.require(:order).permit( :shift_date, user_attributes: [:name, :email, :ph_no], from_house_attributes: [:place_id, :floor, :elevator, :size], to_house_attributes: [:place_id, :floor, :elevator])
end
When I submit the form, as expected a Order gets created with a new from_house and to_house along with a new user. But however my user_id in house table remains NULL. How can I make the houses(both from and to) reference the user created after submit.
The User is not logged in, So there is no current_user. We have to create a new user based on the details given. That user has to be associated with the houses (from and to).
I hope I'm clear. If not please let me know.
P.S: This question is an extension to this Ruby on rails: Adding 2 references of a single model to another model
I think this change in app/models/order.rb should do the trick:
class Order < ActiveRecord::Base
belongs_to :user
belongs_to :from_house, class_name: 'House'
belongs_to :to_house, class_name: 'House'
accepts_nested_attributes_for :user, :from_house, :to_house
validates :user, :from_house, :to_house, presence: true
def from_house_attributes=(attributes)
fh = build_from_house(attributes)
fh.user = self.user
end
def to_house_attributes=(attributes)
th = build_to_house(attributes)
th.user = self.user
end
end
Now, try this in your Rails console:
params = { user_attributes: { name: 'New name', email: 'name#example.com' }, from_house_attributes: { name: 'From house name' }, to_house_attributes: { name: 'to house name' } }
o = Order.new(params)
o.save
o.from_house
o.to_house
Cheers!

NameError with a Many to Many association

I'm trying to add a Join/Unjoin button to user created Events, similar to a Follow/Unfollow button for Users.
I'm not sure what to define #rsvps as in the event#show
NameError in Events#show
undefined local variable or method `event' for #<#:0x007f9dfaf9d978>
show.html.erb
<%= link_to "Join Event", rsvps_path(:event_id => event), :method => :post %>
events_controller.rb
def show
#event = Event.find(params[:id])
#user = current_user
##rsvp = ???? something here ????
end
rsvps_controller.rb
class RsvpsController < ApplicationController
before_filter :signed_in_user
def create
#rsvp = current_user.rsvps.build(:event_id => params[:event_id])
if #rsvp.save
flash[:notice] = "Joined event."
redirect_to root_url
else
flash[:error] = "Unable to join event."
redirect_to root_url
end
end
def destroy
#rsvp = current_user.rsvps.find(params[:id])
#rsvp.destroy
flash[:notice] = "Unjoin Event."
redirect_to current_user
end
end
Here are the models
rsvp.rb
class Rsvp < ActiveRecord::Base
attr_accessible :event_id, :user_id
belongs_to :user
belongs_to :event
end
user.rb
has_many :rsvps
has_many :events, through: :rsvps, dependent: :destroy
event.rb
belongs_to :user
has_many :rsvps
has_many :users, through: :rsvps, dependent: :destroy
Your undefined local variable or method error seems to be coming from trying to pass :event_id => event to your controller through rsvp_path. Instead you should just be passing the event object like so
<%= link_to "Join Event", rsvps_path(event), :method => :post %>
the line #event = Event.find(params[:id]) in your controller will take care of figuring out what event you passed to it.
I think this code would be more rails-ish.
# user.rb
has_many :users_events
has_many :events, through: :users_events
# event.rb
has_many :users_events
has_many :users, through: :users_events
# users_event.rb
belongs_to :user
belongs_to :event
ActiveRecord do everything else. 8)
For example user.events and event.users methods.
Join and unjoin to user actions may be processed by events controller. Update method can look like this
# events_controller.rb
def update
respond_to do |format|
#event = Event.find(params[:id])
#event.users << current_user if params[:action] == 'join'
#event.users.delete(current_user) if params[:action] == 'unjoin'
if #event.update_attributes(params[:event])
format.html { redirect_to #event, notice: 'Event was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #event.errors, status: :unprocessable_entity }
end
end
end
A little bit messy, but I hope the idea is clear.

Resources