Nested model validation - errors don't show

吃可爱长大的小学妹 提交于 2020-01-01 11:55:15

问题


There have been many questions about this, but none of them seem to help. And yes, I have watched this rails cast.

I have an Author who has many Books, like so:

Author:

class Author < ActiveRecord::Base
  attr_accessible :name
  has_many :books, dependent: :destroy

  accepts_nested_attributes_for :books, allow_destroy: true

  validates :name, presence: true
  validates :name, length: { minimum: 3 }
end

Book:

class Book < ActiveRecord::Base
  attr_accessible :name, :year
  belongs_to :author

  validates :name, :year, presence: true
  validates :year, numericality: { only_integer: true, less_than_or_equal_to: Time.now.year }
end

I created the following form to add a book to an author in authors#show:

<%= form_for([@author, @book], html: { class: "well" }) do |f| %>
<% if @book.errors.any? %>
    <div class="alert alert-block">
        <ul>
            <% @author.errors.full_messages.each do |msg| %>
                <li><%= msg %></li>
            <% end %>
        </ul>
    </div>
<% end %>
#labels and buttons...
<% end %>

...with the following authors_controller method:

def show
    @author = Author.find(params[:id])
    @book = @author.books.build
end

...and the following books_controller method:

def create
    @author = Author.find(params[:author_id])
    if @author.books.create(params[:book])
      redirect_to author_path(@author)
    else
      render action: :show
    end
  end

I cannot seem to figure out why the form does not display any error messages. I followed the example from railscasts where they say there should be an instance variable of books in the form instead of @author.books.build, so I put the latter in the controller and @book in the form - still to no avail.

Thanks for any help!


回答1:


Let's step through it.

You submit the create, and that enters your create action

def create
  @author = Author.find(params[:author_id])
  if @author.books.create(params[:book])
    redirect_to author_path(@author)
  else
    render action: :show
  end
end

(Side note, what if @author is not found. You are not handling that case.)

Now, the Author is found, but @author.books.create fails (returns false), so you render the show action.

This uses the show template, but does not call the show action code. (Side note, maybe the new page would be a better choice, so the user can try to create again.)

At this point @author is instantiated with the Author you found, but not @book. So @book, if called will be nil.

Your show template does

if @book.errors.any?

which will not be true, so the rest of the template inside the if will be skipped. That's why there are no errors.

You don't need a form_for to display error messages. If you switch to using the new template, then there will be a form to try again.

So let's switch to rendering new.

Class BooksController < ApplicationController
  def new
    @author = Author.find(params[:author_id])
    @book = @author.books.build
  end

  def create
    @author = Author.find(params[:author_id])
    @book = @author.books.build(params[:book])
    if @author.save
      redirect_to author_path(@author)
    else
      render action: :new
    end
  end

Your new template will be

<% if @author.errors.any? %>
    <div class="alert alert-block">
        <ul>
            <% @author.errors.full_messages.each do |msg| %>
                <li><%= msg %></li>
            <% end %>
        </ul>
    </div>
<% end %>
<% if @book.errors.any? %>
    <div class="alert alert-block">
        <ul>
            <% @book.errors.full_messages.each do |msg| %>
                <li><%= msg %></li>
            <% end %>
        </ul>
    </div>
<% end %>

<%= form_for([@author, @book], html: { class: "well" }) do |f| %>
#labels and buttons...
<% end %>



回答2:


In Books controller /books_controller.rb

def new
   @author = Author.find_by_id(params[:author_id])
   @book = @author.books.build
end

def create 
   @author = Author.find_by_id(params[:author_id])
   if @author
     @book = @author.books.build(params[:book])
     if @book.save
       flash[:notice] = "Book saved successfully"
       redirect_to author_path(@author)
     else
       render :new
     end
  else
   flash[:notice] = "Sorry no author found"
   redirect_to author_path
  end 
end

If author is not present redirect to authors index page with error message dont render the new form as you'll not be able to build the books form as author is nil.

And in your books new form you can have the error listed for books

/books/new.html.erb

  <% if @book.errors.any? %>
    <div class="alert alert-block">
      <ul>
         <% @books.errors.full_messages.each do |msg| %>
             <li><%= msg %></li>
         <% end %>
     </ul>
   </div>
 <% end %>


来源:https://stackoverflow.com/questions/12972112/nested-model-validation-errors-dont-show

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!