Rails-ActiveRecord,其中嵌套路由中有多个条件

时间:2019-02-04 00:24:20

标签: sql ruby-on-rails activerecord

我正在建立一个用户可以在其中创建项目的网站。每个项目包含几篇论文。可以通过回答几个问题来评论每篇论文。

我有四个模型(用户,项目,论文,问题)。

questions_controller中,我创建了一个动作replies,目的是选择所有包含特定project_idpaper_id的答复。

在对象@replies中,我想选择所有论文并投影URL中显示的project_idpaper_id

http://0.0.0.0:3000/projects/3/papers/2/questions/1/replies

我想选择所有带有project_id == 3paper_id == 2的回复

为此,我编写了以下代码:

questions_controller.rb

  def replies
    @project = Project.find(params[:project_id])
    @paper = Paper.find(params[:paper_id])
    @replies = Question.where("project_id = ? AND paper_id = ?", params[:project_id], params[:paper_id])
  end

并在其中传递了@replies

views / questions / replies.html.erb

<% @replies.each do |reply| %>
  <%= reply %>
<% end %>

但是,它不起作用。实际上,我在@replies中进行的查询未在我的 views / questions / replies.html.erb 中传递任何内容。 我不确定自己在做什么错。

routes.rb

Rails.application.routes.draw do
  resources :projects do
    resources :papers do
      resources :questions do
        collection do
          get 'replies'
        end
      end
    end
  end
devise_for :users
root to: 'pages#home'

project.rb

class Project < ApplicationRecord
  belongs_to :user
  has_many :papers, dependent: :destroy
  has_many :questions
end

paper.rb

class Paper < ApplicationRecord
  belongs_to :project
  has_many :questions
  has_one_attached :paper_pdf
end

question.rb

class Question < ApplicationRecord
  belongs_to :user
  belongs_to :paper
  belongs_to :project
end

user.rb

class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable
  has_many :projects
  has_many :questions
end

1 个答案:

答案 0 :(得分:0)

您的应用程序设计中存在不同的问题。

第一个是问题的关系:

class Question < ApplicationRecord
  belongs_to :user
  belongs_to :paper
  belongs_to :project
end

我了解您在此处尝试做的事情:一个问题可以应用于不同的模型:用户,纸张,项目。尽管存在问题:问题只能引用这些模型之一(一个问题同时应用于不同的对象,这很奇怪)。然后,当您创建一个与用户有关的新问题时,纸张(paper_id)和项目(project_id)的外键将为空。

这不是一个好的设计。这样做的方法是使Question模型成为多态模型。

如果如上所述,如果Question仅适用于论文,那么为什么Question是Paper以外的其他模型的子代?

第二个问题:您尝试使用两个外键获取“问题”:

@replies = Question.where("project_id = ? AND paper_id = ?", params[:project_id], params[:paper_id])

正如我之前在问题1中所述,您的Question模型可能有3个外键(属于它的3个模型),但是一个问题一次不能引用多个模型。

因此,当您尝试同时使用这2个外键来提问时,您可能无法获得查询的答案。

第三个问题:使用浅层嵌套,您的路线太深:

http://0.0.0.0:3000/projects/3/papers/2/questions/1/replies

这里不需要三个嵌套级别。您似乎是新手,因此可能不会感到直观,但请在下面查看我的建议

建议

如果我正确阅读了您的问题布局,则问题仅适用于论文。 然后,如果您想获得与本文相关的所有问题,最好在此模型中而不是问题模型中创建一个新方法get replies

使其成为成员,而不是集合:

路线:

...
resources :papers do
  member do
     get 'replies'
  end
end
...

然后,当您的路线以papers/5/replies结尾

您可以在纸张控制器中触发以下代码:

def replies
  @paper = Paper.find(params[:id])
  @replies = @paper.questions
end

您注意到我的操作代码实际上并不需要路线中的任何先前ID。我只选择与数字5相关的params[:id]。纸张模型的ID。

如果您有深层嵌套的路由,您将意识到该路由中有一些未使用的数据。路线中真正重要的数据是最后一个参数。也许是前面的参数。

如果您需要确保论文属于当前用户项目,只需将当前用户与论文ID进行比较即可:

unless current_user.id == Paper.find(params[:id]).project.user.id
redirect_to root_path

然后就这样