Rails проверяет количество ассоциаций перед удалением

Мои модели

class Company
  has_many :admins

  validate :has_one_admin_validation

  private

  def has_one_admin_validation
    errors.add(:admins, :not_enough) if admins.size < 1
  end

end

class Admin
  belong_to :company
end

Теперь предположим, что у меня есть контроллер, который может удалять администраторов. Как предотвратить удаление админа (т.е. генерировать ошибки), если он единственный админ своей компании?

Если я правильно понимаю, мне нужно удалить администратора из объекта памяти и попытаться «сохранить/уничтожить», если сначала проверит компанию?


person Cyril Duchon-Doris    schedule 16.06.2016    source источник
comment
обратите внимание, что есть validates_associated   -  person niceman    schedule 16.06.2016
comment
хм, но, возможно, эта проверка принадлежит классу администратора, хотя не уверен   -  person niceman    schedule 16.06.2016


Ответы (1)


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

validates :admins, length: { minimum: 1 }

Если это не сработает, вы также сможете проверить «marked_for_destruction?» имущество. Вы также должны быть в состоянии проверить взаимные отношения с проверкой «присутствие: истина».

    class Company
        has_many :admins

        validate :has_one_admin_validation

        private

        def has_one_admin_validation
          errors.add :admins, "You need at least one admin" if admins.reject(&:marked_for_destruction?).empty?
        end

    end

    class Admin
        belongs_to :company, presence: true
    end

Вы также можете рассмотреть возможность использования обратного вызова before_destroy в своем классе администратора.

before_destroy :has_company_with_no_other_admins
        ...

        private

        def has_company_with_no_other_admins
          return false if company.admins.length < 2
          true
        end

Здесь есть довольно хорошее описание использования before_destroy: https://stackoverflow.com/a/123190/6441528 Это стоит посмотреть at потому что реализации зависят от вашей версии Rails.

person hightempo    schedule 16.06.2016
comment
хм, будет ли эта проверка выполняться при удалении администратором? обратите внимание, что проверка присутствует в классе категории или, может быть, ее следует поместить в класс администратора? - person niceman; 16.06.2016
comment
Привет! Я не ОП, но у меня есть проект, в котором я могу использовать что-то подобное. Допустим, у меня есть модель с ассоциированной моделью has_one. Поможет ли это мне установить ограничение в 1 ассоциацию для этой модели? Здоровья и спасибо! - person Alfredo Gallegos; 16.06.2016
comment
Одного @AlfredoGallegos has_one достаточно, проверки не нужны, на самом деле добавление более 1 приведет к исключению времени выполнения Ruby (возможно, NoMethodError) - person niceman; 16.06.2016
comment
Дело в том, что в этом проекте я каким-то образом могу установить более одного отношения, даже если мои модели имеют has_one/belongs_to. Я думаю, что это глупость с моей стороны, так что не беспокойтесь :^) - person Alfredo Gallegos; 16.06.2016
comment
@niceman - я думаю, вы можете быть правы в том, что я несколько неправильно понял вопрос и не добавил взаимную проверку администратору. Я соответствующим образом отредактировал свой ответ, добавив модификатор «присутствие: истина» в класс администратора. - person hightempo; 16.06.2016
comment
@AlfredoGallegos - я согласен с хорошим человеком - я думаю, это должно сработать. Если у вас все еще есть проблемы, возможно, вам следует опубликовать свой код в новом вопросе, чтобы люди могли посмотреть на него в контексте. - person hightempo; 16.06.2016
comment
Не следует ли тогда before_destroy проверить наличие company.admins.length < 2 ? <2 означает, что у компании есть еще один администратор на случай, если текущий будет уничтожен? - person Cyril Duchon-Doris; 17.06.2016
comment
@CyrilDuchon-Doris - да, вы правы, поскольку обратный вызов обрабатывается до того, как запись фактически уничтожается. - person hightempo; 17.06.2016
comment
А еще, так как я на Rails 5, мне нужно throw(:abort) верно? - person Cyril Duchon-Doris; 17.06.2016
comment
Да, в этом случае Rails 5 требует фактической ошибки. - person hightempo; 17.06.2016