Rails 3中如何通过子控制器创建父模型? (属于协会)

发布于 2024-11-05 00:33:19 字数 1459 浏览 3 评论 0原文

我有两个资源:主题和帖子。 我试图弄清楚如何通过 Post 控制器创建一个主题。 模型如下所示:

class Topic < ActiveRecord::Base
  has_many :posts, :dependent => :destroy
  validates :name, :presence => true,
                   :length => { :maximum => 32 }
  attr_accessible  :name
end

class Post < ActiveRecord::Base
  belongs_to :topic,    :touch => true
  has_many   :comments, :dependent => :destroy
  accepts_nested_attributes_for :topic
  attr_accessible :name, :title, :content, :topic
end

posts/_form.html.erb:

<%= simple_form_for @post do |f| %>
  <h1>Create a Post</h1>
  <%= f.input :name, :label => false, :placeholder => "Name" %>
  <%= f.input :title, :label => false, :placeholder => "Title" %>
  <%= f.input :content, :label => false, :placeholder => "Content" %>
  <%= f.input :topic, :label => false, :placeholder => "Topic" %>  
  <%= f.button :submit, "Post" %>  
<% end %>

posts_controller.rb#create:

def create
  @post = Post.new(params[:topic])
  respond_to do |format|
    if @post.save
      format.html { redirect_to(@post, :notice => 'Post was successfully created.') }
    else
      format.html { render :action => "new" }
    end
  end
end

使用 posts/_form.html.erb 我可以创建帖子,但不会随之创建关联的主题。谁能告诉我为什么会出现这种行为以及如何纠正它?我正在使用 Ruby 1.9.2、Rails 3.0.7 和 simple_form gem。

I have two resources: Topics and Posts.
I am trying to figure out how I can create a Topic through the Post controller.
The models look like this:

class Topic < ActiveRecord::Base
  has_many :posts, :dependent => :destroy
  validates :name, :presence => true,
                   :length => { :maximum => 32 }
  attr_accessible  :name
end

class Post < ActiveRecord::Base
  belongs_to :topic,    :touch => true
  has_many   :comments, :dependent => :destroy
  accepts_nested_attributes_for :topic
  attr_accessible :name, :title, :content, :topic
end

posts/_form.html.erb:

<%= simple_form_for @post do |f| %>
  <h1>Create a Post</h1>
  <%= f.input :name, :label => false, :placeholder => "Name" %>
  <%= f.input :title, :label => false, :placeholder => "Title" %>
  <%= f.input :content, :label => false, :placeholder => "Content" %>
  <%= f.input :topic, :label => false, :placeholder => "Topic" %>  
  <%= f.button :submit, "Post" %>  
<% end %>

posts_controller.rb#create:

def create
  @post = Post.new(params[:topic])
  respond_to do |format|
    if @post.save
      format.html { redirect_to(@post, :notice => 'Post was successfully created.') }
    else
      format.html { render :action => "new" }
    end
  end
end

With posts/_form.html.erb I can create posts, but an associated topic is not created along with it. Can anyone tell me why I get this behavior and possibly how to correct it? I'm using Ruby 1.9.2, Rails 3.0.7 and the simple_form gem.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

深陷 2024-11-12 00:33:19

Railscasts 有几个关于这个问题的剧集。
第16集(需订阅)

源代码:
https://github.com/railscasts/016-virtual-attributes-revised

还有这一集
http://railscasts.com/episodes/57-create-model-through -text-field

views/products/_form.rhtml

<p>
<label for="product_category_id">Category:</label><br />
<%= f.collection_select :category_id, Category.find(:all), :id, :name, :prompt => "Select a Category" %>
or create one:
<%= f.text_field :new_category_name %>
</p>

models/product.rb

belongs_to :category
attr_accessor :new_category_name
before_save :create_category_from_name

def create_category_from_name
create_category(:name => new_category_name) unless new_category_name.blank?
end

我认为 Ryan 在类别上的解决方案比 @nathanvda 的选项 1 更优雅。因为它在模型而不是控制器中处理数据。如果您需要在不同的控制器/操作中执行相同的工作,您将看到好处。

Railscasts has several episodes about this problem.
episode 16(subscription needed)

Source code:
https://github.com/railscasts/016-virtual-attributes-revised

