Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • SEARCH
  • Home
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 7804771
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: June 2, 20262026-06-02T02:02:00+00:00 2026-06-02T02:02:00+00:00

Assume a standard has_many :through relationship among three models class Person < ActiveRecord::Base has_many

  • 0

Assume a standard has_many :through relationship among three models

class Person < ActiveRecord::Base
  has_many :memberships, :dependent => :destroy
  has_many :clubs, :through => :memberships
end
class Club < ActiveRecord::Base
  has_many :memberships, :dependent => :destroy
  has_many :persons, :through => :memberships
end
class Membership < ActiveRecord::Base
  belongs_to :person
  belongs_to :club
end

In an API driven application, you would expect to expose URIs that:

  • list the clubs that person x belongs to
  • list the persons that are members of club y
  • (…and the usual collection of CRUD methods…)

My first thought is to implement a pair of nested routes that map to the MembersController, something like:

GET /clubs/:club_id/memberships     => members_controller#index
GET /persons/:person_id/memberships => members_controller#index

… but here it gets a bit weird.

Both routes map to the same members_controller method (index). That’s no problem — I can look in the params hash to see if a :club_id or a :person_id is given, and apply appropriate scoping on the members_controller table.

But I’m not certain we want to expose Member objects to the end user at all. A more intuitive pair of routes (at least from the user’s perspective) might be:

GET /clubs/:club_id/persons   
GET /persons/:person_id/clubs 

… which would return a list of persons and a list of clubs (respectively).

