在 Rails 4 论坛应用程序中创建嵌套模型
Creating nested models in Rails 4 forum app
您好,我在Rails 4 中做一个论坛应用。它可以有很多论坛,每个论坛都有很多主题。每个主题可以有很多 post。创建新主题时,还必须创建初始 post,就像 Stack Overflow 本身一样。因此,我有一个 "New Topic" 形式的文本区域,允许使用 fields_for
方法。 问题是,当您在填写表格(包括"post"字段)后单击"Create Topic"按钮时,事务被回滚。出现以下验证错误:
3 errors prohibited this topic from being saved:
- Posts forum must exist
- Posts topic must exist
- Posts user must exist
这是我的表格:app/views/topics/_form.html.erb
<%= form_for([ @forum, topic ]) do |f| %>
<% if topic.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(topic.errors.count, "error") %> prohibited this topic from being saved:</h2>
<ul>
<% topic.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :title %><br>
<%= f.text_field :title %>
</div>
<div class="field">
<%= f.fields_for :posts do |builder| %>
<%= builder.label :content %><br>
<%= builder.cktext_area :content, class: 'ckeditor' %>
<% end %>
</div>
<div class="actions">
<%= f.submit 'Create Topic', class: "btn btn-l btn-success" %>
</div>
<% end %>
型号:forum.rb
class Forum < ApplicationRecord
has_many :topics, dependent: :destroy
has_many :posts, through: :topics
def most_recent_post
topic = Topic.last
return topic
end
end
topic.rb
class Topic < ApplicationRecord
belongs_to :forum
belongs_to :user
has_many :posts, dependent: :destroy
accepts_nested_attributes_for :posts
end
post.rb
class Post < ApplicationRecord
belongs_to :forum
belongs_to :topic
belongs_to :user
validates :content, presence: true
end
主题控制器,app/controllers/topics_controller.rb
class TopicsController < ApplicationController
before_action :get_forum
before_action :set_topic, only: [:show, :edit, :update, :destroy]
# GET /topics
# GET /topics.json
def index
@topics = @forum.topics
end
# GET /topics/1
# GET /topics/1.json
def show
end
# GET /topics/new
def new
@topic = @forum.topics.build
@topic.posts.build
end
# GET /topics/1/edit
def edit
# @topic.posts.build
end
# POST /topics
# POST /topics.json
def create
@topic = @forum.topics.build(topic_params.merge(user_id: current_user.id))
@topic.last_poster_id = @topic.user_id
respond_to do |format|
if @topic.save
format.html { redirect_to forum_topic_path(@forum, @topic), notice: 'Topic was successfully created.' }
format.json { render :show, status: :created, location: @topic }
else
format.html { render :new }
format.json { render json: @topic.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /topics/1
# PATCH/PUT /topics/1.json
def update
respond_to do |format|
if @topic.update(topic_params)
format.html { redirect_to forum_topic_path(@forum, @topic), notice: 'Topic was successfully updated.' }
format.json { render :show, status: :ok, location: @topic }
else
format.html { render :edit }
format.json { render json: @topic.errors, status: :unprocessable_entity }
end
end
end
# DELETE /topics/1
# DELETE /topics/1.json
def destroy
@topic.destroy
respond_to do |format|
format.html { redirect_to forum_path(@forum), notice: 'Topic was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def get_forum
@forum = Forum.find(params[:forum_id])
end
def set_topic
@topic = Topic.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def topic_params
params.require(:topic).permit(:title, :last_poster_id, :last_post_at, :tags, :forum_id, :user_id, posts_attributes: [:id, :content])
end
end
如您所见,我已将 posts_attributes
添加到 topic 的强参数中。这些是 posts 除了外键引用 (:forum_id, :topic_id, :user_id
) 之外仅有的字段。我试过将这些属性放入,但我得到了同样的错误。
最后,这是我的routes.rb
Rails.application.routes.draw do
resources :forums do
resources :topics do
resources :posts
end
end
resources :sessions
resources :users
mount Ckeditor::Engine => '/ckeditor'
end
我还应该提到,我已经尝试在 fields_for
中添加 hidden_fields
,并使用 @forum、@topic 和 current_user 的 id
标准。这会引发相同的验证错误。
我错过了什么?我觉得这是控制器中的东西。就像我没有正确保存它一样。我见过的每个教程都是这样的。除了 Rails <=3 版本,它们因没有 strong_params 而有很大不同。
有什么想法吗?感谢您的帮助!
EDIT 这是我尝试提交标题为 "I am a title" 且内容为 "I am some content"...
的日志输出
Started POST "/forums/1/topics" for 127.0.0.1 at 2016-01-31 09:03:33 -0500
Processing by TopicsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"pYt842XQHiOKqNjPHBO8lNP2z92gHF7Lpt24CppbuvHR/cFHky3FVCpBs77p7WFRKmYBHgeZQjx0sE+DI+Q+sQ==", "topic"=>{"title"=>"I am a title", "posts_attributes"=>{"0"=>{"content"=>"<p>I am some content</p>\r\n"}}}, "commit"=>"Create Topic", "forum_id"=>"1"}
Forum Load (0.6ms) SELECT "forums".* FROM "forums" WHERE "forums"."id" = LIMIT [["id", 1], ["LIMIT", 1]]
User Load (0.6ms) SELECT "users".* FROM "users" WHERE "users"."id" = LIMIT [["id", 1], ["LIMIT", 1]]
(0.3ms) BEGIN
CACHE (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = LIMIT [["id", 1], ["LIMIT", 1]]
(0.4ms) ROLLBACK
这不是一个直接的答案;评论太长了。
您 routes
的一个问题是您 nesting too many resources:
Resources should never be nested more than 1 level deep...
resources :x do
resources :y
end
--
尽管您可以 做您正在做的事情,但使用 scope
:
可能会更好
#config/routes.rb
scope ':forum' do
resources :topics do
resources :posts
end
end
您面临的问题是事情会变得非常复杂、非常迅速。虽然
通过这种方式,您可以使 forums
CRUD 在其自己的一组功能中可访问:
#config/routes.rb
resources :forums #-> only accessible to admins?
scope ...
无论哪种方式,您仍然需要使用存在的 forum
来定义您的路线:
<%= link_to "Test", [@forum, @topic, @post] %>
您好,我在Rails 4 中做一个论坛应用。它可以有很多论坛,每个论坛都有很多主题。每个主题可以有很多 post。创建新主题时,还必须创建初始 post,就像 Stack Overflow 本身一样。因此,我有一个 "New Topic" 形式的文本区域,允许使用 fields_for
方法。 问题是,当您在填写表格(包括"post"字段)后单击"Create Topic"按钮时,事务被回滚。出现以下验证错误:
3 errors prohibited this topic from being saved:
- Posts forum must exist
- Posts topic must exist
- Posts user must exist
这是我的表格:app/views/topics/_form.html.erb
<%= form_for([ @forum, topic ]) do |f| %>
<% if topic.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(topic.errors.count, "error") %> prohibited this topic from being saved:</h2>
<ul>
<% topic.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :title %><br>
<%= f.text_field :title %>
</div>
<div class="field">
<%= f.fields_for :posts do |builder| %>
<%= builder.label :content %><br>
<%= builder.cktext_area :content, class: 'ckeditor' %>
<% end %>
</div>
<div class="actions">
<%= f.submit 'Create Topic', class: "btn btn-l btn-success" %>
</div>
<% end %>
型号:forum.rb
class Forum < ApplicationRecord
has_many :topics, dependent: :destroy
has_many :posts, through: :topics
def most_recent_post
topic = Topic.last
return topic
end
end
topic.rb
class Topic < ApplicationRecord
belongs_to :forum
belongs_to :user
has_many :posts, dependent: :destroy
accepts_nested_attributes_for :posts
end
post.rb
class Post < ApplicationRecord
belongs_to :forum
belongs_to :topic
belongs_to :user
validates :content, presence: true
end
主题控制器,app/controllers/topics_controller.rb
class TopicsController < ApplicationController
before_action :get_forum
before_action :set_topic, only: [:show, :edit, :update, :destroy]
# GET /topics
# GET /topics.json
def index
@topics = @forum.topics
end
# GET /topics/1
# GET /topics/1.json
def show
end
# GET /topics/new
def new
@topic = @forum.topics.build
@topic.posts.build
end
# GET /topics/1/edit
def edit
# @topic.posts.build
end
# POST /topics
# POST /topics.json
def create
@topic = @forum.topics.build(topic_params.merge(user_id: current_user.id))
@topic.last_poster_id = @topic.user_id
respond_to do |format|
if @topic.save
format.html { redirect_to forum_topic_path(@forum, @topic), notice: 'Topic was successfully created.' }
format.json { render :show, status: :created, location: @topic }
else
format.html { render :new }
format.json { render json: @topic.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /topics/1
# PATCH/PUT /topics/1.json
def update
respond_to do |format|
if @topic.update(topic_params)
format.html { redirect_to forum_topic_path(@forum, @topic), notice: 'Topic was successfully updated.' }
format.json { render :show, status: :ok, location: @topic }
else
format.html { render :edit }
format.json { render json: @topic.errors, status: :unprocessable_entity }
end
end
end
# DELETE /topics/1
# DELETE /topics/1.json
def destroy
@topic.destroy
respond_to do |format|
format.html { redirect_to forum_path(@forum), notice: 'Topic was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def get_forum
@forum = Forum.find(params[:forum_id])
end
def set_topic
@topic = Topic.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def topic_params
params.require(:topic).permit(:title, :last_poster_id, :last_post_at, :tags, :forum_id, :user_id, posts_attributes: [:id, :content])
end
end
如您所见,我已将 posts_attributes
添加到 topic 的强参数中。这些是 posts 除了外键引用 (:forum_id, :topic_id, :user_id
) 之外仅有的字段。我试过将这些属性放入,但我得到了同样的错误。
最后,这是我的routes.rb
Rails.application.routes.draw do
resources :forums do
resources :topics do
resources :posts
end
end
resources :sessions
resources :users
mount Ckeditor::Engine => '/ckeditor'
end
我还应该提到,我已经尝试在 fields_for
中添加 hidden_fields
,并使用 @forum、@topic 和 current_user 的 id
标准。这会引发相同的验证错误。
我错过了什么?我觉得这是控制器中的东西。就像我没有正确保存它一样。我见过的每个教程都是这样的。除了 Rails <=3 版本,它们因没有 strong_params 而有很大不同。
有什么想法吗?感谢您的帮助!
EDIT 这是我尝试提交标题为 "I am a title" 且内容为 "I am some content"...
的日志输出Started POST "/forums/1/topics" for 127.0.0.1 at 2016-01-31 09:03:33 -0500
Processing by TopicsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"pYt842XQHiOKqNjPHBO8lNP2z92gHF7Lpt24CppbuvHR/cFHky3FVCpBs77p7WFRKmYBHgeZQjx0sE+DI+Q+sQ==", "topic"=>{"title"=>"I am a title", "posts_attributes"=>{"0"=>{"content"=>"<p>I am some content</p>\r\n"}}}, "commit"=>"Create Topic", "forum_id"=>"1"}
Forum Load (0.6ms) SELECT "forums".* FROM "forums" WHERE "forums"."id" = LIMIT [["id", 1], ["LIMIT", 1]]
User Load (0.6ms) SELECT "users".* FROM "users" WHERE "users"."id" = LIMIT [["id", 1], ["LIMIT", 1]]
(0.3ms) BEGIN
CACHE (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = LIMIT [["id", 1], ["LIMIT", 1]]
(0.4ms) ROLLBACK
这不是一个直接的答案;评论太长了。
您 routes
的一个问题是您 nesting too many resources:
Resources should never be nested more than 1 level deep...
resources :x do resources :y end
--
尽管您可以 做您正在做的事情,但使用 scope
:
#config/routes.rb
scope ':forum' do
resources :topics do
resources :posts
end
end
您面临的问题是事情会变得非常复杂、非常迅速。虽然
通过这种方式,您可以使 forums
CRUD 在其自己的一组功能中可访问:
#config/routes.rb
resources :forums #-> only accessible to admins?
scope ...
无论哪种方式,您仍然需要使用存在的 forum
来定义您的路线:
<%= link_to "Test", [@forum, @topic, @post] %>