Rails 3 - Отображение ошибок отправки в модели полиморфных комментариев - PullRequest
3 голосов
/ 17 февраля 2011

Совершенно новичок в Rails 3, и он все время гуглил безрезультатно, чтобы решить следующую проблему, при этом в большинстве руководств не хватает ошибок обработки.

Я создал проект Rails 3 с несколькими типами / моделями контента, такими как Статьи, Блоги и т. Д. Каждый тип контента имеет комментарии, все они хранятся в одной таблице Комментариев как вложенный ресурс и с полиморфными ассоциациями. Есть только одно действие для комментариев, действие «создать», потому что нет необходимости в показе и т. Д., Поскольку он принадлежит родительскому типу контента и должен просто снова отобразить эту страницу при отправке.

Теперь у меня есть большая часть этой работы, и комментарии отправляются и публикуются просто отлично, но последняя остающаяся проблема - это отображение ошибок, когда пользователь не заполняет обязательное поле. Если поля не заполнены, он должен вернуться на родительскую страницу и отобразить ошибки проверки, как это обычно делает Rails с MVC.

Действие создания моего контроллера комментариев выглядит следующим образом, и это то, что я впервые попробовал ...

def create
   @commentable = find_commentable
   @comment = @commentable.comments.build(params[:comment])

   respond_to do |format|
      if @comment.save
         format.html { redirect_to(@commentable, :notice => 'Comment was successfully created.') }
      else
         format.html { redirect_to @commentable }
         format.xml  { render :xml => @commentable.errors, :status => :unprocessable_entity }
      end
   end
end 

Когда вы ничего не заполняете и не отправляете форму комментариев, страница перенаправляется обратно к соответствующему родителю, но ни флэш, ни ничего не отображается. Теперь я выяснил, почему, насколько я понимаю, флэш-память не будет сохраняться в redirect_to, а только в рендере. Теперь вот в чем проблема.

В контроллере комментариев есть только действие «создать», поэтому мне нужно было указать рендер на «блоги / шоу» (ПРИМЕЧАНИЕ: я знаю, что это не полиморфно, но как только я получу эту работу, я буду беспокоиться об этом потом). Я пробовал это в блоке "else" приведенного выше кода ...

else
   format.html { render 'blogs/show' }
   format.xml  { render :xml => @commentable.errors, :status => :unprocessable_entity }
end

В любом случае, когда я пытаюсь отправить недействительный комментарий в блог, я получаю сообщение об ошибке, в котором говорится: «Показано [...] / app / views / blogs / show.html.erb, где поднята строка # 1: неопределенный метод `title 'для nil: NilClass."

Глядя на URL, я думаю, что знаю, почему ... вместо того, чтобы указывать на / blogs / the-title-of-my-article (я использую friendly_id), он идет в / blogs / the-title- комментарии из-моя-статья /. Я полагаю, что дополнительные «комментарии» отбрасывают запрос и возвращают его ноль.

Так как я могу заставить страницу отображать без добавления дополнительных комментариев? Или есть лучший способ решить эту проблему?

Не уверен, имеет ли это значение или помогает, но route.rb для комментариев / блогов выглядит так ...

resources :blogs, :only => [:show] do
   resources :comments, :only => [:create]
end

Ответы [ 3 ]

3 голосов
/ 05 марта 2011

Я подключился к этому в течение последних нескольких недель, и я думаю, что наконец справился с этим, ошибки / правильное направление при рендеринге, заполненные поля остаются заполненными и все. Я рассматривал AJAX, однако я бы предпочел сделать это с постепенным ухудшением, если это вообще возможно.

Кроме того, я признаю, что мне пришлось пойти по этому очень дурацкому пути, в том числе использовать метод множественного преобразования родительской модели для отображения действия показа соответствующего типа контента, и на этом этапе мне нужен код, чтобы просто работа, не обязательно выглядит красиво, делая это.

Я ЗНАЮ, что его можно реорганизовать намного лучше, и я надеюсь сделать это, когда мне станет лучше с Rails. Или любой, кто думает, что может улучшить это, приветствуется. Во всяком случае, вот весь мой код, просто хотел поделиться и надеюсь, что это поможет кому-то в том же сценарии.

comments_controller.rb

