为其所有者显示资源并同时避免深层嵌套路由

时间:2017-05-22 19:06:15

标签: ruby-on-rails

Rails指导建议以避免深层嵌套路由,因此我想知道如何在不嵌套多个路由的情况下使这种关系工作:

用户有很多课程

课程有很多课程

课程有很多问题

一个问题有很多答案

约束:用户只能看到自己的课程,课程,问题和答案(应用程序需要这样)

作为第一次尝试,我们可以做到这一点(这违反了上面提到的建议):

resources :users
resources :courses do
  resources :lessons do
    resources :questions do
      resources :answers
    end
  end
end

不幸的是,这导致了长嵌套路由,但我发现我们可以像这样传递shallow: true

resources :courses, shallow: true do
  resources :lessons do
    resources :questions do
      resources :answers
    end
  end
end

简而言之,浅选项不会为这些操作生成嵌套URL(:show,:edit,:update,:destroy),例如,课程的show动作将是lessons/2而不是{{1但是这样做的不便之处在于:我无法通过current_user确定范围。

为了确保课程仅针对他/她的主人,我应该做以下事情:

courses/1/lessons/2

这意味着课程ID应该作为url中的参数传递,这不适用于浅层路由。 (对于其他路线也是如此:即:显示一个问题,我们应该通过course_id,lesson_id和question_id作为参数)

知道如何解决这个问题吗?

更新:

让我的问题更简单:

如果我们要遵循Rails建议并且不要将资源嵌套到多个级别,那么我们如何在不通过{def show current_user.courses.find(params[:id].lessons.find(params[:lesson_id]) end 的情况下获得属于current_user答案网址中有{1}}和course_id以及lesson_id

1 个答案:

答案 0 :(得分:4)

你所抓到的是一个常见的错误,你将上下文与授权混淆。嵌套在REST中的作用并不是决定谁得到了什么 - 嵌套告诉你资源之间的关联。

例如:

/users/:user_id/comments

创建一个有意义的restful URI,告诉我们在请求该端点时会发生什么 - 该用户的评论。

当前用户确定范围时,您希望从会话或令牌中获取用户,而不是使用参数:

class ReviewsController
  # POST /books/:book_id/reviews
  def create
    @comment = Book.find(params[:books_id]).comments.new(comment_params) do |c|
      c.user = current_user # from the session
    end
    # ...
  end
  # ...
end

使用param指定当前用户非常糟糕,因为它会让孩子们玩恶搞。恶意用户可以通过更改参数或使用firebug来改变表单上的隐藏输入,从而像其他用户一样执行操作。

您的情况很奇怪,但如果我理解正确,问题的核心实际上是提供间接关联 - 而不是路由。

class User
  has_many :courses
  has_many :lessons, through: :courses
  has_many :questions, through: :courses
  has_many :answers, through: :courses
end

class Course
  belongs_to :user
  has_many :lessons
  has_many :questions, through: :lessons
  has_many :answers, through: :lessons
end

class Lesson
  belongs_to :course
  has_many :questions
  has_many :answers, through: :questions
  has_one :user, through: :course
end

class Question
  belongs_to :lesson
  has_many :answers
  has_one :course, through: :lesson
  has_one :user, through: :course
end

class Answer
  belongs_to :question
  has_one :user, through: :question
  has_one :lesson, through: :question
  has_one :course, through: :question
end
# routes.rb
resources :courses, shallow: true do 
  resources :lessons
end

resources :lessons, only: [], shallow: true do
  resources :questions
end

resources :questions, only: [], shallow: true do
  resources :answers
end
class AnswersController < ApplicationController

  # We only need question for the nested routes
  before_action :set_question, only: [:new, :create, :index]
  before_action :set_answer, only: [:show, :edit, :update, :destroy]

  # /lessons/:lesson_id/answers
  def index
    @lessons = @course.lessons
  end

  # /lessons/:id
  def show
    @lesson = current_user.questions.find(:id)
  end

  private

  def set_question
    @course = current_user.questions.includes(:answers)
                          .find(:question_id)
  end

  def set_answer
    @answer = current_user.answers.find(:id)
  end 
end