Rails routes with :name instead of :id url paramet

2019-01-14 06:07发布

问题:

I have a controller named 'companies' and rather than the urls for each company being denoted with an :id I'd like to have the url use their :name such as: url/company/microsoft instead of url/company/3.

In my controller I assumed I would have

 def show
   @company = Company.find(params[:name])
 end

Since there won't be any other parameter in the url I was hoping rails would understand that :name referenced the :name column in my Company model. I assume the magic here would be in the route but am stuck at this point.

回答1:

params

The bottom line is you're looking at the wrong solution - the params hash keys are rather irrelevant, you need to be able to use the data contained inside them more effectively.

Your routes will be constructed as:

#config/routes.rb
resources :controller #-> domain.com/controller/:id

This means if you request this route: domain.com/controller/your_resource, the params[:id] hash value will be your_resource (doesn't matter if it's called params[:name] or params[:id])

--

friendly_id

The reason you have several answers recommending friendly_id is because this overrides the find method of ActiveRecord, allowing you to use a slug in your query:

#app/models/model.rb
Class Model < ActiveRecord::Base
  extend FriendlyId
  friendly_id :name, use: [:slugged, :finders]
end

This allows you to do this:

#app/controllers/your_controller.rb
def show
    @model = Model.find params[:id] #-> this can be the "name" of your record, or "id"
end


回答2:

Good answer with Rails 4.0+ :

resources :companies, param: :name

optionally you can use only: or except: list to specify routes

and if you want to construct a URL, you can override ActiveRecord::Base#to_param of a related model:

class Video < ApplicationRecord
  def to_param
    identifier
  end

  # or
  alias_method :to_param, :identifier
end

video = Video.find_by(identifier: "Roman-Holiday")
edit_videos_path(video) # => "/videos/Roman-Holiday"


回答3:

Honestly, I would just overwrite the to_param in the Model. This will allow company_path helpers to work correctly.

Note: I would create a separate slug column for complex name, but that's just me. This is the simple case.

class Company < ActiveRecord::Base
  def to_param
    name
  end
end

Then change my routes param for readability.

# The param option may only be in Rails 4+,
# if so just use params[:id] in the controller
resources :companies, param: :name

Finally in my Controller I need to look it up the right way.

class CompaniesController < ApplicationController
  def show
    # Rails 4.0+
    @company = Company.find_by(name: params[:name])
    # Rails < 4.0
    @company = Company.find_by_name(params[:name])
  end
end


回答4:

I recommend using the friendly_id for this purpose. Please be noted that there are differences between friendly_id 4 and 5. In friendly_id 4, you can use like this

 @company = Company.find(params[:id])

However, you won't be able to do that in friendly_id 5, you have to use:

 @company = Company.friendly.find(params[:id])

In case that you don't want to use the params[:id] but params[:name], you have to override the route in routes.rb. For example

 get '/companies/:name', to: "companies#show"

Hope these info would be helpful to you



回答5:

There's actually no magic to implement this, you have to either build it yourself by correctly implementing to_param at your model (not recommended) or using one of the gems available for this like:

  • friendly_id
  • has_permalink

I use friendly_id and it does the job nicely.



回答6:

Model.find(primary_key)
The default parameter here is primary_key id. If you want to use other columns, you should use Model.find_by_xxx so here it could be

def show
  @company = Company.find_by_name(params[:name])
end