5

Hi I have a project and each project has tasks. A task belongs to a project. Before I delete a project I want to check if there are related tasks. If there are tasks I don't want to delete the project. If there are no associated tasks, the project should be deleted. Can you please help me with the code? What am I missing?

class Project < ActiveRecord::Base  
  before_destroy :check_tasks    

  def check_tasks 
    if Project.find(params[:id]).tasks  
      flash[:notice] = 'This project has tasks.'
      redirect_to :action => 'list_projects'    
    end 
  end
end
nslocum
  • 5,037
  • 1
  • 27
  • 27
Runy
  • 57
  • 1
  • 7

3 Answers3

6

Return false from the before_destroy method to prevent the instance from being destroyed.

The method should also return a meaningful error for troubleshooting.

class Project < ActiveRecord::Base
  before_destroy :check_tasks

  def check_tasks
    if self.tasks.any?
      errors.add_to_base "Project has tasks and cannot be destroyed."
      return false
    end
  end
end

Note: flash[:notice] and params[:attr_name] can only be used from controllers.

nslocum
  • 5,037
  • 1
  • 27
  • 27
  • 1
    Hey, what is `flash[:notice]` and `params` doing inside a model? :P – rubyprince Nov 17 '11 at 19:27
  • It works but the problem. I do not get any message saying "Product has tasks etc" when I try to delete the product. Where do I need to put the flash message? code: class Product < ActiveRecord::Base before_destroy :check_tasks has_many :tasks, :order => 'created_at DESC' validates :name, :presence => true belongs_to :sprint validates :sprint_id, :presence => true def check_tasks return !self.tasks.any? errors.add_to_base "Product has tasks and cannot be destroyed." end end – Runy Dec 01 '11 at 01:56
  • Your flash message should be set in the Model as shown in my answer above. It should then be displayed in the view. This page has a good example: http://pupeno.com/2009/11/19/ensuring-the-displaying-of-flash-messages-in-ruby-on-rails/ – nslocum Dec 01 '11 at 19:25
2

You have a couple of problems here.

  1. You don't (or shouldn't) have access to the params variable (it's available in controllers and views only, unless you're passing it to the model, which is probably not what you want).
  2. Your if checks against project.tasks which is an array - even an empty array evaluates to true, so your other code branch will never occur no matter if the project has tasks or not.
  3. You should probably be setting error messages for the view from your ProjectsController#destroy action, not in your model.

Solutions:

  1. Change Project.find(params[:id]) to self - you want to check the tasks for every instance of the Project.
  2. Change the check in your if statement from if self.tasks to if self.tasks.any? which returns the value you want (false if the array is empty, true otherwise).
  3. Move the flash[:notice] from your model to your controller, as well as the redirect_to call, where they belong. This means your check_tasks method can be changed to the following:

code:

def check_tasks
  return !self.tasks.any?
end
Brett Bender
  • 19,388
  • 2
  • 38
  • 46
  • Guys, I'm still having trouble with displaying my error messages. My tasks do not get deleted. On this link I have displayed the code of my view and the code of my model. Can you please help me? What should I put in my view to display the error message? Thanks, Runy http://stackoverflow.com/questions/8545887/how-to-display-errors-notice-in-rails – Runy Dec 17 '11 at 15:55
1

Should the check be self instead? (not sure where you getting the params[:id] from).

Haven't checked this out yet though - but since I need something similar for my Users model I'll see how that works out and get back to you.

class Project < ActiveRecord::Base  
 before_destroy :check_tasks

 private

 def check_tasks
   #edited 
   if tasks.empty?  
     false
   end 
 end
Pasted
  • 864
  • 1
  • 12
  • 22
  • 1
    you can't redirect inside a model! – Wukerplank Nov 17 '11 at 17:29
  • the condition should be `if !self.tasks.empty?` or the `check_tasks` method should rather be `def check_tasks; self.tasks.empty?; end`..`self.tasks` will return `[]` even if there are no tasks and `if self.tasks` will always enter the condition as `[]` is also `true` condition (anything other than `false` or `nil` is `true` condition). – rubyprince Nov 17 '11 at 18:05
  • or even `tasks.empty?` will do, as `self` is taken by default. – rubyprince Nov 17 '11 at 19:30
  • I will try the one from Brett and the one from nslocum. However another question. I have many of these child mother relationships in my app, not only between projects and tasks but also between companies and contacts classes or tasks and timesheets. So question: should I put the code in all the projects, company and tasks models? This seems like duplication. How would you make one Class object and then use this class object for all the relationships in my app? In other words can you help me what the code would be if you need to make it generic for all the mother child relationships in my app? – Runy Nov 17 '11 at 22:29
  • :) I'm probably learning more from this question than Runy is! nice comment rubyprince, I'll edit the answer to include your suggestions / corrections, cheers – Pasted Nov 18 '11 at 12:29
  • So can anyone write it down for me what it should be? I'm still crashing some nice apps here because the mothers still get deleted. What would the final code be? Thanks Runy – Runy Dec 01 '11 at 01:27