0

Две модели, Организация и Пользователь, имеют отношения 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 %> 

Обновление: После шагов от ответа Роба я прибыл на ошибки частично ниже. Это все еще не отображает сообщения об ошибках для пользователя. Я добавил ответы отладчика внутри кода ниже, и по какой-то причине 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 %> 

ответ

0

Попробуйте добавить validates_associated :users под has_many :users ассоциации в организации.

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

+0

Все те же, я боюсь, нет сообщений об ошибках для недопустимой информации о пользователе. Однако есть одна разница: форма регистрации существует на 'url/signup/organization'. Когда он отображается с 'new' при недействительной информации, url изменяется на' url/organization'. Это всегда было так. При отправке визуализированной формы он обрабатывает ее. При добавлении проверки повторная отправка генерирует ошибку программы: «Нет маршрутов, совпадающих с [PATCH]»/организациями »(только если какая-либо информация включена для пользователя). – Nick

+0

Такое поведение исчезло. Я опубликовал свой новый, очищенный код в OP. Я добавил в файл модели 'validates_associated: users'. Единственная оставшаяся проблема заключается в том, что сообщения об ошибках для пользователя не отображаются. – Nick

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 и прохожу через блок с неправильной организацией, а затем с неправильным именем. Затем пустая Организация с неправильными атрибутами пользователя.

+0

Я думал, что блок 'rescue' только запускается, если есть ошибка, и в противном случае он будет пропущен? Итак, если есть ошибка: выполните откат на db и повторно отрисуйте форму. Где в коде вы бы поместили byebug? – Nick

+0

Я бы разместил его перед @ organization.save !, затем проверил все соответствующие случаи. invalid org, invalid user, invalid org с пустым пользователем и т. д. Консоль покажет, что создано и что блок спасения делает под капотом. – goda

+0

Это ново для меня, но я сделал попытку. Результаты byebug добавляются в OP. – Nick

0

организации has_many: пользователи и пользователь belongs_to: организация

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 
+0

Замечания модели, которые вы сделали, я уже реализовал. Метод 'create', который вы предлагаете, не соответствует моим потребностям (например, для того, чтобы пользователь был зарегистрирован, для проверки требуется, чтобы организация была сохранена первой, так как для пользователя требуется организация_ид). Я пробовал ваши предложения относительно формы, но это не имело значения (по-прежнему такое же поведение). – Nick

+0

пользователь хочет получить дополнительные поля из организации, может у пост params .. – Ravindra

+0

Несомненно, без проблем, параметры: 'def new_params'' params.require (: organization) .permit (: name,: bag, users_attributes: [: email,: имя пользователя,: admin,: usertype,: password,: password_confirmation]) '' end'. Также у нас есть частный метод: 'def users_attributes'' new_params [: users_attributes] .values' 'end'. – Nick

0

Это очень относится к this question. Ключ в том, что <%= render 'shared/error_messages', object: f.object %>, я полагаю, только вызывает метод .errors на передаваемом объекте (в данном случае organization).

Однако, поскольку ошибки user находятся в объекте user, они не будут возвращены и поэтому не будут отображаться. Для этого требуется просто изменить логику просмотра, чтобы отобразить результаты .errors на различных моделях user. Как вы хотите это сделать, зависит от вас. В связанном потоке принятый ответ содержал код отображения сообщения ошибки inline вместо частичного, поэтому вы могли бы сделать это таким образом, но это было бы несколько избыточным.

Я бы изменил свой файл 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 } %> 
+0

Спасибо, Роб. Я чувствую, что это должно быть так, и я попробовал, но он до сих пор не получил сообщений об ошибках для пользователей. Я добавил «отладчик» в контроллер перед линией «render: new», так как я подумал, что, возможно, таким образом я могу проверить, действительно ли он распознает «nested_models». Но вводя 'nested_models.any?'в byebug просто вернул' неопределенную локальную переменную или метод 'nested_model' для # '. Если у вас есть предложения по использованию отладки, чтобы решить, почему он все еще не отображает ошибки, сообщите мне. – Nick

+0

@Nick Я немного отредактировал, если передача переменной экземпляра имеет значение. Обратите внимание, однако, что 'nested_models' - это только локальная переменная внутри части _ _error_messages' (устанавливается, когда вы вызывали' render partial' в представлении после того, как контроллер уже передал выполнение в представление, поэтому контроллер не собирается чтобы иметь представление о том, что это такое. Если вы хотите использовать ByeBug, вам нужно поместить его в партию «error_messages». –

+0

Я попробовал это Rob, но все тот же результат. 1) Я поместил Byebug в части ошибок сразу после первая строка 'if object.errors.any?'. Byebug вообще не выполняется. Другими словами, 'if object.errors.any?' Является ложным, тем самым не выполняя все частичное. 2) Я поставил Byebug в контроллер прямо перед '@ organization.users.create! (Users_attributes)', а затем ввел эту строку в Byebug. Он ответил «ActiveRecord :: RecordInvalid Exception: Ошибка проверки: электронная почта не может быть пустым, электронная почта недействительна и т. Д.». Поэтому он корректно вызывает ошибки. – Nick

0

Похоже, у вас есть много непроверяема логики в вашем контроллере. Похоже, для вас логика будет лучше использовать простой шаблон FormObject. https://robots.thoughtbot.com/activemodel-form-objects

+0

Что я хочу делать, это только сохранение, если действительная организация, а также действительный пользователь получают (либо сохранить оба, либо нет).Кроме того, для пользователя требуется организация. С точки зрения требований это не кажется мне слишком сумасшедшим. Но учитывая эти требования, потребуется некоторая специальная кодировка ... – Nick

+0

Мне кажется, что код организован неправильно. Он становится неустойчивым и неподходящим. Невозможно написать достоверные тесты и поддерживать адекватный диалог. Вы должны написать много писем в этом сообщении вместо простого 'я ожидал иметь: а, но есть: b'. До этого момента вам придется реорганизовать свой код. Возможно, ответ тихий. – antiqe