Rails: предотвращение создания родительской модели и появление ошибок проверки модели дочерней модели
Я некоторое время задерживался этой проблемой и полностью смутил себя тем, как совлокальные модели и проверки работают вместе.
В приведенном ниже коде моя цель состоит в том, чтобы создать родительскую модель (изображение или видео), если проверка работоспособности дочерней модели (содержимого) не выполняется. В настоящее время родительская модель сохраняется, пока дочерняя модель отсутствует, а ошибки проверки не слышны. Если ошибок проверки не существует, все работает так, как ожидалось.
#Image.rb
has_one :content,
as: :contentable,
inverse_of: :contentable,
dependent: :destroy
#Video.rb
has_one :content,
as: :contentable,
inverse_of: :contentable,
dependent: :destroy
#Content.rb
belongs_to :contentable,
inverse_of: :content,
polymorphic: true
validate :all_good?
def all_good?
errors.add(:base, "Nope!")
return false
end
Любые выводы или идеи очень ценятся!
Ответы
Ответ 1
Rails имеет специальную проверку, называемую validates_associated, которая гарантирует, что соответствующая запись действительна. Если связанная запись недействительна, родительская запись также будет недействительной, и ошибка для ассоциации будет добавлена в список ошибок.
В обоих классах изображений и видео добавьте следующее:
validates_associated :content
Теперь, если ассоциация content
недействительна, видео или изображение не будут сохранены.
video = Video.new
video.content = Content.new
video.save #=> false
video.valid? #=> false
video.errors #=> [:content => "is invalid"]
Ответ 2
Короткий ответ
Добавить в фото и видео модель:
accepts_nested_attributes_for :content
Доказательство
Я был уверен, что знаю ответ на этот вопрос, но не был уверен, что он работал с полиморфными ассоциациями (которые я раньше не использовал), поэтому я установил небольшой тест.
Создал модели так же, как у вас есть ваша установка, но с атрибутом имени и с проверкой, которую я могу использовать для проверки отказа.
class Image < ActiveRecord::Base
has_one :content,
as: :contentable,
inverse_of: :contentable,
dependent: :destroy
validates_length_of :name, maximum: 10
end
class Content < ActiveRecord::Base
belongs_to :contentable,
inverse_of: :content,
polymorphic: true
validates_length_of :name, maximum: 10
end
Затем установите миграцию следующим образом:
class CreateImages < ActiveRecord::Migration
def change
create_table :images do |t|
t.string :name
t.timestamps null: false
end
end
end
class CreateContents < ActiveRecord::Migration
def change
create_table :contents do |t|
t.string :name
t.references :contentable, polymorphic: true, index: true
t.timestamps null: false
end
end
end
Затем напишите RSpec, чтобы проверить, что родительский объект не сохранен, если дочерний элемент не может быть сохранен и что ошибки проверки будут пересчитаны.
it 'should not save image if content is invalid' do
image = Image.new()
image.name = 'this is ok'
expect(image).to be_valid
content = Content.new()
content.name = 'a string that should fail validation'
image.content = content
expect(image).to_not be_valid
image.save
expect(image).to_not be_persisted
expect(content).to_not be_persisted
expect(image.errors.count).to eq(1)
expect(image.content.errors[:name][0]).to include('is too long')
end
Выполнить тест и, конечно же, не получится.
Затем добавьте следующую строку в изображение (и видео)
accepts_nested_attributes_for :content
Теперь проходят тесты - то есть, если ребенок не выполняет проверку, родитель также откажет проверку и не сохранит.
Ответ 3
Вам нужно повысить исключение в своей пользовательской проверке.
сделать что-то вроде
before_save :ensure_all_good
def ensure_all_good
try saving stuff to chile
failed? raise nope
end