Метод сборки коллекции в Rails является разрушительным?

#ruby-on-rails #ruby

#ruby-on-rails #ruby

Вопрос:

Является ли метод colleciton.build разрушительным?

например (я ссылался https://api.rubyonrails.org/classes/ActiveRecord/Associations/CollectionProxy.html#method-i-build )

 class Pet
 belongs_to :person
end

class Person
  has_many :pets
end

new_pet = person.pets.build
# => #<Pet id: nil, name: nil, person_id: 1>

new_pet
# => #<Pet id: nil, name: nil, person_id: 1>
  

Я могу это понять.
Но,

 person.pets
# => [#<Pet id: nil, name: nil, person_id: 1>]
  

человек.домашние животные выглядят разрушительными из-за метода сборки коллекции.
Я думаю person.pets , что возвращает пустую коллекцию.

Я не могу понять это поведение. Почему это так? Документы пишут о методе сборки, как показано ниже

Возвращает новый объект типа коллекции, который был создан с атрибутами и связан с этим объектом, но еще не был сохранен. Вы можете передать массив хэшей атрибутов, это вернет массив с новыми объектами.

Комментарии:

1. Что именно вы подразумеваете под деструктивным ? Я полагаю, вы думаете, что он переопределяет текущий person.pets . Вы проверяли это, проверяя значение person.pets перед вызовом person.pets.build , а затем проверяя значение person.pets впоследствии? build не следует уничтожать какие-либо данные, он должен возвращать новый экземпляр и добавлять вновь созданный экземпляр в связанную коллекцию.

2. @3limin4t0r: возможно, они имели в виду это как «мутирующее»

3. @SergioTulentsev Это то, о чем я тоже подумал, поэтому я попросил разъяснений.

4. @3limin4t0r Спасибо за комментарии. «деструктивный» означает изменение.

Ответ №1:

Да, он «деструктивный» в том смысле, что он изменяет коллекцию, добавляя новый элемент.

Я не могу понять это поведение. Почему это так?

Я уверен, что есть много причин, но очевидная, которая приходит на ум, — это упрощает клиентский код. Предположим, у вас есть человек, у которого уже есть несколько домашних животных, и вы хотите предоставить пользователю возможность добавлять новых домашних животных.

 # controller
@person = Person.find(params[:id])
@person.pets.size # => 2
@person.pets.build

# view
<% @person.pets.each do |pet| %>
  <% if pet.persisted? %>
    display pet
  <% else %>
    render new pet form
  <% end %>
<% end %>
  

Вы могли бы сделать что-то подобное @new_pet = Pet.new(person_id: @person.id) и использовать это для рендеринга формы, но это выглядит более сложным. Кроме того, описанный выше подход позволяет добавлять новые элементы по N за раз: просто создайте N новых питомцев перед рендерингом страницы.

Комментарии:

1. Спасибо за ответ. Я стал способен понять это поведение. И я впервые знаю, что «деструктивный» в ja означает «мутирующий» в en. Я многому научился у вас

Ответ №2:

Основная причина, по которой вызов #new ( #build это просто псевдоним для #new ) изменяет получателя, — это простота.

Например, он позволяет автоматически сохранять:

 person = Person.new
person.pets.build(name: 'Spot')
person.pets.build(name: 'Professor Fluffy')
person.save!
  

Это приведет к вставке как person, так и двух строк в таблицу pets. Он также позволяет создавать поля формы для вложенных записей:

 def new
  @person = Person.new
  5.times { @person.pets.new }
end
  

Это позволяет нам просто вызывать @person.pets , чтобы получить записи, которые мы «посеяли»:

 <%= form_with(model: @person) do |f| %>
  <%= fields_for :pets do |pet_fields| %>
    # ...
  <% end %>
<% end %>
  

Если #new бы получатель не мутировал, нам пришлось бы собрать все записи, которые мы посеяли, и каким-то образом передать их в форму. Это было бы очень неудобно, если бы у вас было несколько форм на одной странице.

Комментарии:

1. Спасибо за ответ. точно, если он не мутирует, мы должны их собрать. Я многому научился у вас