【问题标题】:Nested form inside of a nested form - Cocoon Gem: 3rd level child doesn't save - Rails 5嵌套表单内的嵌套表单 - Cocoon Gem:第 3 级子级不保存 - Rails 5
【发布时间】:2017-03-24 23:51:14
【问题描述】:

花了几个小时试图研究解决方案,发现了一些非常相似的问题like this onethis one,尽管所有建议的修复都不能解决我的问题:

尝试使用 Cocoon Gem 在嵌套表单内构建嵌套表单,但 3 级子级未保存到数据库中。

模型的结构非常简单,只有“has_many / belongs_to”关系:

一个文本有很多引号。一个 Quote 有许多 cmets。

实现中的动态 UI 交互有效,添加和删除字段有效,但不幸的是,只有文本和引号被保存,而不是 cmets。没有显示错误。

以下是表格:

_text_form.html.erb

<%= form_for(@text, html: { multipart: true }) do |f| %>
  <%= render 'shared/error_messages', object: f.object %>
  <div class="field">
    <%= f.text_field :title, placeholder: "Enter Title" %>      
  </div>
  <div>
      <h3>Quotes:</h3>
      <div id="quotes">
        <%= f.fields_for :quotes do |quote| %>
          <%= render 'quote_fields', f: quote %>
        <% end %>

        <div class="links">
          <%= link_to_add_association 'add quote', f, :quotes, class: "btn btn-default" %>
        </div>
      </div>
  </div>

  <%= f.submit %>
<% end %>

_quote_fields.html.erb

<div class="nested-fields">
  <%= f.text_area :content, placeholder: "Compose new quote..." %>
  <span class="picture">
    <%= f.file_field :picture, accept: 'image/jpeg,image/gif,image/png' %>
  </span>

  <div>
      <h3>Comments:</h3>
      <div id="comments">
        <%= f.fields_for :comments do |comment| %>
          <%= render 'comment_fields', f: comment %>
        <% end %>
        <div class="links">
          <%= link_to_add_association 'add comment', f, :comments, class: "btn btn-default" %>
        </div>
      </div>
  </div>
  <div class="links">
    <%= link_to_remove_association "remove quote", f, class: "btn btn-default" %>
  </div>
</div>

<script type="text/javascript">
  $('#quote_picture').bind('change', function() {
    var size_in_megabytes = this.files[0].size/1024/1024;
    if (size_in_megabytes > 2) {
      alert('Maximum file size is 2MB. Please choose a smaller file.');
    }
  });
</script>

_comment_fields.html.erb

<div class="nested-fields">
  <%= f.text_area :bodycomment, placeholder: "Write a comment..." %>
  <%= link_to_remove_association "remove comment", f, class: "btn btn-default" %>
</div>

以下是模型:

text.rb

class Text < ApplicationRecord
  belongs_to :user, inverse_of: :texts
  has_many :quotes, dependent: :destroy, inverse_of: :text
  has_many :comments, :through => :quotes
  accepts_nested_attributes_for :quotes, reject_if: :all_blank, allow_destroy: true
  accepts_nested_attributes_for :comments, reject_if: :all_blank, allow_destroy: true
  default_scope -> { order(created_at: :desc) }
  mount_uploader :coverimage, CoverimageUploader

  validates :user_id, presence: true
  validates :title, presence: true
  validate :coverimage_size

  private

      # Validates the size of an uploaded picture.
      def coverimage_size
        if coverimage.size > 5.megabytes
          errors.add(:coverimage, "should be less than 5MB")
        end
      end

end

quote.rb

class Quote < ApplicationRecord
  belongs_to :text, inverse_of: :quotes
  has_many :comments, dependent: :destroy, inverse_of: :quote
  accepts_nested_attributes_for :comments, reject_if: :all_blank, allow_destroy: true

  mount_uploader :picture, PictureUploader
  validates :content, presence: true, length: { maximum: 350 }
  validate :picture_size

private

  #Validates size of image upload
  def picture_size
    if picture.size > 2.megabytes
      errors.add(:picture, "should be less than 2MB")
    end
  end

end

comment.rb

class Comment < ApplicationRecord
  belongs_to :quote, inverse_of: :comments
  validates :quote_id, presence: true
  validates :bodycomment, presence: true

end

这里是控制器:

quotes_controller.rb

