【问题标题】:Self-Referential Association has a routing error自引用关联出现路由错误
【发布时间】:2010-10-22 15:14:41
【问题描述】:

我正在关注 Ryan Bate 的教程:http://railscasts.com/episodes/163-self-referential-association

但我的设置略有不同。

我正在制作自引用的 cmets,以便可以评论 cmets。

表单显示在视图中,但是当我提交时,我得到了这个:

Routing Error

No route matches "/conversations"

我的网址是这样写的:http://localhost:3000/conversations?convo_id=1

模型

#conversation.rb
belongs_to :comment
belongs_to :convo, :class_name => "Comment"

#comment.rb
belongs_to  :post
has_many :conversations
has_many :convos, :through => :conversations

我的表格:

- for comment in @comments
  .grid_7.post.alpha.omega
    = comment.text
    %br/
    - form_for comment, :url => conversations_path(:convo_id => comment), :method => :post do |f|
      = f.label 'Comment'
      %br/
      = f.text_area :text
      %br/
      = f.submit 'Submit'

我的对话控制器:

def create
  @conversation = comment.conversations.build(:convo_id => params[:convo_id])

应用程序在此处创建失败,因为它永远不会到达create method 的重定向部分。

【问题讨论】:

    标签: ruby-on-rails forms ruby-on-rails-3 model associations


    【解决方案1】:

    这里有几部分需要解决,但好消息是我认为您正在寻找的答案比您已有的要简单。如果我理解正确,您希望 cmets 拥有许多自己的子 cmets。这就是 YouTube 的工作方式,让会员回复现有的 cmets。为此,您不需要已实施的has_many :through 解决方案。您根本不需要对话对象。一条评论可能有很多回复(子 cmets),但一条回复不会有多个父级。

    解决办法是使用多态性,它比发音更容易实现:) 您希望您的 cmets 要么属于某个帖子,要么属于另一个评论。多态性让一个对象属于可能的许多事物之一。事实上,cmets 是最常见的用途。

    我在这篇博文中以地址示例来介绍多态性:

    http://kconrails.com/2010/10/19/common-addresses-using-polymorphism-and-nested-attributes-in-rails/

    但我可以更具体地向您展示它如何适用于您的案例。首先,完全放弃对话模型/控制器/路由。然后,更改您的 cmets 表:

    change_table :comments do |t|
      t.integer :commentable_id
      t.string  :commentable_type
    
      t.remove :post_id
    end
    

    我们不再需要post_id,因为我们将改变我们与其他表的关联方式。现在让我们更改模型:

    # app/models/post.rb
    has_many :comments, :as => :commentable
    
    # app/models/comment.rb
    belongs_to :commentable, :polymorphic => true
    has_many :comments, :as => :commentable
    

    请注意,我们直接删除了属于某个帖子的评论。相反,它连接到多态的“可评论”关联。现在你对拥有 cmets 的 cmets 拥有无限的深度。

    现在在您的 Post#show 操作中,您需要像这样创建一个空白评论:

    get show
      @post = Post.find(params[:id])
      @comment = @post.comments.build
    end
    

    @comment 现在将自动为您设置commentable_idcommentable_type。现在在您的显示页面中,使用 erb:

    <% form_for @comment do |f| %>
      <%= f.hidden_field :commentable_type %>
      <%= f.hidden_field :commentable_id %>
    
      /* other fields go here */
    <% end %>
    

    现在,当调用 Comments#create 时,它​​会像您期望的那样工作,并附加到正确的父级。上面的示例显示了直接将评论添加到帖子中,但评论的过程基本相同。在控制器中调用@comment.comments.build,表单本身将保持不变。

    我希望这会有所帮助!

    【讨论】:

    • 太棒了。你的提示让我看到了 Ryan Bates 的多态截屏视频。除了能够对 cme​​ts 发表评论之外,我能够设置 几乎 一切。我正在使用他的 find_commentables 方法,它适用于页面的父对象,在这种情况下是 Post,我无法弄清楚如何为 cmets 编写 form_for
    • 是的,这部分有点棘手,因为用户界面需要干净。您不能真正在每条评论之后显示“新评论”表单,因此您需要将用户发送到新页面(可以使用上面的表单代码),或者使用 AJAX 内联显示表单。
    • 我很好奇 Reddit 如何实现同样的目标。我可能可以通过ajax来完成它。但是必须有一种方法可以为每个表单设置一个唯一的 id。 ;)
    【解决方案2】:

    应用程序失败的速度比您想象的要快 - 它没有找到执行该操作的路径,因此根本没有到达该操作。在您的 routes.rb 文件中,您需要添加:

    # rails 3
    resources :conversations
    
    # rails 2
    map.resources :conversations
    

    这应该可以解决它。

    【讨论】:

    • 它在我的 routes.rb 中。但可能会发生这种情况,因为此表单位于 Post#show 中。所以也许我需要某种嵌套路线?
    • 我刚刚添加了一个不同的答案,希望能解决您如何建模数据的更大问题。这应该可以简化事情,让它们为你工作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-26
    • 1970-01-01
    • 2012-01-28
    相关资源
    最近更新 更多