Pain-Free Pretty URLs in Rails
Brian Landau, Former Developer
Article Category:
Posted on
When working recently on a client project in a later stage of development, it became clear that the client wanted “pretty” URLs for specific models in the application. In particular, they wanted some URLs without an id, meaning the normal Rails to_param
trick was out:
def to_param "#{self.id}-#{self.name.parameterize}" end
We needed this type of URL on the User
model. We already had a field that was both unique and “URL safe,” the login field, so for this model we could just do
def to_param self.login end
On another model, I decided to create a slug
field just for the purpose of pretty URLs. There was already a name
field that had to be unique, so I just created a before_validation
method to handle the creation and updating of the slug:
before_validation :update_or_create_slug def to_param slug end private def update_or_create_slug if self.new_record? || self.name_changed? || self.slug.blank? self.slug = self.name.try(:slugize) end end
I used my own “slugize
” method, as it’s a little different from the Rails built-in parameterize
, and I believe it offers a few minor advantages. I also check for three conditions where we need to assign a new slug:
- If it's on a new record.
- If the name has changed.
- If the slug currently happens to be blank.
You’ll also notice I use the “.try(:slugize)
” in case the model is invalid and has nil
for the name.
With all this in place, you’d think we’d be all set -- or at least I did.
There was one problem left, though. Let’s say you have a user who is editing an existing model object in the system, and they try to update it with a blank name. This will result in a user with a blank slug
field. This wouldn’t seem too bad, but it results in the edit page not being able to render because to_param
returns a blank string to the named route URL helper on the form, which raises an ActionController::RoutingError
exception.
The solution is to return the old slug value for to_param
:
def to_param slug_changed? ? slug_was : slug end
With that solved, we’re all set to go with pretty, id-free URLs.
If you’re in a situation where you don’t have a required unique field like we did on this project, you might want to look at Patrick’s solution from FeedStitch.