class QuotesController < ApplicationController
before_action :logged_in_user, only: [:create, :destroy]
before_action :correct_user, only: :destroy


  def show
  end



  def create
    @quote = current_user.quotes.build(quote_params)
   if @quote.save
     flash[:success] = "Quote created!"
     redirect_to root_url
   else
     @feed_items = []
     render 'static_pages/home'
   end
  end

  def destroy
    @quote.destroy
    flash[:success] = "Quote deleted"
    redirect_to request.referrer || root_url
  end


  private

     def quote_params
       params.require(:quote).permit(:content, :picture, comments_attributes: [:id, :bodycomment
         , :_destroy])
     end

     def correct_user
       @quote = current_user.quotes.find_by(id: params[:id])
       redirect_to root_url if @quote.nil?

     end

end

cmets_controller.rb

class CommentsController < ApplicationController
before_action :logged_in_user, only: [:create, :edit, :update, :destroy]
before_action :correct_user, only: :destroy


    def show
    end

    def create
        @comment = current_user.comments.build(comment_params)
        if @comment.save
          flash[:success] = "Comment created!"
          redirect_to root_url
        else
          @feed_items = []
          render 'static_pages/home'
        end
      end

    def destroy
      @comment.destroy
      flash[:success] = "Comment deleted"
      redirect_to request.referrer || root_url
    end

    private

    def comment_params
          params.require(:comment).permit(:bodycomment)
    end

    def correct_user
      @comment = current_user.comments.find_by(id: params[:id])
      redirect_to root_url if @comment.nil?

    end

end

想知道是不是一些javascript问题...

非常感谢您对此进行调查。非常感谢,感谢您的帮助。

编辑

这里是 texts_controller.rb

class TextsController < ApplicationController
  before_action :logged_in_user, only: [:create, :edit, :update, :destroy]
  before_action :correct_user, only: :destroy
  before_action :find_text, only: [:show, :edit, :update, :destroy]


  def show
  end

  def new
    @text = current_user.texts.build
  end

  def create
    @text = current_user.texts.build(text_params)
    if @text.save
     flash[:success] = "Text created!"
     render 'show'
    else
     render 'static_pages/home'
    end
  end


  def edit
  end

  def update
    if @text.update(text_params)
      redirect_to root_url
    else
      render 'edit'
    end
  end

  def destroy
    @text.destroy
    flash[:success] = "Text deleted"
    redirect_to request.referrer || root_url
  end

  private

    def text_params
    params.require(:text).permit(:url, :title, :coverimage,
                                  :publication, :author, :summary, quotes_attributes: [:id, :content, :picture, :_destroy], comments_attributes: [:id, :bodycomment, :_destroy])
    end

    def find_text
        @text = Text.find(params[:id])
      end

    def correct_user
      @user = User.find(params[:id])
      redirect_to(root_url) unless current_user?(@user)
    end

end

以下是我保存表单字段后得到的一些日志信息:

--- !ruby/object:ActionController::Parameters
parameters: !ruby/hash:ActiveSupport::HashWithIndifferentAccess
  utf8: "✓"
  authenticity_token: NQLh7TwlhfbV4ez91HGMyYZK6YYYiLXhHG/cAhrAsRylIAuFFhjnKX0vEO8ZIVbsxGES3byBgUMz21aSOlGiqw==
  text: !ruby/object:ActionController::Parameters
    parameters: !ruby/hash:ActiveSupport::HashWithIndifferentAccess
      title: Title of the Book
      quotes_attributes: !ruby/hash:ActiveSupport::HashWithIndifferentAccess
        '1490626822148': !ruby/hash:ActiveSupport::HashWithIndifferentAccess
          content: This is a quote from the book.
          _destroy: 'false'
          comments_attributes: !ruby/hash:ActiveSupport::HashWithIndifferentAccess
            '1490626833771': !ruby/hash:ActiveSupport::HashWithIndifferentAccess
              bodycomment: Here is a comment on the quote of the book.
              _destroy: 'false'
    permitted: false
  commit: Create Text
  controller: texts
  action: create
permitted: false 

这里是终端的日志文件:

