2009-09-06 106 views
2

我创建了一个应用程序,该应用程序具有多个与评论模型呈多态关联的模型(称为A,B)。当查看与A控制器关联的页面时,显示动作,与A对象关联的注释将以创建新对象的形式显示。所有这些工作都与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 

查看局部征求意见和形式(使用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/comments,当我希望渲染一个/ 2时。

最新的解决方案:

def create 
    subject = "" 
    if [email protected] 
    subject = "?subject=#{@comment.subject}" 
    end 
    redirect_to @back + subject 
end 

然后在控制器显示行动:

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

这工作,但感觉那种难看......

回答

3

这是一种难以包裹因为你不知道你将会在评论控制器中接收什么样的对象。

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

我要指出,这是假定你有你的资源/路线正确定义:

map.resources:帖子:的has_many => [:评论] map.resources:页面:的has_many => [ :评论]

比方说,我们有一个简单的例子,一个职位有很多评论。这里是一个这样做的示例:

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 

现在我们想要在多态关系中使用它,所以我们必须设置几件事情。假设我们有现在有评论的网页和帖子。下面是这样的一个样本的方式:

从您的文章和网页页面中显示:

<%= render 'comments/new' %> 

在posts控制器:

before_filter :fetch_post 

    def show 
     @comment = @commentable.comments.build 
    end 

    protected 
     def fetch_post 
     @post = @commentable = Post.find(params[:id]) 
     end 

这将设置你的形式很简单: <%error_messsages_for:comment%>

<% 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 %> 

在你身上[R评论控制器:

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 
+0

很不错的解决方案。出于好奇,为什么不直接在表单中包含创作者和editor_id?在我看来,这是降低控制器复杂度的简单方法。有了这个说法,视图应该比控制器更清洁吗? – LDK 2009-09-20 00:20:02

+0

赛维LDK, 一般规则是你不应该信任用户输入。即使有隐藏字段,恶意用户也可以将任何想要的ID注入表单中,然后将其传递,然后模拟另一个用户。 通过删除该选项,您可以使应用程序更安全。 – Carlos 2009-09-21 20:27:38