I decided to change the destroy function on my app to just switch a Status field to false (The index and show actions only show the results with status: true).
Well, I've changed this, and it worked at first, but now it's just "rolling back" before it complete the commit...
The controller destroy :
def destroy
@customer = Customer.find(params[:id])
@contact = @customer.contacts
@adress = @customer.adresses
if @customer.delete(current_user)
@contact.each do |f|
f.delete(current_user)
end
@adress.each do |g|
@telephone = g.telephones
@telephone.each do |t|
t.delete(current_user)
end
g.delete(current_user)
end
flash[:success] = "Cliente excluído"
else
flash[:danger] = "Erro! O cliente não foi excluído."
end
redirect_to customers_path
end
The model delete :
def delete(user_id)
update_attributes(status: false, changed_by: user_id, deleted_at: Time.now, updated_at: Time.now)
end
Besides that I restricted the :destroy method only to Admin (Is that the right method? Why is it not delete?)
Thanks!
Your issue with the rolling back is likely related to a validation failure. You can find out what this is by changing update_attributes
into update_attributes!
This will throw an error when the validation fails and you can see what went wrong.
Apart from that there are a few ways you can improve your code
First off, rather than manually deleting a customer's contacts and address you can add dependent: :destroy to the relation.
This works like so:
class Customer < ActiveRecord::Base
has_many :contacts, dependent: :destroy
has_many :adresses, dependent: :destroy
end
class Adress < ActiveRecord::Base
has_many :telephones, dependent: :destroy
end
When you call .destroy
on a record it will call .destroy
on any relations that have dependent: :destroy on them. .delete
will not do this pass through, that is the major difference between them.
By adding that to the above relations like so you can change your controller to this:
def destroy
@customer = Customer.find(params[:id])
if @customer.destroy
flash[:success] = "Cliente excluído"
else
flash[:danger] = "Erro! O cliente não foi excluído."
end
redirect_to customers_path
end
This will destroy the customer, it's adresses, and the addresses' telephones.
To do a soft-delete like you want you should use the Paranoia gem.
It lets you assign a class with acts_as_paranoid
so when you call .delete
or .destroy
it isn't really removed, it is just hidden and can be recovered later.
It is well integrated with Activerecord so you won't need to do things like Customer.where(status: true)
to get your non-deleted records, it just won't include deleted records by default. Although you can request them if you need.
The gem's Github page does a really good job of explaining how to use it.
By default it won't save the user that performed the deletion, just the time it was performed at. If you want to do that you should create a wrapper that calls delete after assigning the user that performed the deletion.
Something like this should work:
def destroy_by(user)
update_attributes!(changed_by: user)
destroy
end
Then you can change @customer.destroy
in your controller to @customer.destroy_by(current_user)
There is no need to override or restrict delete or destroy.