Rails - Best-Practice: Как создать зависимые отношения has_one - PullRequest
72 голосов
/ 28 сентября 2010

Не могли бы вы рассказать, как лучше всего создавать отношения 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 ]

120 голосов
/ 28 сентября 2010

Для создания отношения 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
27 голосов
/ 28 сентября 2010

Ваше решение, безусловно, является достойным способом сделать это (по крайней мере, пока вы не перерастете его), но вы можете упростить его:

22 голосов
/ 24 июля 2013

Если это новая ассоциация в существующей большой базе данных, я буду управлять переходом следующим образом:

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 и далее.

7 голосов
/ 16 февраля 2011

Возможно, это не самое чистое решение, но у нас уже была база данных с полмиллиона записей, некоторые из которых уже создали модель «Профиль», а некоторые - нет.Мы использовали этот подход, который гарантирует, что модель профиля присутствует в любой точке, без необходимости проходить и задним числом генерировать все модели профиля.

alias_method :db_profile, :profile
def profile
  self.profile = Profile.create(:user => self) if self.db_profile.nil?
  self.db_profile
end
4 голосов
/ 23 апреля 2012

Вот как я это делаю. Не уверен, насколько это стандартно, но он работает очень хорошо и ленив в том, что он не создает дополнительных накладных расходов, если только это не необходимо для создания новой ассоциации (я рад, что исправлю это):

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, но это, похоже, добавляет немного накладных расходов, так как он запускается каждый раз, когда объект инициализируется, и могут быть ситуации, когда вам не нужен доступ к ассоциации. Похоже, это пустая трата проверить его существование.

1 голос
/ 13 августа 2012

Для этого есть драгоценный камень:

https://github.com/jqr/has_one_autocreate

Похоже, сейчас оно немного старое. (не работает с рельсами3)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...