2017-02-03 8 views
1

Предположим, у меня есть модель Person, что у Человека много домашних животных, а у Pet есть полиморфная связь с собакой и кошкой. Обратите внимание, что у человека не может быть животное, у которого у него аллергия.Как заменить ассоциации моделей в памяти в Rails?

class Person < ActiveRecord::Base 
    has_many :pets, dependent: :destroy 
    validate :not_allergic_to_animal 

    def not_allergic_to_animal 
    if allergic_to_cats? && owns_a_cat? 
     errors.add(:pets, 'cannot have a cat if allergic to cats') 
    elsif allergic_to_dogs? && owns_a_dog? 
     errors.add(:pets, 'cannot have a dog if allergic to dogs') 
    end 
    end 

    def owns_a_cat? 
    pets.any? { |pet| pet.animal_type == Cat.name } 
    end 

    def owns_a_dog? 
    pets.any? { |pet| pet.animal_type == Dog.name } 
    end 
end 

class Pet < ActiveRecord::Base 
    belongs_to :user 
    belongs_to :animal, polymorphic: true 

    validates :animal_type, inclusion: { in: [Cat.name, Dog.name] } 
end 

class Cat < ActiveRecord::Base 
end 

class Dog < ActiveRecord::Base 
end 

Теперь предположим, что у человека Bob уже есть 2 кошки, одна по имени Алиса, а другая по имени Адель.

bob = Person.where(name: 'Bob') 

alice = Cat.where(name: 'Alice') 
bob.pets.create!(animal: alice) 

adele = Cat.where(name: 'Adele') 
bob.pets.create!(animal: adele) 

Боб любит Алису, но Адель - действительно агрессивная кошка, поэтому Боб больше этого не хочет. Он также хочет собаку. Поэтому он решает сделать запрос PUT к своей конечной точке. 3 - это идентификатор Джека, а 1 - идентификатор Алисы кошки.

PUT /me/ { 
    "pets": { 
    "dog_ids": [3], 
    "cat_ids": [1] 
    } 
} 

Теперь вопрос!

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

Я знаю, что можно создавать объединения в памяти с помощью #build, но я не хочу, чтобы добавить к существующим объединениям, но заменить уже существующие, все в памяти перед сохранением. Поэтому приведенный выше пример выполнил бы 2 запроса базы данных после проверки экземпляра Person, один для создания ассоциации Pet для Jack the dog, а другой - для удаления ассоциации для Adele the cat, после проверки.

Я пытался манипулировать ActiveRecord::Associations::CollectionProxy, но он всегда фиксируется в базе данных при удалении или добавлении элемента.

Обратите внимание, что я не хочу создавать валидатор параметров и просто проверять идентификаторы, другие интерфейсы могут использоваться для обновления Person, а проверка должна быть согласованной во всех этих интерфейсах и близка к модели.

Редактировать

Я попытался с помощью #build в сочетании с #mark_for_destruction, а также, но замена не будет работать, существующие ассоциации будут воссозданы после сохранения.

ответ

0

Вы можете назначить коллекцию напрямую, используя метод pets=. Например,

# Find the first pet 
p1 = bob.pets.find_by_animal_id(alice.id) 

# Create the second pet, this won't save yet 
jack = Dog.find(3) 
p2 = Pet.new(animal: jack) 

bob.pets = [p1, p2] 
+0

Выполнение 'bob.pets = [p1, p2]' сохранит ассоциацию домашних животных в базе данных. Я хочу назначить домашних питомцев в память для проверки боба перед выполнением любой транзакции базы данных. Теоретически это возможно, но ActiveRecord не позволяет мне это делать. –

1

Решение Я только что пришел с:

знак для домашних животных, для которых уничтожение animal_id не в предоставленных идентификаторами, построить животных, которые уже не существуют, и пропустить животных отмечены для уничтожения Проверка.

class Person < ActiveRecord::Base 
    has_many :pets, dependent: :destroy 
    validate :not_allergic_to_animal 

    def not_allergic_to_animal 
    if allergic_to_cats? && owns_a_cat? 
     errors.add(:pets, 'cannot have a cat if allergic to cats') 
    elsif allergic_to_dogs? && owns_a_dog? 
     errors.add(:pets, 'cannot have a dog if allergic to dogs') 
    end 
    end 
    def owns_a_cat? 
    pets 
     .reject(&:marked_for_destruction?) 
     .any? { |pet| pet.animal_type == Cat.name } 
    end 

    def owns_a_dog? 
    pets 
     .reject(&:marked_for_destruction?) 
     .any? { |pet| pet.animal_type == Dog.name } 
    end 
end 


bob = Person.where(name: 'Bob') 

alice = Cat.where(name: 'Alice') 
bob.pets.create!(animal: alice) 

adele = Cat.where(name: 'Adele') 
bob.pets.create!(animal: adele) 

jack = Dog.create 

bob.pets.find_by(animal_type: Cat.name, animal_id: adele.id).mark_for_destruction 

bob.pets.build(animal: jack) 
bob.valid? 
bob.save! 

 Смежные вопросы

  • Нет связанных вопросов^_^