Started POST "/texts" for ::1 at 2017-03-27 17:00:51 +0200
Processing by TextsController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"NQLh7TwlhfbV4ez91HGMyYZK6YYYiLXhHG/cAhrAsRylIAuFFhjnKX0vEO8ZIVbsxGES3byBgUMz21aSOlGiqw==", "text"=>{"title"=>"Title of the Book", "publication"=>"", "author"=>"", "url"=>"", "summary"=>"", "quotes_attributes"=>{"1490626822148"=>{"content"=>"This is a quote from the book.", "_destroy"=>"false", "comments_attributes"=>{"1490626833771"=>{"bodycomment"=>"Here is a comment on the quote of the book.", "_destroy"=>"false"}}}}}, "commit"=>"Create Text"}
  User Load (0.4ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
Unpermitted parameter: comments_attributes
   (0.1ms)  begin transaction
  SQL (0.7ms)  INSERT INTO "texts" ("user_id", "url", "title", "publication", "author", "summary", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?, ?, ?, ?)  [["user_id", 1], ["url", ""], ["title", "Title of the Book"], ["created_at", 2017-03-27 15:00:51 UTC], ["updated_at", 2017-03-27 15:00:51 UTC]]
  SQL (0.4ms)  INSERT INTO "quotes" ("content", "created_at", "updated_at", "text_id") VALUES (?, ?, ?, ?)  [["content", "This is a quote from the book."], ["created_at", 2017-03-27 15:00:51 UTC], ["updated_at", 2017-03-27 15:00:51 UTC], ["text_id", 366]]
   (1.3ms)  commit transaction
  Rendering texts/show.html.erb within layouts/application
  Quote Load (0.2ms)  SELECT "quotes".* FROM "quotes" WHERE "quotes"."text_id" = ?  [["text_id", 366]]
  Rendered texts/show.html.erb within layouts/application (5.8ms)
  Rendered layouts/_shim.html.erb (0.5ms)
  Rendered layouts/_header.html.erb (1.4ms)
  Rendered layouts/_footer.html.erb (1.8ms)
Completed 200 OK in 127ms (Views: 100.1ms | ActiveRecord: 3.1ms)

【问题讨论】:

  • 你能显示发布到控制器的内容吗? (您可以在日志文件中找到它)您能否显示来自TextsController 的强参数定义(cmets 和报价控制器与嵌套表单无关——整个表单都发布到 textscontroler)
  • ...直到我发布后才看到@nathanvda(宝石的制造者)在这里!哎呀!
  • @nathanvda 非常感谢您正在调查此问题!非常感谢。我添加了 texts_controller.rb 以及上面的一些日志文件。

标签: javascript ruby-on-rails ruby-on-rails-5 nested-forms cocoon-gem


【解决方案1】:
  1. Models ... text.rb ... 除了已经发布的,我还得输入 accepts_nested_attributes_for :comments ...
  2. TextController ...您的嵌套允许参数需要在这里发生,.permit(:content, :picture, quotes_attributes: [:id, :content, :picture, :_destroy, comments_attributes: [:id, :bodycomment, :_destroy]])

我可能会关闭变量名称(尤其是您没有列出的 TextController),但基本上,看起来您正在尝试通过其他控制器继承嵌套 - 当唯一被调用的控制器是文本控制器。

可以肯定的是,在执行保存/更新时将tail -f log/&lt;logname&gt; 放入第二个控制台或查看终端控制台以观察许可问题。

如果还有问题,请告诉我!

...基于新控制器更新

控制器需要调整(你现在有一个放错位置的']')。

params.require(:text).permit(:url, :title, :coverimage,
                                  :publication, :author, :summary, quotes_attributes: [:id, :content, :picture, :_destroy, comments_attributes: [:id, :bodycomment, :_destroy]])

除非你也修复模型,否则这将不起作用......

accepts_nested_attributes_for :comments

【讨论】:

  • 感谢您的帮助,很抱歉忘记添加 TextController... 以为我会发布它。它现在在那里。我尝试为 text.rb 中的注释添加“accepts_nested_attributes_for”...但没有成功。请参阅上面的日志。
  • 好的,所以问题看起来像 2 个部分 - 您确实需要添加 accept_for 回来 & 当您查看控制器允许的 TextController 值时...您没有 comments在它们所属的quotes 内...仔细阅读我的答案,在#2 的末尾有两个右括号(一个用于引号,一个用于 cmets)彼此相邻。
  • 恕我直言,accepts_nested_attributes 已经可以了,但确实必须按照发送到控制器(并记录在日志文件中)的指示来嵌套强参数。
  • 伙计们,你们是最棒的。事实上,我没有明白 cmets_attributes 必须在quotes_attributes 的“内部”,因此我的方括号放错了位置。修复。作品。现在觉得很傻。再次感谢!
  • 我们都去过那里!很高兴你一直在摇摆
猜你喜欢
  • 1970-01-01
  • 2016-04-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多