class CommentsController < ApplicationController
    # this include will bring all the Text Helper methods into your Controller
    include ActionView::Helpers::TextHelper

    def create
        @commentable = find_commentable
        @comment = @commentable.comments.build(params[:comment])

        respond_to do |format|
            if @comment.save
                format.html { redirect_to(@commentable, :notice => 'Comment was successfully created.') }
            else

                # Transform class of commentable into pluralized content type
                content_type = find_commentable.class.to_s.downcase.pluralize

                # Choose appropriate instance variable based on @commentable, rendered page won't work without it
                if content_type == 'blogs'
                    @blog = @commentable
                elsif content_type == 'articles'
                    @article = @commentable
                end

                format.html { render "#{content_type}/show" }
                format.xml  { render :xml => @commentable.errors, :status => :unprocessable_entity }
            end
        end
    end 

    private

    # Gets the ID/type of parent model, see Comment#create in controller
    def find_commentable
        params.each do |name, value|
            if name =~ /(.+)_id$/
                return $1.classify.constantize.find(value)
            end
        end
    end
end

articles_controller.rb

class ArticlesController < ApplicationController

  def show
    @article = Article.where(:status => 1).find_by_cached_slug(params[:id])
    @comment = Comment.new

    # On another content type like blogs_controller.rb, replace with appropriate instance variable
    @content = @article

    respond_to do |format|
      format.html # show.html.erb
      format.xml  { render :xml => @article }
    end
  end

end

show.html.erb для статей (измените соответствующие переменные для блога и т. Д.)

<h1><%= @article.title %></h1>

<%= @article.body.html_safe %>

<%= render :partial => 'shared/comments', :locals => { :commentable => @article } %>

shared / _comments.html.erb (здесь я упускаю отображение опубликованных комментариев для упрощения, просто показываю форму для их отправки)

<%= form_for([commentable, @comment]) do |f| %>
    <h3>Post a new comment</h3>

    <%= render :partial => 'shared/errors', :locals => { :content => @comment } %>  

    <div class="field">
        <%= f.label :name, :value => params[:name] %>
        <%= f.text_field :name, :class => 'textfield' %>
    </div>

    <div class="field">
        <%= f.label :mail, :value => params[:mail] %>
        <%= f.text_field :mail, :class => 'textfield'  %>
    </div>

    <div class="field">
        <%= f.text_area :body, :rows => 10, :class => 'textarea full', :value => params[:body] %>
    </div>

    <%= f.submit :class => 'button blue' %>
<% end %>

shared / _errors.html.erb (я реорганизовал это как частичное повторное использование для статей, блогов, комментариев и т. Д., Но это просто стандартный код ошибки)

<% if content.errors.any? %>
    <div class="flash error">
        <p><strong><%= pluralize(content.errors.count, "error") %> prohibited this page from being saved:</strong></p>
        <ul>
            <% content.errors.full_messages.each do |msg| %>
                <li><%= msg %></li>
            <% end %>
        </ul>
    </div>
<% end %>
1 голос
/ 21 января 2013

Я слегка переработал ответ @Shannon, чтобы сделать его более динамичным. В моем методе 'find_parent' я беру путь URL и получаю имя контроллера. В методе 'create' я создаю 'instance_variable_set', который создает динамическую переменную либо для Articles (@article), либо для Blogs (@blog), либо чем бы то ни было.

Надеюсь, тебе понравится то, что я сделал? Пожалуйста, дайте мне знать, если у вас есть какие-либо сомнения или что-то можно улучшить?

def create
    @comment = @commentable.comments.new(params[:comment])
    if @comment.save
        redirect_to @commentable, notice: "Comment created."
    else
        content_type = find_parent
        instance_variable_set "@#{content_type.singularize}".to_sym, @commentable
        @comments = @commentable.comments

        render "#{content_type}/show"
    end
end

def find_parent
    resource = request.path.split('/')[1]
    return resource.downcase
end
0 голосов
/ 17 февраля 2011

Вы получаете сообщение об ошибке, поскольку представление blogs/show, скорее всего, относится к объекту @blog, которого нет при отображении в контроллере комментариев.

Вы должны вернуться к использованию redirect_to вместо рендеринга. Когда вы делали недопустимый комментарий, он не отображал вспышку, потому что вы не говорили ему установить вспышку, если комментарий не был сохранен. Вспышка будет сохраняться до следующего запроса.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...