Search code examples
ruby-on-railsrubyruby-on-rails-3grails-controllerrespond-with

rails respond_with associated record


for example, there's a Question model that has_many :answers

I'd like to show a form for new answer on the questions#show ( just like stackoverflow does )

routes would be something like:

resources :questions do
  resources :answers
end

What is the best way to show the errors for an invalid record under these circumstances?

The problem is that I can't render an action I need from within AnswersController ( since it would be questions#show ), the only way seem to be redirect_to and store errors in flash hash.

It just looks like a pretty much common scenario, I believe there should be some better way


Solution

  • This may be one of a few cases where it's actually justified to add a new resourceful member route to your QuestionsController:

    resources :questions do
      post 'answer', :on => :member
    end
    

    which would recognize question/:id/answer with a POST request routed to questions#answer, allowing you keep all the logic in one controller:

    class QuestionsController < ApplicationController
      ...
      def show
        @question = Question.find(params[:id])
      end
    
      def answer
        @question = Question.find(params[:id])
    
        @answer = @question.answers.build(params[:question][:answer])
    
        if @answer.save
          # show question with newly posted answer at url /question/:id
          redirect_to @question
        else
          # show question with invalid editable answer at url /question/:id/answer
          render 'show'
        end
      end
      ...
    end
    

    Explanation: In my opinion, the decision to handle the logic in one controller as opposed to two comes down to what you consider to be the resource of interest. Normally, you would consider each model to represent a distinct resource and thus create a separate controller to handle actions related to each resource. However, when there are multiple deeply coupled models for which multiple actions (e.g. show, new, create) are handled in a single view, it might be cleaner to think of the models as forming a single resource.

    In this example, I think of the resource as a collective one consisting of both the question and its answers. Since this collective resource is uniquely identified by the question itself, I would let the question controller handle it. The show action in the questions controller already involves retrieving the collective question-answers resource, so you might think of the answer action (and potentially unanswer and reanswer actions) as the analogue of update to that collective resource.

    Thinking of the resources this way is largely a matter of design preference, and there will be trade-offs depending on the requirements.