Rails-Best-Practice: как создать зависимые отношения
не могли бы вы сказать мне, что лучшая практика для создания has_one отношения?
f.e. если у меня есть модель пользователя, и она должна иметь профиль...
Как я мог это сделать?
одним из решений может быть:
# user.rb
class User << ActiveRecord::Base
after_create :set_default_association
def set_default_association
self.create_profile
end
end
но это не кажется очень чистым... Любой предполагает?
6 ответов
Лучшая практика для создания отношения has_one-использовать обратный вызов ActiveRecord before_create
, а не after_create
. Или используйте еще более ранний обратный вызов и разберитесь с проблемами (если таковые имеются) ребенка, не проходящего собственный шаг проверки.
потому что:
- при хорошем кодировании у вас есть возможность показать пользователю проверки дочерней записи, если проверки не удастся
- это чище и явно поддерживается ActiveRecord -- AR автоматически заполняет внешний ключ в дочерней записи после сохранения родительской записи (при создании). Затем AR сохраняет дочернюю запись как часть создания родительской записи.
как делать:
# in your User model...
has_one :profile
before_create :build_default_profile
private
def build_default_profile
# build default profile instance. Will use default params.
# The foreign key to the owning User model is set automatically
build_profile
true # Always return true in callbacks as the normal 'continue' state
# Assumes that the default_profile can **always** be created.
# or
# Check the validation of the profile. If it is not valid, then
# return false from the callback. Best to use a before_validation
# if doing this. View code should check the errors of the child.
# Or add the child's errors to the User model's error array of the :base
# error item
end
ваше решение, безусловно, достойный способ сделать это (по крайней мере пока вы не перерастете это), но вы можете упростить это:
# user.rb
class User < ActiveRecord::Base
has_one :profile
after_create :create_profile
end
Если это новая ассоциация в существующей большой базе данных, я буду управлять переходом следующим образом:
class User << ActiveRecord::Base
has_one :profile
before_create :build_associations
def profile
super || build_profile(avatar: "anon.jpg")
end
private
def build_associations
profile || true
end
end
таким образом, существующие записи пользователей получают профиль, когда их просят, и с ним создаются новые. Это также атрибуты по умолчанию в одном месте и правильно работает с accepts_nested_attributes_for в Rails 4 года.
вероятно, не самое чистое решение, но у нас уже была база данных с полумиллионом записей, некоторые из которых уже создали модель "профиля", а некоторые-нет. Мы пошли с этим подходом, который гарантирует, что модель профиля присутствует в любой момент, без необходимости проходить и ретроактивно генерировать все модели профиля.
alias_method :db_profile, :profile
def profile
self.profile = Profile.create(:user => self) if self.db_profile.nil?
self.db_profile
end
вот как я это делаю. Не уверен, насколько это стандартно, но он работает очень хорошо, и его ленивый в том, что он не создает дополнительных накладных расходов, если не нужно создавать новую ассоциацию (я рад быть исправленным на этом):
def profile_with_auto_build
build_profile unless profile_without_auto_build
profile_without_auto_build
end
alias_method_chain :profile, :auto_build
Это также означает, что ассоциации есть, как только вам это нужно. Я думаю, альтернативой является подключение к after_initialize, но это, похоже, добавляет довольно много накладных расходов, поскольку он запускается каждый раз, когда объект инициализируется, и могут быть времена где вы не хотите получить доступ к ассоциации. Проверить его существование-пустая трата времени.
для этого есть драгоценный камень:
https://github.com/jqr/has_one_autocreate
похоже, он немного устарел. (не работает с rails3)