Creating Nested Models in a Rails 4 Application

Hi, I am making a Forum application in Rails 4. It can have many forums, each of which has many topics. Each topic can have many posts. When creating a new topic, you must also create an initial entry, such as Stack Overflow. Therefore, I have a text area in the form of a "New Theme" that allows this using the method fields_for. The problem is , when you click the "Create Topic" button after filling out the form (including the "post" field), the transaction is rolled back. The following validation error will appear:

3 errors forbade the preservation of this topic:

  • There must be a message forum
  • Posts must exist
  • A message user must exist

This is my form: app / views / themes / _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 %>

Models: 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

Controller for themes, application / controllers / themes_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

As you can see, I added posts_attributesto the strong parameters for the topic . These are the only fields in which messages are in addition to foreign key references ( :forum_id, :topic_id, :user_id). And I tried to include these attributes, but I get the same error.

Finally, this is my 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

It should also be mentioned that I tried to add hidden_fieldsinternally fields_for, with criteria idfor @forum, @topic and current_user. This causes the same validation error.

? , - . . , , . Rails <= 3, - _.

? !

EDIT , " - " " - "...

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" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
  User Load (0.6ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
   (0.3ms)  BEGIN
  CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
   (0.4ms)  ROLLBACK
+4
1

; .

routes , :

1 ...

resources :x do
  resources :y
end

-

, , scope:

#config/routes.rb
scope ':forum' do
   resources :topics do
      resources :posts
   end
end

, , , , .

, CRUD forums :

#config/routes.rb
resources :forums #-> only accessible to admins?
scope ...

forum present:

<%= link_to "Test", [@forum, @topic, @post] %>
+1

Source: https://habr.com/ru/post/1626747/


All Articles