GuidoMedina
GuidoMedina

Reputation: 127

Filtering nested attributes in rails API json output

Im building an API in which i have a model called Studio. Studio has many movies and many series, and movies and series have many characters of their own. Also, a character can be in many movies and/or many series, and belongs to a studio. I got all the models relational tables logistics figured out by now, but what i'm trying to do right now, i can't think of a way.

So my routes are designed so i can do something like localhost:3000/api/v1/studios/1/characters -> this way i display all the studio's (studio_id: 1) characters (from all the movies and series that belong to that studio). That is fairly easy with just doing something like:

@characters = Character.where(studio_id:@studio.id)

In my CharactersController, where i have

def get_studio
    @studio = Studio.find(params[:studio_id])
end

Now i am trying to design the logistics so i can do localhost:3000/api/v1/studios/1/seriees/1/characters And only get the characters from that specific seriees. localhost:3000/api/v1/studios/1/movies/1/characters should also be valid.

I tried

def get_movie
    @movie = Movie.find(params[:movie_id])
end`

and

def get_serie
    @serie = Seriee.find(params[:seriee_id])
end

in my CharactersController, with before_action :get_serie and before_action :get_movie at the very start of the code, but it gives me an error because i'm never getting both a movie_id and a seriee_id, it's either one or the other, so it can't search for both at the same time.

The POST from character i will only do from localhost:3000/studios/1/characters so that's not a problem, i only want to be able to GET from localhost:3000/studios/1/movies/1/characters and localhost:3000/studios/1/seriees/1/characters

I hope i didn't ramble to much and you are able to understand me. If you think that my way of solving this problem is just not right, please tell me as i am just learning and any advice would be very valuable.

Upvotes: 0

Views: 408

Answers (1)

max
max

Reputation: 102240

I doubt you actually want to nest those resources more then one level deep:

Rule of thumb: resources should never be nested more than 1 level deep. A collection may need to be scoped by its parent, but a specific member can always be accessed directly by an id, and shouldn’t need scoping (unless the id is not unique, for some reason).
- Jamis Buck

Given the domain here it does not make sense that you would have to specify the studio to get the characters for a given movie. The goal of nesting in REST is to make the relationships between two entities explicit by making them part of the URL structure - not to be overly explicit.

Excessive nesting leads to some very overcomplicated code when it comes to stuff like url generation - I mean do you really want to write?

 url_for [:ap1, :v1, studio, movie, :characters]

You can fix these issues by using the shallow: true option:

resources :studios do
  resources :movies, shallow: :true do
    resources :characters
  end
end

or by avoiding "russian doll" calls to resources:

resources :studios do
  resources :movies, only: [:new, :create]
end

resources :movies, only: [:show, :update, :destroy] do
  resources :characters, only: [:create, :index]
end

You also really should be using assocations to fetch the nested items.

class CharactersController
  before_action :set_movie, only: [:index, :create]

  # GET /api/v1/movies/1/characters.json
  def index
    @characters = @movie.characters
  end


  private

  def set_movie
    @movie = Movie.find(params[:movie_id])
  end
end

In general code like Character.where(studio_id: @studio.id) should be avoided as it leaks the implementation details of the relationship. Your controller should not need to know how characters and studios are related - it just knows that movie has a method named characters.

And I don't really want to get into the details of your modeling here but Character.where(studio_id: @studio.id) will not work with the requirement that movies most likely contains a subset of the characters of a studio - not their entire cinematic universe. Instead you need a join table that links movies and characters.

Upvotes: 1

Related Questions