2010-09-18 7 views
2

J'ai quelque chose comme ceci:Comment définir une valeur par défaut has_one dans ActiveRecord?

class User < ActiveRecord::Base 
    has_one :profile 
end 

class Profile < ActiveRecord::Base 
    belongs_to :user 
end 

user = User.new 
user.profile.something #=> ERROR 

Qu'est-ce qu'une bonne façon de définir un objet de profil par défaut dans ce cas? J'ai essayé ceci:

class User < ActiveRecord::Base 
    default_scope :include => :profile 
    has_one :profile 

    def after_initialize 
    self.profile ||= Profile.new(:user => self) 
    end 
end 

... mais cela crée N + 1 requêtes. Des idées?

Mise à jour

C'est ce que j'ai maintenant, tout fonctionne bien, toujours à la recherche quelque chose de mieux:

class User < ActiveRecord::Base 
    default_scope :include => :profile 
    has_one :profile, :autosave => true 

    def after_initialize 
    self.profile = Profile.new(:user => self) if new_record? 
    end 
end 

De cette façon, vous allez avoir un profil à chaque fois que vous avez enfin votre create utilisateur. Sinon, le seul cas est un new_record?.

Répondre

0

La bonne réponse dépend de vos intentions, car il n'y a pas de solution directe à ce genre de problème.

Le rappel after_initialize est appelé après l'instanciation de l'objet, ce n'est donc pas un bon endroit pour ce type de logique.

Peut-être devriez-vous plutôt utiliser before_create/after_create? Ces rappels sont appelés uniquement au moment de la création de l'objet.

En outre, ne pas utiliser Profile.new, utilisez l'une des méthodes suivantes au lieu:

self.build_profile(...) 
self.create_profile(...) 

Dans le 2ème cas, le modèle est en cours d'enregistrement. Vous pouvez passer un hachage avec les attributs du modèle aux deux méthodes (ne pas passer: utilisateur, car il est défini automatiquement).

+0

Je pense à ceux-ci, mais en fait pour mon spécifi Dans le cas contraire, la classe de profil dépend de la sous-classe: 'class Admin

1

Je pense que votre réponse est bonne.J'ai une solution légèrement différente:

class User < ActiveRecord::Base 
    default_scope :include => :profile 
    has_one :profile 
    alias_method :my_profile, :profile 

    def my_profile 
    self.profile = Profile.create(:user => self) unless self.profile 
    self.profile 
    end 
end 

Bonne

  • créer un profil sur demande, pas sur instanciation

Pas si bon

  • Tu as d'utiliser my_profile (ou comme vous voulez l'appeler)
  • Le contrôle unless self.profile doit être fait sur chaque appel de profil
+0

merci, mais je ne suis pas un grand fan des méthodes d'aliasing comme ça, cela rend le sous-classement et l'inclusion de modules plus difficiles à gérer. –

3

Vous pouvez écrire votre propre profil de # utilisateur qui construira pour vous si elle doesn « existe:

class User < ActiveRecord::Base 
    has_one :profile 

    def profile_with_default 
    profile_without_default || build_profile 
    end 
    alias_method_chain :profile, :default 
end 
0

This est une bonne réponse:

class User < ActiveRecord::Base 
has_one :preference_set 

def preference_set 
    super || build_preference_set 
    end 
end 
Questions connexes