Валидация не мешает уничтожить - Rails 5 - PullRequest
0 голосов
/ 20 сентября 2018

У меня есть запись Customer со связью «многие ко многим» с Location.

В настоящее время я проверяю / снимаю отметки с местоположений, которые я хочу для конкретного клиента, на странице редактирования клиента.Когда я снимаю флажок со всех местоположений и нажимаю «Отправить», местоположения удаляются из моей модели и выдается ошибка из моей проверки.Я ожидал бы, что местоположения не будут удалены, так как у меня есть проверка, которая требует, чтобы они существовали, и это то, что мне нужно.

Моя проверка в модели Customer.

validates :locations, presence: true

Мой взгляд:

<% @locations.each do |i| %>
      <li class="list-group-item">
        <%= hidden_field_tag "customer[location_ids][]", '' %>
        <%= check_box_tag "customer[location_ids][]", i.id, @customer.location_ids.include?(i.id) %>
        <%= i.name %>
      </li>
<% end %>

Мой контроллер:

    @customer = Customer.find(params[:id])
    @customer.assign_attributes(params.require(:customer).permit(:first_name, :middle_initial, :last_name, :location_ids => []))
    @customer.save!

.save! вызывает следующую ошибку:

error Locations can't be blank

Однако, когда вы обновляете страницу или просматриваете записи базы данных, ссылки на местоположения уничтожаются, и я также вижу, что они уничтожаются из CLI.Я не понимаю, почему проверка не предотвращает уничтожение ссылок на местоположение.Заранее спасибо.

Ответы [ 3 ]

0 голосов
/ 20 сентября 2018

Я обнаружил, что ActiveRecord более надежен в проверке существования связанных записей, запрашивая его для проверки длины связанного массива *_ids.В вашем случае:

class Customer < ApplicationRecord
  has_many :locations
  validates :location_ids, length: { minimum: 1 }
end
0 голосов
/ 21 сентября 2018

Это распространенная проблема, с которой люди сталкиваются с ActiveRecord.

Проблема

Метод установки location_ids= немедленно добавит / обновит / удалит записи, о которых говорится в Справочник ассоциаций ActiveRecord .Такое поведение часто удивляет разработчиков и часто нежелательно.Я почти всегда избегаю этого.

В своем коде вы сначала вызываете assign_attributes, что, в свою очередь, вызывает location_ids=.Изменения немедленно сохраняются в записях.Впоследствии, когда вызывается save!, открывается новая транзакция.Если возникает ошибка проверки, тогда будут отменены только изменения в этой транзакции, которые исключают уже сохраненные изменения, сделанные location_ids=.

@customer.assign_attributes(params...)  # location_ids are saved outside of the `save!` transaction.
@customer.save!                         # validation errors will cause a rollback only to this point, excluding changes from the previous line.

Простое решение

Использованиеupdate_attributes! для замены assign_attributes и save!.Это приведет к тому, что все изменения в транзакции будут перенесены так, что откат отменит все, что вы хотите.Yay!

@customer.update_attributes!(params.require(:customer).permit(:first_name, :middle_initial, :last_name, :location_ids => []))

Альтернативный подход

Иногда может быть невозможно избежать отдельных вызовов на номера assign_attributes и save.Это делает вещи намного сложнее.Я не могу придумать ни одного варианта для этого случая.

Одним из возможных решений является использование вложенных атрибутов для обновления / уничтожения дочерних записей.

location_ids действительно ярлык для обновлениякаждая запись местоположения связана с данным клиентом.Вместо того, чтобы полагаться на это, вы можете использовать вложенные атрибуты в ваших формах для обновления местоположений.Этот метод может использовать функцию mark_for_destruction ( link ) для автосохранения.Описание полного решения для этого подхода было бы чрезмерно длительным, но оно оказалось очень эффективным для меня.

0 голосов
/ 20 сентября 2018

Вместо этого вы можете сделать это.Для валидации мы должны использовать before_destory callback.Это должно препятствовать удалению местоположений, когда вы сохраняете клиента, когда вы сняли отметку со всех местоположений.

class Customer < ActiveRecord::Base
  has_many :locations
  before_destroy :check_for_locations?

  private

  def check_for_locations?
    errors.add(:base, "cannot delete customer with locations") unless locations.count == 0
    errors.blank?
  end
end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...