But if you do it this way, what controller and action would you map these routes to? Is there any convention in Rails that offers guidance? Or is this strayed far enough off the track that I should just implement it any way I see fit?

  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-06-02T02:02:02+00:00Added an answer on June 2, 2026 at 2:02 am

    I’ve ended up implementing routes and controllers that accomplish the following:

    • It’s fully RESTful.
    • It’s fully symmetrical: /persons/:person_id/clubs/:club_id/memberships is identical to /clubs/:club_id/persons/:person_id/memberships
    • It’s very DRY.
    • Ordinary Users never see a Membership :id. Instead, :person_id and :club_id serve as a compound key that refer to a specific membership.
    • It does allow direct access to Membership objects by :id, but that’s reserved for Administrators.

    Here are some of the routes it recognizes:

    /persons/:person_id/clubs/:club_id/memberships - a specific membership association
    /clubs/:club_id/persons/:person_id/memberships - equivalent
    /persons/:person_id/clubs - all the clubs that a specific person belongs to
    /clubs/:club_id/persons - all the persons that belong to a specific club
    /memberships/:id - access to a specific membership (accessible only to admin)
    

    Note that in the following examples, I have NOT included the Devise and CanCan constructs for authentication and authorization. They’re easy to add.

    Here is the routing file:

    # file: /config/routes.rb
    Clubbing::Application.routes.draw do
    
      resources :persons, :except => [:new, :edit] do
        resources :clubs, :only => :index
      end
    
      resources :clubs, :except => [:new, :edit] do
        resources :persons, :only => :index
      end
    
      resources :memberships, :except => [:new, :edit]
    
      # there may be clever ways to specify these routes using #resources and
      # #collections and #member, but this ultimately is more straightforward
    
      match "/persons/:person_id/clubs/:club_id/memberships" => "memberships#create", :via => :post
      match "/persons/:person_id/clubs/:club_id/memberships" => "memberships#show", :via => :get
      match "/persons/:person_id/clubs/:club_id/memberships" => "memberships#update", :via => :put
      match "/persons/:person_id/clubs/:club_id/memberships" => "memberships#destroy", :via => :delete
    
      match "/clubs/:club_id/persons/:person_id/memberships" => "memberships#create", :via => :post
      match "/clubs/:club_id/persons/:person_id/memberships" => "memberships#show", :via => :get
      match "/clubs/:club_id/persons/:person_id/memberships" => "memberships#update", :via => :put
      match "/clubs/:club_id/persons/:person_id/memberships" => "memberships#destroy", :via => :delete
    

    The controllers were surprisingly simple. For PersonsController and ClubsController, the only non-standard thing was in the :index methods, where we look for the presence of :club_id or :person_id in the parameters and scope accordingly:

    # file: /app/controllers/persons_controller.rb
    class PersonsController < ApplicationController
      respond_to :json
      before_filter :locate_collection, :only => :index
      before_filter :locate_resource, :except => [:index, :create]
    
      def index
        respond_with @persons
      end
    
      def create
        @person = Person.create(params[:person])
        respond_with @person
      end
    
      def show
        respond_with @person
      end
    
      def update
        if @person.update_attributes(params[:person])
        end
        respond_with @person
      end
    
      def destroy
        @person.destroy
        respond_with @person
      end
    
      private
    
      def locate_collection
        if (params.has_key?("club_id"))
          @persons = Club.find(params[:club_id]).persons
        else
          @persons = Person.all
        end
      end
    
      def locate_resource
        @person = Person.find(params[:id])
      end
    
    end
    
    # file: /app/controllers/clubs_controller.rb
    class ClubsController < ApplicationController
      respond_to :json
      before_filter :locate_collection, :only => :index
      before_filter :locate_resource, :except => [:index, :create]
    
      def index
        respond_with @clubs
      end
    
      def create
        @club = Club.create(params[:club])
        respond_with @club
      end
    
      def show
        respond_with @club
      end
    
      def update
        if @club.update_attributes(params[:club])
        end
        respond_with @club
      end
    
      def destroy
        @club.destroy
        respond_with @club
      end
    
      private
    
      def locate_collection
        if (params.has_key?("person_id"))
          @clubs = Person.find(params[:person_id]).clubs
        else
          @clubs = Club.all
        end
      end
    
      def locate_resource
        @club = Club.find(params[:id])
      end
    
    end
    

    The MembershipsController is only slightly more complicated: it detects :person_id and/or :club_id in the parameters hash and applies scoping accordingly. If both :person_id and :club_id are present, we can assume it refers to a unique membership object:

    # file: /app/controllers/memberships_controller.rb
    class MembershipsController < ApplicationController
      respond_to :json
      before_filter :scope_collection, :only => [:index]
      before_filter :scope_resource, :except => [:index, :create]
    
      def index
        respond_with @memberships
      end
    
      def create
        @membership = scope_collection.create(params[:membership])
        respond_with @membership
      end
    
      def show
        respond_with @membership
      end
    
      def update
        if @membership.update_attributes(params[:membership])
        end
        respond_with @membership
      end
    
      def destroy
        @membership.destroy
        respond_with @membership
      end
    
      private
    
      # apply :person_id and/or :club_id scoping if present in params hash
    
      def scope_collection
        @memberships = scope_by_parameters
      end
    
      def scope_resource
        @membership = scope_by_parameters.first
      end
    
      def scope_by_parameters
        scope_by_param_id(scope_by_param_id(Membership.scoped, :person_id), :club_id)
      end
    
      def scope_by_param_id(relation, scope_name)
        (id = params[scope_name]) ? relation.where(scope_name => id) : relation
      end
    
    end
    
    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

Let's assume three models, standard joins: class Mailbox < ActiveRecord::Base has_many :addresses has_many :domains,
I have a fairly standard 'has_many :x, :through => :y' relationship with a user,
Let's assume I have two models: A and B. A has one-to-many relationship with
I have a standard many-to-many relationship in my database between Person and Widget. A
Suppose you have a standard org hierarchy table in Oracle. For simplicity sake, assume
Assume I have a C# class like this: [XmlRoot(floors)] public class FloorCollection { [XmlElement(floor)]
Assume a class Vehicle has a method called StartEngine, and in my subclass called
I am writing a standard database-backed business application. Assume that I work with bank
Assume a class (for instance URI) that is convertable to and from a String
Assume I have the following string: Hellotoevryone<img height=115 width=150 alt= src=/Content/Edt/image/b4976875-8dfb-444c-8b32-cc b47b2d81e0.jpg />Iamsogladtoseeall. This

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.