Скажем, у меня есть базовое приложение 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
Это имеет смысл, потому что в комментарии еще нет страницы_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
Не уверен, что я хочу добавить что-то подобное для каждой ассоциации "один ко многим". Энди, вероятно, прав в том, что лучше всего не пытаться выполнить каскадное сохранение и использовать решение вложенных атрибутов. Я оставлю это открытым какое-то время, чтобы узнать, есть ли у кого-нибудь другие предложения.