ruby-on-railsruby-on-rails-5custom-routes

Rails Custom route, how to remove ?id=


My app has a model msa with a :name and :short_name.

On the msa index page, there is a drop-down menu of all msa.names so the user can route to the show page of the selected msa.

This functionality is coded with a collection_select that routes to a custom method in the msa controller.

msa index view:

<%= form_with url: 'msas/redirect', method: :post, local: true do |f| %>
<%= f.collection_select(:id, Msa.all, :id, :name)%>
  <%= f.submit "Search" %>
<% end %>

msa controller redirect method:

 def redirect
    @msa=Msa.find(params[:id])
    redirect_to msa_path(@msa, short_name: @msa.short_name, id: @msa.id)
  end

Rather than have routes that read localhost:3000/msa/1, I want them to read localhost:3000/search/:short_name.

My routes.rb:

  scope format: false do
    resources :msas, :only => [:show], path: '/search', param: :short_name
  end
  resources :msas, :except => [:show]

Everything seems to work ok, except the route appears like this in the browser:

localhost:3000/search/Chicago?id=1

I tried remove the id: @msa.id from the redirect method, but ended up with this error:

Couldn't find Msa without an ID

which was keyed off the set_msa method in the msa controller.

I'm wondering two things: a) have I strayed too far from typical rails convention, and b) if there is a way to do this and not reveal the id# of my msa models to the world?


Solution

  • If there is a way to do this and not reveal the id# of my msa models to the world?

    Just remove the id from the path helper

    redirect_to msa_path(@msa, short_name: @msa.short_name)
    

    and counter the error by defining a new method and removing the entry for show method in the set_msa like so

    before_action :set_custom_msa, only: [:show]
    before_action :set_msa, only: [:edit, :update,..] #remove the entry for show
    
    private
    def set_custom_msa
      @msa = Msa.find_by(short_name: params[:short_name])
    end
    

    Have I strayed too far from typical rails convention

    No! but if your final goal is to hide :id from the URL and to make user-friendly URLs, then I would suggest you to look at friendly_id