问题
I have an application where my users can have a set of preferences. Both are stored as ActiveRecord-models as follows:
class User < AR::Base
has_one :preference_set
end
class PreferenceSet < AR::Base
belongs_to :user
end
I can now access the preferences of a user:
@u = User.first
@u.preference_set => #<PreferenceSet...>
@u.preference_set.play_sounds => true
But this fails if a preference set is not already created, since @u.preference_set will be returning nil, and I'll be calling play_sounds
on nil
.
What I want to archive is that User.preference_set always returns a PreferenceSet instance. I've tried defining it like this:
class User < ..
has_one :preference_set
def preference_set
preference_set || build_preference_set
end
end
This is causing a 'Stack level too deep'
, since it is calling itself recursively.
My question is this:
How can I ensure that @user.preference_set
returns either the corresponding preference_set-record or, if none exists, builds a new one?
I know I could just rename my association (eg. preference_set_real
) and avoid recursive calls this way, but for the sake of simplicity in my app, I'd like to keep the naming.
Thanks!
回答1:
Well the best way to do this is to create the associated record when you create the primary one:
class User < ActiveRecord::Base
has_one :preference_set, :autosave => true
before_create :build_preference_set
end
That will set it up so whenever a User
is created, so is a PreferenceSet
. If you need to initialise the the associated record with arguments, then call a different method in before_create
which calls build_preference_set(:my_options => "here")
and then returns true
.
You can then just normalise all existing records by iterating over any that don't have a PreferenceSet
and building one by calling #create_preference_set
.
If you want to only create the PreferenceSet
when it is absolutely needed, then you can do something like:
class User < ActiveRecord::Base
has_one :preference_set
def preference_set_with_initialize
preference_set_without_initialize || build_preference_set
end
alias_method_chain :preference_set, :initialize
end
回答2:
or simply
class User < ApplicationRecord
has_one :preference_set
def preference_set
super || build_preference_set
end
end
This works because ActiveRecord defines the association method in a mixin.
来源:https://stackoverflow.com/questions/3812667/rails-create-association-if-none-is-found-to-avoid-nil-errors