【问题标题】:Polymorphic comments, how to render when comment validation fails?多态评论,评论验证失败时如何渲染?
【发布时间】:2009-09-06 20:06:33
【问题描述】:

我创建了一个应用程序,该应用程序具有多个模型(例如 A、B),这些模型以多态方式与 Comment 模型相关联。当查看与 A 控制器关联的页面时,与 A 对象关联的 show action、cmets 显示为创建新对象的表单。所有这些都有效,类似于 Ryan Bates 在 rails 网站上发布的 15 分钟博客。但是,如果我添加验证以确保用户不会提交空白评论,我不确定如何呈现它。这是我的评论控制器中的内容:

before_filter :load_resources, :only => [:create]
def create
  if @comment.save
    redirect_to @back
  else
    render @action
  end
end

private

def load_resources
  @comment = Comment.new(params[:comment])
  case @comment.commentable_type
  when 'A'
    @a = A.find(params[:a_id]
    @comments = @a.comments
    @back = a_url(@comment.commentable_id)
    @resource = @a
    @action = 'as/show'
  when 'B'
    ...
  end
end

查看 cmets 和表单的部分内容(使用 Haml):

=render :partial => 'comments/comment', :collection => @comments

%h3 Leave a comment:
-form_for [@resource, Comment.new] do |f|
  =f.error_messages
  =f.hidden_field :commentable_type, :value => params[:controller].singularize.titleize
  =f.hidden_field :commentable_id, :value => params[:id]
  =f.hidden_field :editor_id, :value  => @current_user.id
  =f.hidden_field :creator_id, :value => @current_user.id
%fieldset
  =f.label :subject, 'Subject', :class => 'block'
  =f.text_field :subject, :class => 'block'
  =f.label :text, 'Comment', :class => 'block'
  =f.text_area :text, :class => 'block'
  .clear_thick
 =f.submit 'Submit', :id => 'submit'

我似乎可以弄清楚如何处理验证错误。当触发验证错误时,它似乎不会触发 f.error_messages。此外,当触发渲染时,它会将用户带到具有以下 url 的页面:a/2/cmets,当我希望它渲染 a/2 时。

最新解决方案:

def create
  subject = ""
  if !@comment.save
    subject = "?subject=#{@comment.subject}"
  end
  redirect_to @back + subject
end

然后在A控制器中显示动作:

if params.has_key?('subject')
  @comment = Comment.create(:subject => params[:subject])
else
  @comment = Comment.new
end

这可行,但感觉有点难看......

【问题讨论】:

    标签: ruby-on-rails


    【解决方案1】:

    有点难以理解它,因为你不知道你将在 cmets 控制器中接收什么样的对象。

    当它不是多态关系时,它会简单得多。在我们了解如何做到这一点之前,我们需要了解实现单数版本的最佳方式。

    我应该注意,这是假设您正确定义了资源/路线:

    map.resources :posts, :has_many => [ :cmets ] map.resources :pages, :has_many => [ :cmets ]

    假设我们有一个 Post 有很多 Comments 的简单示例。这是执行此操作的示例方法:

    class CommentsController < ApplicationController
      before_filter => :fetch_post 
    
      def create
        @comment = @post.comments.new(params[:comment])
    
        if @comment.save
          success_message_here
          redirect post_path(@post)
        else
          error_message_here
          redirect_to post_path(@post)
        end
      end
    
      protected
        def fetch_post
          @post = Post.find(params[:post_id])
        end
    end
    

    现在我们想在多态关系中使用它,所以我们必须设置一些东西。假设我们现在有具有 cmets 的 Pages 和 Posts。这是执行此操作的示例方法:

    来自您的帖子和页面显示页面:

    <%= render 'comments/new' %>
    

    在帖子控制器中:

    before_filter :fetch_post
    
        def show
          @comment = @commentable.comments.build
        end
    
        protected
          def fetch_post
            @post = @commentable = Post.find(params[:id])
          end
    

    这会将您的表单设置为简单:

    <% form_for [ @commentable, @comment ] do |f| %>
      #Your form fields here (DO NOT include commentable_type and or commentable_id also don't include editor and creator id's here either. They will created in the controller.)
    <% end %>
    

    在您的 cmets 控制器中:

    def create
      @commentable = find_commentable
      # Not sure what the relationship between the base parent and the creator and editor are so I'm going to merge in params in a hacky way
      @comment = @commentable.comments.build(params[:comment]).merge({:creator => current_user, :editor => current_user})
    
      if @comment.save
        success message here
        redirect_to url_for(@commentable)
      else
        failure message here
        render :controller => @commentable.class.downcase.pluralize, :action => :show
      end
    end
    
      protected
        def find_commentable
          params.each do |name, value|
            if name =~ /(.+)_id$/
              return $1.classify.constantize.find(value)
            end
          end
          nil
        end
    

    【讨论】:

    • 非常好的解决方案。出于好奇,为什么不直接在表单中包含 creator 和 editor_id 呢?在我看来,这是一种降低控制器复杂性的简单方法。话虽如此,视图应该比控制器更干净吗?
    • LDK,一般规则是您不应该信任用户输入。即使有隐藏字段,恶意用户也可以将他们想要的任何 ID 注入表单,然后将其传递,从而冒充另一个用户。通过删除该选项,您可以让应用更加安全。
    猜你喜欢
    • 2021-03-13
    • 1970-01-01
    • 2013-05-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-16
    • 1970-01-01
    相关资源
    最近更新 更多