devroom.io/content/posts/2007/2007-01-23-rails-nested-resource-scaffold.md

131 lines
3.8 KiB
Markdown
Raw Normal View History

2015-03-26 11:28:08 +00:00
+++
date = "2007-01-23"
title = "Rails: Nested resource scaffold"
tags = ["General", "Everything", "Features"]
slug = "rails-nested-resource-scaffold"
2017-09-11 12:20:15 +00:00
description = "Nested resources with Rails' scaffolds"
2015-03-26 11:28:08 +00:00
+++
In my <a href="http://ariejan.net/2007/01/23/new-in-rails-resource-scaffold-generator/">previous post</a> I told you about the resource scaffold. What you'll be doing a lot is nesting these resources. Ingredients in recipes, comments on posts, options for products. You name it, you nest it!
Since Rails does not automatically nest resources for you, you should do this yourself. This is, with some minor tweaks, really easy to accomplish. In this example I'll create recipes that have multiple ingredients.
I assume you have Rails 1.2.1 installed for this tutorial to work properly.
<!--more-->
First, I create an new rails project named 'cookbook'. I use an SQLite3 database because it's easy to do so. You may use any Rails compatible database for this example.
2024-01-30 08:26:51 +00:00
```bash
$ mkdir cookbook
2015-03-26 11:28:08 +00:00
rails --database sqlite3 cookbook
2024-01-30 08:26:51 +00:00
cd cookbook
```
2015-03-26 11:28:08 +00:00
First I create resource scaffolds for both the Recipe and Ingredient models:
2024-01-30 08:26:51 +00:00
```bash
$ ./script/generate scaffold_resource Recipe title:string instructions:text
./script/generate scaffold_resource Ingredient name:string quantity:string
```
2015-03-26 11:28:08 +00:00
As you can see I did not add a recipe_id to the ingredient model because of the has_many relationship. Add this column to the migration file. You should now be able to migrate your database:
2024-01-30 08:26:51 +00:00
```bash
$ rake db:migrate
```
2015-03-26 11:28:08 +00:00
If you add the recipe_id to the generate script the view for your ingredients will include a field for the recipe_id and that's not what you want.
Next, make the has_many relationship in your models.
app/models/recipe.rb:
2024-01-30 08:26:51 +00:00
```ruby
class Recipe < ActiveRecord::Base
2015-03-26 11:28:08 +00:00
has_many :ingredients
2024-01-30 08:26:51 +00:00
end
```
2015-03-26 11:28:08 +00:00
app/models/ingredient.rb
2024-01-30 08:26:51 +00:00
</pre>```ruby
class Ingredient < ActiveRecord::Base
2015-03-26 11:28:08 +00:00
belongs_to :recipe
2024-01-30 08:26:51 +00:00
end
```
2015-03-26 11:28:08 +00:00
So far, nothing new. Next we check out config/routes.rb:
2024-01-30 08:26:51 +00:00
```ruby
map.resources :ingredients
map.resources :recipes
```
2015-03-26 11:28:08 +00:00
What we want is to map ingredients as a resource to recipes. Replace these two lines with:
2024-01-30 08:26:51 +00:00
```ruby
map.resources :recipes do |recipes|
2015-03-26 11:28:08 +00:00
recipes.resources :ingredients
2024-01-30 08:26:51 +00:00
end
```
2015-03-26 11:28:08 +00:00
This will give you urls like /recipes/123/ingredients/321
Now we need to make some changes to the ingredients controller. Every ingredient belongs to a recipe. First add the filter:
2024-01-30 08:26:51 +00:00
```ruby
before_filter(:get_recipe)
2015-03-26 11:28:08 +00:00
private
def get_recipe
@recipe = Recipe.find(params[:recipe_id])
2024-01-30 08:26:51 +00:00
end
```
2015-03-26 11:28:08 +00:00
This will make sure that every ingredient knows what recipe it belongs to.
In the index method of the ingredient controller, make sure you have this:
2024-01-30 08:26:51 +00:00
```ruby
@ingredients = @recipe.ingredients.find(:all)
```
2015-03-26 11:28:08 +00:00
This makes sure you only show ingredients for this recipe, and not all ingredients in the database.
Because we changed the route for ingredients, we need to update all ingredient_url() and ingredient_path() calls in our controller and views. Change all occurrences of
2024-01-30 08:26:51 +00:00
```ruby
ingredient_url(@ingredient)
```
2015-03-26 11:28:08 +00:00
and
2024-01-30 08:26:51 +00:00
```ruby
ingredient_path(@ingredient)
```
2015-03-26 11:28:08 +00:00
to
2024-01-30 08:26:51 +00:00
```ruby
ingredient_url(@recipe, @ingredient)
```
2015-03-26 11:28:08 +00:00
and
2024-01-30 08:26:51 +00:00
```ruby
ingredient_path(@recipe, @ingredient)
```
2015-03-26 11:28:08 +00:00
<em>Note: Make sure that you don't replace 'ingredient' with '@ingredient' in your views!
Add a link to the ingredients to your recipe's index.rhtml view.
2024-01-30 08:26:51 +00:00
```ruby
link_to 'Ingredients', ingredients_path(recipe)
```
2015-03-26 11:28:08 +00:00
And, at last, make sure the create method of your ingredients controller attaches the ingredient to the right recipe. Make sure the first two lines look like this:
2024-01-30 08:26:51 +00:00
```ruby
def create
@ingredient = @recipe.ingredients.new(params[:ingredient])
```
2015-03-26 11:28:08 +00:00
You may now start your webserver and check out http://localhost:8000/recipes . Create some recipes and click 'ingredients'. You can now add ingredients for this recipe!
The next step will be customizing each method to suit your own needs.</em></pre>