Rails routing for has_and_belongs_to_many relationship

末鹿安然 提交于 2019-12-24 06:07:33

问题


I'm working on a rails 4 API for a sports team where I have players and teams and I'm struggling a little with rails routing and a has_many relationship. My relationship between players and teams looks like:

class Team < ActiveRecord::Base
  extend Searchable
  validates :title, presence: true
  has_and_belongs_to_many :players
end

class Player < ActiveRecord::Base
  extend Searchable
  validates :first_name, presence: true
  validates :last_name, presence: true
  has_and_belongs_to_many :teams
end

I'd like to be able to add an existing player to a team, but I'm unsure of how to change my routes.rb file. Currently, it looks like:

Rails.application.routes.draw do
  devise_for :users
  namespace :api, defaults: { format: :json },
            constraints: { subdomain: 'api' }, path: '/'  do
    scope module: :v1 do
      resources :users, :only               => [:show, :create, :update, :destroy]
      resources :teams, :only               => [:show, :create, :update, :destroy, :index]
      resources :players, :only             => [:show, :create, :update, :destroy, :index]
      resources :sessions, :only            => [:create, :destroy]
    end
  end
end

which allows for CRUD operations on players and teams models. I was thinking that for adding a player to an existing team, my route would need to look like:

/teams/:team_id/add_player/

but I'm unsure of how to declare that route in routes.rb. So, a couple of questions:

  1. Does that route make sense to people from a REST-ful perspective? If so, how would I declare this route in routes.rb
  2. Should it be a PATCH or a POST method for adding a player to a team?

Thanks for any help offered,

Sean


回答1:


You can declare this route like this:

resources :teams, only: [:show, :create, :update, :destroy, :index] do
  put 'add_player', on: :member
end

It will map the route to the add_player action in your TeamsController.

From the REST-ful perspective i would suggest you to make this in the players#update action though, since you are basically changing the player record.




回答2:


You'll need to use a nested resource:

#config/routes.rb
resources :teams, only: [....] do
   resources :players, path: "", path_names: {new: "add_player", create: "add_player", destroy: "remove_player" }, only: [:create, :destroy] #-> /v1/teams/:team_id/add_player
end

This will route to your players_controller, passing the :team_id param:

#app/controllers/players_controller.rb
class PlayersController < ApplicationController
   def new
      @players = Player.all
   end

   def create
      if params[:team_id]
        @team = Team.find params[:team_id]
        @player = Player.find params[:id]
        @team.players << player
      else
        # normal "create" action
      end
   end

   def destroy
      if params[:team_id]
          @team = Team.find params[:id]
          @player = Player.find params[:id]
          @team.players.delete player
      end
   end
end

You'd be able to couple this with the following view:

#app/views/players/new.html.erb
<%= form_tag team_players_path do %> #-> method should be "post"
   <%= collection_select :player, :id, @players, :id, :name %>
   <%= submit_tag %>
<% end %>

--

Trivia:

With HABTM, you get the << & collection.delete methods -- both allowing you to add and remove objects from a collection super simply.

-

Does that route make sense to people from a REST-ful perspective? If so, how would I declare this route in routes.rb

Yep.

Should it be a PATCH or a POST method for adding a player to a team?

In line with the resourceful routing structure, I'd say you could get away with a POST request to the create action, but it could be done a number of ways!


Update

Do this:

#config/routes.rb
resources :teams, only: [....] do
   match "players/:id", to: "players", via: [:put, :delete]
end

This will create the following routes:

#put      url.com/teams/:team_id/players/:id
#destroy  url.com/teams/:team_id/players/:id

This will allow you to use a players method in your teams controller:

#app/controllers/teams_controller.rb
class TeamsController < ApplicationController
   def players
      @team = Team.find params[:team_id]
      @player = Player.find params[:id]
      if request.put?
         @team.players << player
      elsif request.delete?
         @team.players.destroy player
      end
   end
end


来源:https://stackoverflow.com/questions/33364584/rails-routing-for-has-and-belongs-to-many-relationship

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!