【问题标题】:Rails 5 RESTful API with advanced relations具有高级关系的 Rails 5 RESTful API
【发布时间】:2017-04-29 18:41:46
【问题描述】:

我有很多具有高级关系的资源(habtm/hm/hmt 等),你能想象到的一切,但现在是时候为这个 API 编写一个漂亮的路由了。 问题是,我无法找到关于嵌套资源 + 高级关系的最佳实践来做我的路由,这就是我想要做的:

这是我与相关关系的模型

# app/models/candidate.rb
class Candidate < ApplicationRecord
  include Sociable, Locatable

  belongs_to :user
  has_many :sourcing_accounts
  has_many :accounts, through: :sourcing_accounts
  has_many :users, through: :sourcing_accounts
end

# app/models/sourcing_account.rb
class SourcingAccount < ApplicationRecord
  belongs_to :account
  belongs_to :candidate
  belongs_to :user
end

# app/models/user.rb
class User < ApplicationRecord
  include Sociable

  has_many :candidates
  has_many :campaigns
  has_many :sourcing_account
end

对于这个例子,我愿意通过创建SourcingAccount 来创建CandidateUser 之间的关系。

resources :candidates do
  resources :accounts
  resources :users, only: [:index] do
    post :remove
    post :add
  end
end

它生成:

v1_candidate_user_remove POST   /v1/candidates/:candidate_id/users/:user_id/remove(.:format) api/v1/users#remove {:subdomain=>"api", :format=>:json}
   v1_candidate_user_add POST   /v1/candidates/:candidate_id/users/:user_id/add(.:format)    api/v1/users#add {:subdomain=>"api", :format=>:json}

我没有发现任何关于此的信息。有最佳实践吗??? 如果不是,您认为最适合这种情况的是什么?

如果没有精确性,Rails 想将其路由到 users#remove 和 users#add,我认为这是完全错误的。这些动作不能属于用户控制器。

奖金:

创建属于其他 2 个模型的 Account 的多态路径应该是什么(具有存在验证),2 个模型是 Source,另一个是多态 [Candidate,User] # for example,(它们是 Sociable 模型)

【问题讨论】:

    标签: ruby-on-rails ruby routing ruby-on-rails-5


    【解决方案1】:

    最好的做法是永远不要*嵌套资源超过一层,并且只在需要嵌套或提供上下文的地方嵌套。

    请记住,任何具有唯一 id 或 uid 的记录都可以在没有上下文的情况下直接获取。因此,不必要地嵌套成员路由会使您的 API 过于复杂且冗长。

    DELETE /as/:id
    is a lot better than
    DELETE /as/:a_id/bs/:b_id/c/:id # Are you kidding me!
    

    我们以一个经典的微博应用为例:

    class User
      has_many :posts, foreign_key: 'author_id'
      has_many :comments
    end
    
    class Post
      belongs_to :author, class_name: 'User'
    end
    
    class Comment
      belongs_to :user
      belongs_to :post
    end
    

    您可以将路线声明为:

    resources :users do
      scope module: :users do
        resources :posts, only: [:index]
        resources :comments, only: [:index]
      end
    end
    
    resources :posts do
      resources :comments, module: :posts, only: [:index, :create]
    end
    
    resources :comments, only: [:index, :destroy, :update]
    

    使用模块选项可以让我们在控制器之间区分“基础资源”及其嵌套表示:

    class API::V1::PostsController < ApplicationController
      # GET /api/v1/posts
      def index
        @posts = Post.all
      end
    
      def show
        # ...
      end
      def destroy
        # ...
      end
      def update
        # ...
      end
    end
    
    # Represents posts that belong to a user
    class API::V1::Users::PostsController < ApplicationController
      # GET /api/v1/users/:user_id/posts
      def index
        @user = User.eager_load(:posts).find(params[:user_id])
        respond_with(@user.posts)
      end
    end
    

    在某些情况下,如果资源应该在另一个上下文中创建,您还需要嵌套创建操作:

    class API::V1::Posts::CommentsController < ApplicationController
      # PATCH /api/v1/posts/:post_id/comments
      def create
        @post = Post.find(params[:post_id])
        @comment = @post.comments.create(comment_params)
        respond_with(@comment)
      end
    
      # GET /api/v1/posts/:post_id/comments
      def index
        @post = Post.eager_load(:comments).find(params[:post_id])
        respond_with(@post.comments)
      end
    end
    

    【讨论】:

    • 此外,规范的 CRUD 操作(显示、索引、创建、销毁、更新)可以使用额外的动词进行扩充,但如果您曾经使用过类似 addremove 或列表或嵌套的同义词def things 的资源。你做错了。
    • 感谢您的清晰解释,但是在您的最后一个示例中,您将如何使用 2 个上下文(模型属于其他 2 个)包装创建操作?就像我帖子中的奖金问题一样。当两个父母需要同时存在时如何创建关系对象
    • 你没有。使用最重要的模型作为上下文,并将其他关系作为参数传递。
    • 好的!非常感谢 !如果我做对了,它应该如下所示:POST /api/v1/candidates/1/accounts?source_id=2。
    猜你喜欢
    • 1970-01-01
    • 2021-09-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-01
    • 2017-01-29
    • 1970-01-01
    相关资源
    最近更新 更多