How to Override the Named Route Parameter ID
In the previous posts, we've learned about the concepts of dynamic segments as well as named routes in Rails. Naming a route generates two helper methods which can be used to generate the route URL, whereas a dynamic segment allows us to create dynamic routes, where the parts of the URL can vary.
In this post, we'll combine these two concepts. Consider the following named route which contains a dynamic segment called :id
:
# config/routes.rb
get "item/:id", to: "items#show", as: "item"
When the application receives a request on /item/4
, it forwards it to the ItemsController#show
action. You can access the ID of the post as follows: params[:id]
.
To generate the item URL, you can use the named route helper methods item_path
and item_url
, passing the ID of the post.
item_path(id: @item.id) # /item/10
In this example, @item
is an instance of a Rails model.
Typically, most of the time you'll work with the id
of a model. To make this convenient, Rails makes the above named helper method call even shorter. Just pass the id, without the :id
key.
item_path(@item.id) # /item/10
You can go even one step further. Simply pass the instance, and Rails will figure out the id
from the object.
item_path(@item)
It's recommended that you use the last syntax as not only it's shorter, but also it's very readable. Just reading item_path(@item)
tells us that we need the path to an item.
At this point, you may be wondering: How does Rails know to call the id
method on the passed instance, i.e. @item
?
Answer: Behind the scenes, Rails calls the to_param
method on the provided object. The to_param
method is defined on all ActiveRecord models, and it returns the id
of the model by default. That's how Rails knows to find the proper ID of the model when you pass it directly to the helper method, i.e. item_path(@item)
.
If there are multiple dynamic segments in the route, you can provide multiple corresponding models.
get "section/:section_id/article/:id", to: "articles#show", as: "section_article"
section = Section.first
article = session.articles.first
section_article_url(section, article) # /section/1/article/2
You must provide the model arguments to the helper method in the same order in which their IDs occur in the dynamic segments.
So here's what we've learned so far: Rails will infer the model ID passed to the named route helper method. It does so by calling the to_param
method on the provided model instance, which returns the model's id
by default.
That raises the question, can we customize the value of the dynamic segment to use another value, instead of using the default model id
value? We can. Simply override the id
value by defining a to_param
method in your model and returning the value you want to insert.
For example, instead of an id
, you want to use the slug of a post to identify it, i.e. instead of /posts/10
where 10 is the ID of the post, you want /posts/ruby-on-rails
where ruby-on-rails
is the slug of the post titled "Ruby on Rails" (a slug is typically the lowercased, dasherized version of the title).
To achieve this, you will override the to_param
method in the Post
model as follows:
class Post < ApplicationRecord
def to_param
title.parameterize
end
end
Note: The parameterize
method on a string replaces special characters in a string so that it may be used as part of a ‘pretty’ URL.
After this, whenever you call the named helper methods post_path(post)
, or post_url(post)
it will produce the URL containing the slug of the post.
@post = Post.create(title: "Ruby on Rails")
# in view
post_path(@post) # /posts/ruby-on-rails
Finally, you can update the dynamic segment from :id
to :slug
, so that you can use params[:slug]
in the controller to extract the slug from the URL.
get "posts/:slug", to: "posts#show", as: "post"
As you can see, we've successfully overridden the default segment key from :id
to :slug
. If instead of a single route, you're using a resourceful route, you can accomplish the same thing as follows:
resources :posts, param: :slug
In the next lesson, we'll learn about one of the most important concepts in Rails and the one you'll be spending most of your time as a practicing Rails developer: Resources and Resourceful Routes.
If you have any questions or feedback, didn't understand something, or found a mistake, please leave a comment below or send me an email. You can also subscribe to my blog to receive future posts via email.