My two models are ProductType and Product. ProductType has_many Products. ProductType.destroy should fail and set an error if products.count > 0. Before I re-invent the wheel, there must be some rails way of approaching this problem. Any thoughts? Thanks!
on 2007-02-07 13:30
on 2007-02-07 14:02
Try something like this in your model: def before_destroy raise "Type as associated products" if products.count > 0 end Steve
on 2007-02-07 15:11
photo matt wrote: > My two models are ProductType and Product. > ProductType has_many Products. > > ProductType.destroy should fail and set an error > if products.count > 0. > > Before I re-invent the wheel, there must be some > rails way of approaching this problem. Any thoughts? If you've (1) included belongs_to ProductType in your Products model, and (2) included ProductType_id as a foreign key in your Products table, then what you're looking for is the default behavior. hth, Bill
on 2007-02-08 02:10
Bill and Steve, Thank you for your responses. Steve--your suggestion simply raises an exception, which is not the most elegant solution. I'd rather use an error that can actually be returned. Bill -- you may be right, but my application is not working like that. First, maybe I need a little lesson on naming things.... My ProductType model pulls from a database table called product_types. My Product model pulls from a table named products, and has the following association: belongs_to :product_type The products table also has a column named product_type_id. Is my problem that I've used underscores in my table names? I thought that was the proper table naming convention for names that have two words. Thanks, Matt
on 2007-02-08 04:53
Hi Matt, photo matt wrote: First, thank you for signing your post. Maybe I'm just getting old but I like to know with whom I'm conversing. > my application is not working like that. Then it's broke. But then you already knew that ;-) I'll apologize up front for the shortness of my response. I don't have much time at the moment and hope you won't be offended. Here's what I'd do if I had 20 minutes and your code. It's what I _always_ do when I'm going to try something new or having a problem. Do NOT use any of the Rails magic you don't absolutely have to except for scaffolding. The scaffold code works. Always. Unless you've screwed up something it depends on. And that's what you need to find out. If you're using migrations, don't for this. Because that might be where you're problem is. Use a script. Same principle applies across the board. Focus on what you're trying to discover and don't include ANYTHING that might obscure that. 1) Set up a database named 'sandbox' (if you don't already have one) 2) Create the tables you need to investigate the thing you're interested in. In your case you need two tables. Use the same convention you're already using. Other than pluralization, Rails doesn't give a damn whether you use underscores / CamelCase or not. Use the scaffolding to learn. The product_types table should only have two fields: id and a text field named whatever you've got it named in the app you're having a problem with. The products table should only have three fields: id, product_type_id, and a text field. 3) Scaffold your sandbox app on both the product and product_type models. 4) Add the relations to your models. 5) Add a product type 6) Add a product 7) Try to delete the product type. You should get an error. If you don't, the problem's in your setup / config. If you do, the problem's in your (other) code. I'm sorry I can't offer more right now. I'll be back in 8-10 hours. Let us know how it goes. Best regards, Bill
on 2007-02-09 01:28
Bill, Thank you for your suggestions. I managed to reproduce the same behavior in a sandbox. I've also searched the documentation and I can't seem to find where it says that this is the default behavior. This definitely can be enforced at the database level, and maybe it should be done there? Thanks, Matt
on 2007-02-09 03:45
Hi Matt, photo matt wrote: > I managed to reproduce the same behavior in > a sandbox. Excellent! Please post your sandbox code. Including your SQL script. > I've also searched the documentation and I can't > seem to find where it says that this is the default > behavior. Not surprising. It's an implied understanding about the way databases work You may well have discovered and reported a bug! Good on ya, bud! And even if it's not a bug, we'll all learn something. Best regards, Bill
on 2007-02-09 04:28
Well, after a bit more research, I'm pretty convinced that this just isn't the way rails works. According to Agile Web Development with Rails, "Notice that this table has two foreign keys. Each row in the line_items table is associated both with an order and with a product. Unfortunately, Rails migrations donâ€™t provide a database-independent way to specify these foreign key constraints, so we had to resort to executing native DDL statements (in this case, those of MySQL)." Furthermore, the has_many relationship has a :dependent attribute that can be set to :destroy_all, :delete_all, or :nullify. It seems to me that it should have a :delete_restrict option, but it doesn't. If you are using InnoDB tables on MySQL, you can use database level foreign key constraints that will protect the dependent rows from being orphaned. I'll do that, and I am reasonably confident it will work. But it seems to me that Rails should have some method of preventing rows from being orphaned. http://dev.mysql.com/doc/refman/5.0/en/innodb-fore... Thanks, Matt Bill Walton wrote: > Hi Matt, > > photo matt wrote: > >> I managed to reproduce the same behavior in >> a sandbox. > > Excellent! Please post your sandbox code. Including your SQL script > >> I've also searched the documentation and I can't >> seem to find where it says that this is the default >> behavior. > > Not surprising. It's an implied understanding about the way databases > work > You may well have discovered and reported a bug! > > Good on ya, bud! And even if it's not a bug, we'll all learn something. > > Best regards, > Bill
on 2007-02-09 04:59
photo matt wrote: > Unfortunately, Rails migrations don't provide a > database-independent way to specify these foreign key > constraints, so we had to resort to executing native > DDL statements (in this case, those of MySQL)." Which is precisely why I said, in my earlier response: > If you're using migrations, don't for this. Because that > might be where you're problem is. Use a script. " Do I contradict myself? Very well, then I contradict myself, I am large, I contain multitudes. " Walt Whitman Congrats on your learning, though. Good post. You've undoubtedly helped someone who will follow your footsteps. I hope you see that as a Good Thing. Best regards, Bill
on 2007-02-09 05:10
> " Do I contradict myself? Very well, then I contradict myself, I am large, > I > contain multitudes. " > Walt Whitman The allusion was to Rails.
on 2007-02-10 14:59
Here is my ruby solution to this problem. In the product type model, implement the before_destroy method. This gives us a chance to cancel the destroy by returning false. def before_destroy if products.count() > 0 return false end end In the product types controller, check the return value of destroy, and if it is false, output an error message. def destroy if !ProductType.find(params[:id]).destroy flash[:notice] = "Cannot delete this product type until there are no products with this product type." end redirect_to :action => 'list' end It seems to me that at least the part in the model should be an option on the :has_many declaration. The part I'm not so sold on, is shouldn't that error message that I'm setting in the controller be set somewhere in the model? But then, how would the View ever get that error message?