I have /users/1
as the public user profile (show method) but I'd like to have /user_name
instead of /users/1
.
Can I accomplish that if I use devise?
Thanks
Edit:
My show controller:
# public profile page
def show
@user = User.find(params[:id])
end
Get username into URL
You can do it. Devise is just an authentication solution. Your other controllers can behave however you want them to. Here is an overview of the steps that you need to get this URL behavior.
- Ensure that
user_name
is unique for each user
- Override the User's
to_param
method to return user_name
- Change the way that you get the user from the database
This would go in your User model
class User < ActiveRecord::Base
devise :database_authenticatable, :confirmable, :validatable
validates_uniqueness_of :user_name
def to_param
user_name
end
end
Then, in your controller, do something like this:
@user = User.find_by_user_name(params[:id])
Helpers like user_path(@user)
use the to_param
method to generate the URL.
Remove controller from URL
As for changing the route from /user/:id
to just /:id
. Something like this would work:
match '/:id' => 'users#show', :as => :user
Here you can use user_path
and user_url
because of the :as => :user
But you may benefit from reading these:
How can I implement vanity URL's in a Rails application?
How to implement "short" nested vanity urls in rails?
There are a lot of things to think about when you do it that way.
If you want to find by either the username or the id something like this should work:
@user = User.find_by_username(params[:id]) || User.find_by_id(params[:id])
dontangg's answer is mostly right, but there are some very important things his answer is missing.
First, you absolutely should not use straight usernames for the slugs. You need to normalize them in some way and blacklist certain slugs to prevent them from colliding with your resource or action names, something that is especially important when you are using them without the resource prefix in the URL. So, as tee pointed out, the first step is using a slug generation library, and friendly_id is currently the best. Secondly, you need to add your resource names to the friendly_id slug blacklist and handle the exception thrown when an offending slug is generated. You'll generally want to have a cached_slug
attribute on the sluggable model, and you'll need to expose that in #to_param (eg, def to_param; cached_slug end
). Then you'll need to update your finders to check on the slug, eg ModelName.where(:cached_slug => params[:id]).first
Edit: also, WRT the comment on another answer that recommended using User.all
, remember that .all
should never be used on any table that has more than a handful of records. You load the whole table into memory as Ruby objects and can easily bring your server to a standstill.