如何使用事务以多模型形式显示错误消息?

时间:2015-06-26 11:57:58

标签: ruby-on-rails ruby ruby-on-rails-4 activerecord transactions

两个模型,组织和用户,有1:多关系。我有一个组合注册表单,组织和该组织的用户都可以注册。

我遇到的问题是:当为用户提交无效信息时,它会再次呈现表单,但是用户的错误消息(例如“用户名不能为空”)不是显示。表单在提交有效信息时确实有效,并且确实显示组织的错误消息,而不是用户。

我应该如何调整下面的代码,以便显示用户的错误消息?

def new
  @organization = Organization.new
  @user = @organization.users.build
end

def create
  @organization = Organization.new(new_params.except(:users_attributes))    #Validations require the organization to be saved before user, since user requires an organization_id. That's why users_attributs are above excluded and why below it's managed in a transaction that rollbacks if either organization or user is invalid. This works as desired.

  @organization.transaction do
    if @organization.valid?
        @organization.save
        begin
          # I executed next line in debugger (with invalid user info), which correctly responds with: ActiveRecord::RecordInvalid Exception: Validation failed: Email can't be blank, Email is invalid, Username can't be blank, etc.
          @organization.users.create!(users_attributes)
        rescue
          # Should I perhaps add some line here that adds the users errors to the memory?
          raise ActiveRecord::Rollback
        end
     end
  end

  if @organization.persisted?
    flash[:success] = "Yeah!"
    redirect_to root_url
  else
    @user = @organization.users.build(users_attributes)  # Otherwise the filled in information for user is gone (fields for user are then empty)
    render :new
  end

end

表单视图包括:

<%= form_for @organization, url: next_url do |f| %>
    <%= render partial: 'shared/error_messages', locals: { object: f.object, nested_models: f.object.users } %>
    <%= f.text_field :name %>
        # Other fields

    <%= f.fields_for :users do |p| %>
        <%= p.email_field :email %>
            # Other fields
    <% end %>

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

错误消息部分如下:

<% object.errors.full_messages.each do |msg| %>
  <li><%= msg.html_safe %></li>
<% end %>

更新:按照Rob的回答,我到达了下面的错误部分。这仍然不会显示用户的错误消息。我在下面的代码中添加了调试器响应,由于某种原因nested_model.errors.any?返回false,而控制器内的调试器(见上文)确实为用户返回错误消息。

<% if object.errors.any? %>
  <div id="error_explanation">
    <div class="alert alert-danger">
      The form contains <%= pluralize(object.errors.count, "error") %>.
    </div>

    <ul>
      <% object.errors.full_messages.each do |msg| %>
        <li><%= msg.html_safe %></li>
      <% end %>
    </ul>

  </div>
<% end %>

<% if defined?(nested_models) && nested_models.any? %>
  # Debugger: responds with "local-variable" for "defined?(nested_models)" and for "nested_models.any?" returns true.
  <div id="error_explanation">
    <ul>
      <% nested_models.each do |nested_model| %>
      # Debugger: "nested_model" has the same values as "nested_models.any?", as you would expect. But for "nested_model.errors.any?" it returns false, which it shouldn't.
        <% if nested_model.errors.any? %>    #Initially had "unless nested_model.valid?" but then errors for User are immediately displayed on loading the form page (new method).
          <ul>
            <% nested_model.errors.full_messages.each do |msg| %>
              <li><%= msg.html_safe %></li>
            <% end %>
          </ul>
        <% end %>
      <% end %>
    </ul>
  </div>
<% end %>

5 个答案:

答案 0 :(得分:0)

尝试在您的validates_associated :users协会组织下添加has_many :users

http://apidock.com/rails/ActiveModel/Validations/ClassMethods/validates_associated

答案 1 :(得分:0)

您是否在救援区段内成功创建了一个人?

  rescue ActiveRecord::RecordInvalid => exception
      # do something with exception here
      raise ActiveRecord::Rollback
      @organization.users.build if @organization.users.blank?
      render :new and return

此代码看起来会创建一个新的空用户,无论验证不正确。假设组织没有用户,则渲染new将简单地返回没有错误,因为用户已成功创建。

这种方法的控制流程有一些结果,肯定需要进一步细分。我会使用byebug并使用不正确的组织遍历块,然后使用错误的名称。然后是一个空组织,其用户属性不正确。

答案 2 :(得分:0)

组织has_many:用户和用户belongs_to:organization

organization.rb

accepts_nested_attributes_for :users

new.html.erb

<%= form_for @organization, url: next_url do |f| %>
 <%= render 'shared/error_messages', object: @organization %>
 <%= f.text_field :name %>
    # Other fields
 <%= f.fields_for(:users,@organization.users.build) do |p| %>
   <%= p.email_field :email %>
   # Other fields
 <% end %>
 <%= f.submit "Submit" %>
<% end %>

在控制器中

def create
  @organization = Organization.new(new_params)
  if @organization.save
    flash[:success] = "Yeah!"
    redirect_to root_url
  else
   render :new
  end
end

答案 3 :(得分:0)

这与this question非常相关。关键是我假设<%= render 'shared/error_messages', object: f.object %>仅在传递的对象上调用.errors方法(在本例中为organization)。

但是,由于user错误存在于user对象中,因此无法返回,因此不会显示。这需要简单地更改视图逻辑,以便在各种.errors模型上显示user的结果。你想怎么做取决于你。在链接的线程中,接受的答案有错误消息显示代码内联而不是部分,所以你可以这样做,但它会有点多余。

我会修改我的shared/error_messages.html.erb文件以检查另一个名为nested_models的传递本地文件。然后它将使用它来搜索关联的模型并在其上包含错误。我们只需要检查它是否是第一个定义的,这样你的其他不具有嵌套模型的视图就不会导致它引发错误。

<强>共享/ error_messages.html.erb

<% if object.errors.any? %>
  <div class="error-messages">
    Object Errors:
    <ul>
      <% object.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
    </ul>
    <% if defined?(nested_models) && nested_models.any? %>
      Nested Model(s) Errors:
      <ul>
        <% nested_models.each do |nested_model| %>
          <% unless nested_model.valid? %>
            <li>
              <ul>
                <% nested_model.errors.full_messages.each do |msg| %>
                  <li><%= msg %></li>
                <% end %>
              </ul>
            </li>
          <% end %>
        <% end %>
      </ul>
    <% end %>
  </div>
<% end %>

然后您只需要在视图中更改一行:

<%= render partial: 'shared/error_messages', locals: { object: @organization, nested_models: @organization.users } %>

答案 4 :(得分:0)

看起来你的控制器中有很多不可测试的逻辑。看起来对你来说逻辑会更好用到简单的FormObject模式。 https://robots.thoughtbot.com/activemodel-form-objects