I''d like to be able to have an association method return a default
blank ActiveRecord object rather than ''nil'' if the association
doesn''t
exist in the database yet. For example:
my_booking = Booking.find(10)
location = my_booking.origin_location #<-- I want this to never return nil
I''ve set up my model like this (taking out irrelevant stuff):
class Booking < ActiveRecord::Base
belongs_to :origin_location, :class_name => "Location",
:foreign_key
=> "origin_location_id"
belongs_to :destination_location, :class_name => "Location",
:foreign_key => "destination_location_id"
alias :old_origin_location :origin_location
alias :old_destination_location :destination_location
def origin_location
@origin_location ||= (old_origin_location || Location.new)
end
def destination_location
@destination_location ||= (old_destination_location || Location.new)
end
end
The trouble with this code is that by assigning @origin_location and
@destination_location to Location.new, I''ve short-circuited the
built-in association functions. By so doing, my new Location object
does not have the same methods as the Location object would have if it
were created through the magical association methods. My new
Location, for example, does not have the "construct_sql" method which
is necessary for the auto-saving to occur when my_booking.save is
called.
Does anyone know how to solve this problem? And if not, is there
another way to make an association have a ''default object''
returned?
Cheers,
Duane Johnson
Michael Koziarski
2005-Jan-27 02:54 UTC
Re: Making an association return a default object?
If you''re only dealing with ''created'' objects. i.e ones returned by find. You could simply use the DDL to handle this: CREATE TABLE bookings ( id ... ... ... origin_location_id integer DEFAULT 0, ); Then make the entry for ''0'' be your special ''default blank one''. If you want to work with objects that are created too, you could use the callbacks: def after_create self.origin_location ||= Location.default_value end All this assumes there''s no magic ActiveRecord incantation for it. ;) On Thu, 27 Jan 2005 02:41:42 +0000, Duane Johnson <duane.johnson-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> I''d like to be able to have an association method return a default > blank ActiveRecord object rather than ''nil'' if the association doesn''t > exist in the database yet. For example: > > my_booking = Booking.find(10) > location = my_booking.origin_location #<-- I want this to never return nil > > I''ve set up my model like this (taking out irrelevant stuff): > > class Booking < ActiveRecord::Base > belongs_to :origin_location, :class_name => "Location", :foreign_key > => "origin_location_id" > belongs_to :destination_location, :class_name => "Location", > :foreign_key => "destination_location_id" > > alias :old_origin_location :origin_location > alias :old_destination_location :destination_location > > def origin_location > @origin_location ||= (old_origin_location || Location.new) > end > > def destination_location > @destination_location ||= (old_destination_location || Location.new) > end > end > > The trouble with this code is that by assigning @origin_location and > @destination_location to Location.new, I''ve short-circuited the > built-in association functions. By so doing, my new Location object > does not have the same methods as the Location object would have if it > were created through the magical association methods. My new > Location, for example, does not have the "construct_sql" method which > is necessary for the auto-saving to occur when my_booking.save is > called. > > Does anyone know how to solve this problem? And if not, is there > another way to make an association have a ''default object'' returned? > > Cheers, > Duane Johnson > _______________________________________________ > Rails mailing list > Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org > http://lists.rubyonrails.org/mailman/listinfo/rails >-- Cheers Koz
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Duane Johnson wrote:> alias :old_origin_location :origin_location > > def origin_location > @origin_location ||= (old_origin_location || Location.new) > enddef origin_location old_origin_location or build_origin_location end jeremy -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.2.6 (Darwin) Comment: Using GnuPG with Thunderbird - http://enigmail.mozdev.org iD8DBQFB+FieAQHALep9HFYRAgwwAJ0U66TuD7gAjXy1FQOqubmJ+AE4QQCdEHFl hhb3XyS7uHhBQGp/L6w2HIQ=5Alz -----END PGP SIGNATURE-----
Thank-you both Jeremy and Michael. You''ve opened my darkened and
weary eyes :o) This has literally taken me more than two hours to
figure out!
Jeremy, I tried the code you wrote as follows:
alias :old_origin_location :origin_location
def origin_location
old_origin_location or build_origin_location
end
alias :old_destination_location :destination_location
def destination_location
old_destination_location or build_destination_location
end
However, I get the following error:
NameError in Bookings#new
undefined local variable or method `build_origin_location'' for
#<Booking:0x8fde374>
Am I missing something?
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Duane Johnson wrote:> alias :old_origin_location :origin_location > > def origin_location > old_origin_location or build_origin_location > end > > However, I get the following error: > NameError in Bookings#new > undefined local variable or method `build_origin_location'' for > #<Booking:0x8fde374> > > Am I missing something?Ah, this will work for has_one (composition), not for belongs_to (reference). For belongs_to, you must create the associated Location before you may reference it: def origin_location old_origin_location or self.origin_location = Location.create end It works for has_one because the reference (foreign key) is in the other direction, from the newly associated object to its parent. Thus you can set the id on the new object before saving it. Tim Bates did substantial work removing this restriction so perhaps he has a better way. Unfortunately some technical issues prevent being able to say booking.origin_location.build unless the origin_location already exists (ironic, eh?) Perhaps booking.association(:origin_location).build calling build on the underlying BelongsToAssociation would be a reasonable compromise. jeremy -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.2.6 (Darwin) Comment: Using GnuPG with Thunderbird - http://enigmail.mozdev.org iD8DBQFB+IXKAQHALep9HFYRAr3SAKCUyLZuhrcVC/fAsE67ckJJRLbioQCgvHHu RmObBxX4fRllNdCd2+0rYJA=V+UH -----END PGP SIGNATURE-----