And this episode
http://railscasts.com/episodes/57-create-model-through-text-field

views/products/_form.rhtml

<p>
<label for="product_category_id">Category:</label><br />
<%= f.collection_select :category_id, Category.find(:all), :id, :name, :prompt => "Select a Category" %>
or create one:
<%= f.text_field :new_category_name %>
</p>

models/product.rb

belongs_to :category
attr_accessor :new_category_name
before_save :create_category_from_name

def create_category_from_name
create_category(:name => new_category_name) unless new_category_name.blank?
end

I think Ryan's solution on category is more elegant than @nathanvda 's option 1. As it deal the data in Model instead of Controller. If you need to do same work in different controllers/actions, you will see the benefits.

紫罗兰の梦幻 2024-11-12 00:33:19

根据你想做什么,我可以看到两种选择。

选项 1:使用文本框创建或查找现有主题(如您所用)。在你的控制器中你可以写这样的东西:

def create
  topic_name = params[:post].delete(:topic)
  @topic = Topic.find_or_create_by_name(topic_name)
  @post = Post.new(params[:post])
  @post.topic = @topic
  if @post.save
    format.html { redirect_to(@post, :notice => 'Post was successfully created.') }
  else
    format.html { render :action => "new" }
  end
end

这是快速而肮脏的方式。对于您输入的每个主题,它将尝试按名称查找该主题,或者创建它并分配它。但是,这很容易出错。如果您的主题集有限,还有一种更简单的方法。

选项 2:使用选择框,即可用主题的列表。在您的视图中写入:

<%= simple_form_for @post do |f| %>
  <h1>Create a Post</h1>
  <%= f.input :name, :label => false, :placeholder => "Name" %>
  <%= f.input :title, :label => false, :placeholder => "Title" %>
  <%= f.input :content, :label => false, :placeholder => "Content" %>
  <%= f.association :topic %>  
  <%= f.button :submit, "Post" %>  
<% end %>

这将呈现一个包含可能主题的选择框。在您的控制器中,您只需编写:

def create
  @post = Post.new(params[:post])
  if @post.save
    format.html { redirect_to(@post, :notice => 'Post was successfully created.') }
  else
    format.html { render :action => "new" }
  end
end

虽然第二个选项非常简单,但动态添加主题却不太容易。您可以使用自动完成字段在两者之间执行某些操作,该字段将允许查找值(如果存在)或添加新值(如果不存在)。

希望这有帮助。

Depending on what you want to do, i can see two options.

Option 1: Use a text-box to create or find an existing topic (as you had). In your controller you would write something like:

def create
  topic_name = params[:post].delete(:topic)
  @topic = Topic.find_or_create_by_name(topic_name)
  @post = Post.new(params[:post])
  @post.topic = @topic
  if @post.save
    format.html { redirect_to(@post, :notice => 'Post was successfully created.') }
  else
    format.html { render :action => "new" }
  end
end

That is the quick and dirty way. It will, for each topic you type, try to find that topic, by name, or create it and assign it. But, this is error-prone. If your sets of topics is limited, there is a much easier way.

Option 2: use a select-box, a list of available topics. In your view write:

<%= simple_form_for @post do |f| %>
  <h1>Create a Post</h1>
  <%= f.input :name, :label => false, :placeholder => "Name" %>
  <%= f.input :title, :label => false, :placeholder => "Title" %>
  <%= f.input :content, :label => false, :placeholder => "Content" %>
  <%= f.association :topic %>  
  <%= f.button :submit, "Post" %>  
<% end %>

That will render a select-box with the possible topics. And in your controller you just have to write:

def create
  @post = Post.new(params[:post])
  if @post.save
    format.html { redirect_to(@post, :notice => 'Post was successfully created.') }
  else
    format.html { render :action => "new" }
  end
end

While this second option is really easy, it is less easy to add topics on the fly. You could do something in between, using an autocomplete field, that will either allow looking up values if they exist, or add new values if they don exist.

Hope this helps.

陌上芳菲 2024-11-12 00:33:19

您是否在服务器日志中收到批量分配错误?您可能需要将 :topic_attributes 添加到 attr_accessible 列表中。

Are you getting mass assignment errors in the server log? You may need to add :topic_attributes to your attr_accessible list.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文