Строить против нового в Rails 3

126

В Rails 3 Docs , то buildметод ассоциаций описывается как такой же , как newметод, но с автоматическим присвоением внешнего ключа. Прямо из документации:

Firm#clients.build (similar to Client.new("firm_id" => id))

Я читал подобное в другом месте.

Однако, когда я использую new(например , some_firm.clients.newбез каких - либо параметров), нового клиента firm_idассоциация будет создана автоматически. Я прямо сейчас смотрю на результаты в консоли!

Я что-то упускаю? Документы немного устарели (маловероятно)? В чем разница между buildи new?

ClosureCowboy
источник
3
Люди, которые ищут быстрый ответ, проверьте второй вариант: «build» - это всего лишь псевдоним для «new»
ivanreese

Ответы:

209

Вы немного неправильно читаете документы. some_firm.client.newсоздает новый Clientобъект из коллекции клиентов, и поэтому он может автоматически установить firm_idзначение some_firm.id, тогда как вызываются документы, Client.newкоторые вообще не знают идентификатора какой-либо фирмы, поэтому ему нужно firm_idпередать ему.

Единственная разница между some_firm.clients.newи, some_firm.clients.buildпохоже, состоит в том, что buildв clientsколлекцию также добавляется вновь созданный клиент :

henrym:~/testapp$ rails c
Loading development environment (Rails 3.0.4)
r:001 > (some_firm = Firm.new).save # Create and save a new Firm
#=> true 
r:002 > some_firm.clients           # No clients yet
#=> [] 
r:003 > some_firm.clients.new       # Create a new client
#=> #<Client id: nil, firm_id: 1, created_at: nil, updated_at: nil> 
r:004 > some_firm.clients           # Still no clients
#=> [] 
r:005 > some_firm.clients.build     # Create a new client with build
#=> #<Client id: nil, firm_id: 1, created_at: nil, updated_at: nil> 
r:006 > some_firm.clients           # New client is added to clients 
#=> [#<Client id: nil, firm_id: 1, created_at: nil, updated_at: nil>] 
r:007 > some_firm.save
#=> true 
r:008 > some_firm.clients           # Saving firm also saves the attached client
#=> [#<Client id: 1, firm_id: 1, created_at: "2011-02-11 00:18:47",
updated_at: "2011-02-11 00:18:47">] 

Если вы создаете объект через ассоциацию, это buildдолжно быть предпочтительнее, так newкак сборка сохраняет ваш объект в памяти some_firm(в данном случае) в согласованном состоянии даже до того, как какие-либо объекты были сохранены в базе данных.

henrym
источник
8
Использование some_firm.client.newтакже добавляет клиент к some_firm.clients, и вызов saveна some_firmв результате ошибки проверки , указывающей , что clientявляется недопустимым. Если оба newи buildдобавить нового клиента в some_firmколлекцию клиентов, что buildделать, что newне делает? Простите за то, что был тупицей, здесь!
ClosureCowboy
1
+1 Получил ваш результат с 3.0.4. Я был бы рад, если бы кто-нибудь с 3.0.3 подтвердил, что я не сумасшедший.
ClosureCowboy
41
@henrym Похоже, что в 3.2.6 клиенты.new и clients.build похожи в том, что они оба добавляют новый объект в коллекцию. Я хотел бы добавить комментарий для всех, кто сталкивался с этим, когда гуглил, как я
Hubbard
11
Похоже, в Rails 3.2.3 между ними нет разницы
Адитья Капур
4
Этот ответ неверен для Rails> 3.2.13, где build - это просто псевдоним для new. См. Ответ @ HatemMahmoud ниже.
Андреас
92

buildэто просто псевдоним для new:

alias build new

Полный код можно найти: https://github.com/rails/rails/blob/master/activerecord/lib/active_record/relation.rb#L74

Хатем Махмуд
источник
13
alias build newas of rails 3.2.13
fontno
7
Это верно только для некоторых ассоциаций / отношений. Например, единичные ассоциации имеют совершенно разные определения для buildи build_#{association}. Смотрите здесь и здесь .
Coreyward
1
Это все еще верно для Rails 4?
fatman13
1
вот отчет об ошибке ... который предполагает, что если вы использовали new, например restaurant.customers.new, как способ связать нового клиента с рестораном, не добавляя его в restaurant.customers, использовать scoped ... например, restaurant .customers.scoped.new
user3334690
11

Вы правы, функции build и new имеют одинаковый эффект установки внешнего ключа, когда они вызываются через ассоциацию. Я считаю, что причина, по которой документация написана таким образом, состоит в том, чтобы уточнить, что создается новый объект Client, а не отношения новой активной записи. Это тот же эффект, что и вызов .new для класса в Ruby. Это означает, что в документации разъясняется, что вызов построения на основе ассоциации - это то же самое, что и создание нового объекта (вызов .new) и передача внешних ключей этому объекту. Все эти команды эквивалентны:

Firm.first.clients.build
Firm.first.clients.new
Client.new(:firm_id => Firm.first.id)

Я считаю, что причина .build существует в том, что Firm.first.clients.new может интерпретироваться как означающее, что вы создаете новый объект отношения has_many, а не фактического клиента, поэтому вызов .build - это способ прояснить это.

Пан Томакос
источник
Таким образом , они являются эквивалентными. Это определенно то, чем кажется. Спасибо!
ClosureCowboy
5
Это не так. Первые два эквивалентны в более поздних версиях Rails (похоже, на момент публикации их не было). НО, последний имеет существенное отличие в том, что Firm.first.clients не будет содержать нового клиента.
tybro0103 03
4

buildпротив new:

в основном новый и сборка такие же, но сборка хранит объект в памяти ,

например:

для новых:

Client.new(:firm_id=>Firm.first.id)

Для сборки:

Firm.first.clients.build

Здесь клиенты хранятся в памяти, при сохранении фирмы также сохраняются связанные записи.

Сарван Кумар
источник
2

Model.new

Tag.new post_id: 1создаст экземпляр тега с его post_idнабором.

@ model.models.new

@post.tags.buildделает то же самое, И созданный тег будет существовать @post.tagsдаже до его сохранения.

Это означает, что @post.saveбудут сохранены как @post, так и вновь созданный тег (при условии, что установлен inverse_of). Это замечательно, потому что Rails будет проверять оба объекта перед сохранением, и ни один из них не будет сохранен, если какой-либо из них не прошел проверку.

models.new против models.build

@post.tags.buildи @post.tags.newэквивалентны (по крайней мере, начиная с Rails 3.2).

tybro0103
источник
как насчет этого The only difference between some_firm.clients.new and some_firm.clients.build seems to be that build also adds the newly-created client to the clients collection:?
ア レ ッ ク ス