Скажем, у меня есть базовое приложение Rails с базовым отношением один-ко-многим, где каждый комментарий относится к статье:
$ rails blog
$ cd blog
$ script/generate model article name:string
$ script/generate model comment article:belongs_to body:text
Теперь я добавляю код для создания ассоциаций, но я также хочу быть уверен, что когда я создаю комментарий, у него всегда есть статья:
class Article < ActiveRecord::Base
has_many :comments
end
class Comment < ActiveRecord::Base
belongs_to :article
validates_presence_of :article_id
end
Итак, давайте предположим, что я хотел бы создать статью с комментарием сразу:
$ rake db:migrate
$ script/console
Если вы сделаете это:
>> article = Article.new
=> #<Article id: nil, name: nil, created_at: nil, updated_at: nil>
>> article.comments.build
=> #<Comment id: nil, article_id: nil, body: nil, created_at: nil, updated_at: nil>
>> article.save!
Вы получите эту ошибку:
ActiveRecord::RecordInvalid: Validation failed: Comments is invalid
Что имеет смысл, потому что в комментарии еще нет page_id.
>> article.comments.first.errors.on(:article_id)
=> "can't be blank"
Так что, если я уберу validates_presence_of :article_id
из comment.rb
, тогда я смогу сделать сохранение, но это также позволит вам создавать комментарии без идентификатора статьи. Какой типичный способ справиться с этим?
ОБНОВЛЕНИЕ : Основываясь на предложении Николаса, вот реализация save_with_comments, которая работает, но безобразно:
def save_with_comments
save_with_comments!
rescue
false
end
def save_with_comments!
transaction do
comments = self.comments.dup
self.comments = []
save!
comments.each do |c|
c.article = self
c.save!
end
end
true
end
Не уверен, что я хочу добавить что-то подобное для каждой ассоциации один-ко-многим. Энди, вероятно, прав в этом, просто лучше избегать попыток каскадного сохранения и использовать решение с вложенными атрибутами. Я оставлю это открытым какое-то время, чтобы посмотреть, есть ли у кого-нибудь другие предложения