【问题标题】:Filtering nested attributes in rails API json output过滤rails API json输出中的嵌套属性
【发布时间】:2021-04-19 01:53:47
【问题描述】:

我正在构建一个 API,其中有一个名为 Studio 的模型。工作室有很多电影和很多系列,电影和系列有很多自己的角色。此外,一个角色可以出现在许多电影和/或许多系列中,并且属于工作室。 我现在已经弄清楚了所有模型关系表的后勤工作,但是我现在正在尝试做的事情,我想不出办法。

所以我的路线经过设计,因此我可以执行类似 localhost:3000/api/v1/studios/1/characters 之类的操作 -> 这样我可以显示所有工作室的 (studio_id: 1) 角色(来自所有电影和系列属于那个工作室)。 这很容易,只需执行以下操作:

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

在我的 CharactersController 中,我有

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

现在我正在尝试设计物流以便我可以做到localhost:3000/api/v1/studios/1/seriees/1/characters 并且只从那个特定的系列中获取角色。 localhost:3000/api/v1/studios/1/movies/1/characters 也应该是有效的。

我试过了

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

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

在我的 CharactersController 中,before_action :get_seriebefore_action :get_movie 在代码的最开始,但它给了我一个错误,因为我从来没有得到一个 movie_id 和一个 seriee_id,它要么是一个要么另一个,所以它不能同时搜索两者。

来自角色的 POST 我只会从 localhost:3000/studios/1/characters 进行,所以这不是问题,我只想能够从 localhost:3000/studios/1/movies/1/characterslocalhost:3000/studios/1/seriees/1/characters 获取

我希望我没有说太多,你能理解我。如果您认为我解决此问题的方法不正确,请告诉我,因为我正在学习,任何建议都会非常有价值。

【问题讨论】:

    标签: ruby-on-rails controller nested-attributes


    【解决方案1】:

    我怀疑您是否真的想将这些资源嵌套在一层以上:

    经验法则:资源的嵌套深度不得超过 1 级。集合可能需要由其父级确定范围,但特定成员始终可以通过 id 直接访问,并且不需要确定范围(除非由于某种原因 id 不是唯一的)。
    - Jamis Buck

    鉴于此处的域,您必须指定工作室来获取给定电影的角色是没有意义的。在 REST 中嵌套的目标是通过使两个实体成为 URL 结构的一部分来使它们之间的关系变得明确 - 不要过于明确。

    当涉及到像 url 生成这样的东西时,过多的嵌套会导致一些非常复杂的代码 - 我的意思是你真的想写吗?

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

    您可以使用shallow: true 选项解决这些问题:

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

    或者通过避免“俄罗斯娃娃”呼叫resources

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

    您也确实应该使用关联来获取嵌套项。

    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
    

    一般来说,应该避免使用像Character.where(studio_id: @studio.id) 这样的代码,因为它会泄露关系的实现细节。您的控制器不需要知道角色和工作室之间的关系——它只知道movie 有一个名为characters 的方法。

    我真的不想在这里深入了解您的建模细节,但Character.where(studio_id: @studio.id) 不适用于电影很可能包含工作室角色的子集 - 而不是整个电影世界的要求。相反,您需要一个连接电影和角色的连接表。

    【讨论】:

    • 是的,我确实有一种“俄罗斯娃娃”routes.rb ......只是有点难以理解我应该如何面对这个问题。我需要能够返回工作室的所有电影、工作室的所有系列以及工作室的所有角色(比如说,所有 MCU 角色)。但也可以单独返回单个电影中的角色列表。并且一个角色应该返回他也参加过的电影/系列的列表。我已经设置了联接表,所以我会尝试重新考虑我的路线...谢谢您的帮助。
    • 您确实需要各种端点,例如 /studios/marvel/characters/movies/amazing-spider-man/characters。一个技巧是在设置路由时使用module: 选项将其发送到Studios::CharactersControllerMovies::CharactersController。而不是将所有东西都塞进一个控制器中。
    • 在某些情况下,您还可以让资源的显示或索引响应包含一堆附加数据。例如,/studios/marvel 的 JSON 响应也可以包含字符列表或电影列表。 API 设计在这里没有真正正确的答案,您必须确定所需的请求数量与响应的重量,因为包含每个可能用例的所有内容会占用大量带宽。
    • 我决定我会采取一种更简单的方法,暂时不嵌套我的路线。它只是带来了很多麻烦,这只是一个小项目,现在不需要如此挑剔。也许稍后我会尝试做嵌套的事情。现在我只想让它工作。谢谢你,你很有帮助。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-04
    • 2019-03-22
    • 2015-07-10
    • 1970-01-01
    • 2023-03-31
    • 1970-01-01
    • 2013-11-03
    相关资源
    最近更新 更多