Dan Tenenbaum
2006-May-08 21:19 UTC
[Rails] enforcing special behavior of child rows in HABTM
Hi, I have a scenario where a doctor can have one or more specialties. For each doctor, one and only one of her specialties can be designated as primary. So I have tables called doctors, specialties, and doctors_specialties, the last of which has a boolean is_primary column. The doctor model class specifies that: has_and_belongs_to_many :specialties I want to enforce, at the lowest possible level, the constraint that a doctor can only have one primary specialty. So, whenever a member of the association is added or updated with the value is_primary set to true, code is run to first set all records in the association to primary = false, thus ensuring that the row about to be saved is the only one with is_primary = true. I looked into passing a code block to the HABTM, but couldn''t quite get that to work. Anyone have any suggestions? This is for a demo to show how one would do this in Rails as opposed to existing code that does it in .NET. So I''d like the solution to be very elegant and Rails-esque. Can someone help? Thanks -- Posted via http://www.ruby-forum.com/.
Ray Baxter
2006-May-08 23:02 UTC
[Rails] Re: enforcing special behavior of child rows in HABTM
Dan Tenenbaum wrote:> I have a scenario where a doctor can have one or more specialties. > For each doctor, one and only one of her specialties can be designated > as primary.> So I have tables called doctors, specialties, and doctors_specialties, > the last of which has a boolean is_primary column. > > The doctor model class specifies that: > has_and_belongs_to_many :specialties > > I want to enforce, at the lowest possible level, the constraint that > a doctor can only have one primary specialty.One way: rename doctors_specialties to something like specializations and make it a full-fledged model. Then replace your habtm relationship with a has_many :through relationship. The specialization model can enforce the uniqueness with either a validation or a before filter. class doctor < AR::B has_many :specializations has_many :specialties, :through => :specializations end class specialization < AR::B belongs_to :doctors belongs_to :specialties validates_uniqueness_of :primary, :scope => :doctor_id end class specialty < AR::B has_many :specializations has_many :doctors, :through => :specializations end> So, whenever a member of the association is added or updated with the > value is_primary set to true, code is run to first set all records in > the association to primary = false, thus ensuring that the row about to > be saved is the only one with is_primary = true.The above doesn''t handle unsetting the other specialties. You would have to add a before_save filter to do that. -- Ray
Ray Baxter
2006-May-08 23:15 UTC
[Rails] Re: enforcing special behavior of child rows in HABTM
Ray Baxter wrote:> validates_uniqueness_of :primary, :scope => :doctor_idThat won''t work. There are only two values of primary, true and false. Under this logic there can only be two specialties. Go with the filter. -- Ray
Dan Tenenbaum
2006-May-08 23:41 UTC
[Rails] Re: enforcing special behavior of child rows in HABTM
Ray Baxter wrote:> Ray Baxter wrote: > >> validates_uniqueness_of :primary, :scope => :doctor_id > > That won''t work. There are only two values of primary, true and false. > Under this logic there can only be two specialties. Go with the filter. > > -- > > RayThis works great. Thanks. -- Posted via http://www.ruby-forum.com/.
Dan Tenenbaum
2006-May-09 18:03 UTC
[Rails] Re: enforcing special behavior of child rows in HABTM
Running into a problem...original post (with code) as at bottom, for context. The problem is, I am not sure how to elegantly add to a doctor''s "specialties" collection. I can construct a new Specialization and save it, but that doesn''t seem like the Rails way. When I try this:>> d = Doctor.find 1=> #<Doctor:0xb77f07b0 @attributes={"name"=>"name", "title"=>"title", "id"=>"1"}>>> s = Specialty.find 1=> #<Specialty:0xb77eaa68 @attributes={"name"=>"neuro", "id"=>"1"}>>> s.is_primary = true=> true>> d.specialties<<s=> [#<Specialty:0xb77eaa68 @attributes={"name"=>"neuro", "id"=>"1"}, @is_primary=true>]>> Specialization.find_all=> [] As you can see, nothing is written to Specializations. Similarly:>> d.specializations<<sActiveRecord::AssociationTypeMismatch: Specialization expected, got Specialty I thought perhaps my before_save method was the culprit, and got rid of it temporarily, but still got the same behavior. Any thoughts? Thanks. Original post: Ray Baxter wrote:> Dan Tenenbaum wrote: > >> I have a scenario where a doctor can have one or more specialties. >> For each doctor, one and only one of her specialties can be designated >> as primary. > >> So I have tables called doctors, specialties, and doctors_specialties, >> the last of which has a boolean is_primary column. >> >> The doctor model class specifies that: >> has_and_belongs_to_many :specialties >> >> I want to enforce, at the lowest possible level, the constraint that >> a doctor can only have one primary specialty. > > One way: rename doctors_specialties to something like specializations > and make it a full-fledged model. Then replace your habtm relationship > with a has_many :through relationship. The specialization model can > enforce the uniqueness with either a validation or a before filter. > > class Doctor < AR::B > has_many :specializations > has_many :specialties, :through => :specializations > end > > class Specialization < AR::B > belongs_to :doctors > belongs_to :specialties > # before_save filter which handles enforcement goes here > end > > class Specialty < AR::B > has_many :specializations > has_many :doctors, :through => :specializations > attr_accessor :is_primary > end > >> So, whenever a member of the association is added or updated with the >> value is_primary set to true, code is run to first set all records in >> the association to primary = false, thus ensuring that the row about to >> be saved is the only one with is_primary = true.-- Posted via http://www.ruby-forum.com/.
Eden Brandeis
2006-May-10 00:33 UTC
[Rails] Re: enforcing special behavior of child rows in HABTM
Do you need to save the doctor for the connection to be made? d.save On 5/9/06, Dan Tenenbaum <dandante@dandante.com> wrote:> > Running into a problem...original post (with code) as at bottom, for > context. > > The problem is, I am not sure how to elegantly add to a doctor''s > "specialties" collection. I can construct a new Specialization and save > it, but that doesn''t seem like the Rails way. When I try this: > > >> d = Doctor.find 1 > => #<Doctor:0xb77f07b0 @attributes={"name"=>"name", "title"=>"title", > "id"=>"1"}> > >> s = Specialty.find 1 > => #<Specialty:0xb77eaa68 @attributes={"name"=>"neuro", "id"=>"1"}> > >> s.is_primary = true > => true > >> d.specialties<<s > => [#<Specialty:0xb77eaa68 @attributes={"name"=>"neuro", "id"=>"1"}, > @is_primary=true>] > >> Specialization.find_all > => [] > > As you can see, nothing is written to Specializations. > > Similarly: > >> d.specializations<<s > ActiveRecord::AssociationTypeMismatch: Specialization expected, got > Specialty > > I thought perhaps my before_save method was the culprit, and got rid of > it temporarily, but still got the same behavior. Any thoughts? > Thanks. > > Original post: > Ray Baxter wrote: > > Dan Tenenbaum wrote: > > > >> I have a scenario where a doctor can have one or more specialties. > >> For each doctor, one and only one of her specialties can be designated > >> as primary. > > > >> So I have tables called doctors, specialties, and doctors_specialties, > >> the last of which has a boolean is_primary column. > >> > >> The doctor model class specifies that: > >> has_and_belongs_to_many :specialties > >> > >> I want to enforce, at the lowest possible level, the constraint that > >> a doctor can only have one primary specialty. > > > > One way: rename doctors_specialties to something like specializations > > and make it a full-fledged model. Then replace your habtm relationship > > with a has_many :through relationship. The specialization model can > > enforce the uniqueness with either a validation or a before filter. > > > > class Doctor < AR::B > > has_many :specializations > > has_many :specialties, :through => :specializations > > end > > > > class Specialization < AR::B > > belongs_to :doctors > > belongs_to :specialties > > # before_save filter which handles enforcement goes here > > end > > > > class Specialty < AR::B > > has_many :specializations > > has_many :doctors, :through => :specializations > > attr_accessor :is_primary > > end > > > >> So, whenever a member of the association is added or updated with the > >> value is_primary set to true, code is run to first set all records in > >> the association to primary = false, thus ensuring that the row about to > >> be saved is the only one with is_primary = true. > > -- > Posted via http://www.ruby-forum.com/. > _______________________________________________ > Rails mailing list > Rails@lists.rubyonrails.org > http://lists.rubyonrails.org/mailman/listinfo/rails >-------------- next part -------------- An HTML attachment was scrubbed... URL: http://wrath.rubyonrails.org/pipermail/rails/attachments/20060510/d314e833/attachment-0001.html
Dan Tenenbaum
2006-May-10 01:06 UTC
[Rails] Re: Re: enforcing special behavior of child rows in HABTM
Eden Brandeis wrote:> Do you need to save the doctor for the connection to be made? > > d.saveNo, that didn''t make a difference...... -- Posted via http://www.ruby-forum.com/.
Eden Brandeis
2006-May-10 01:13 UTC
[Rails] Re: Re: enforcing special behavior of child rows in HABTM
Dan, Did you switch to a join model? If so, then this probably answers your question: http://blog.hasmanythrough.com/articles/read/150 On 5/9/06, Dan Tenenbaum <dandante@dandante.com> wrote:> > Eden Brandeis wrote: > > Do you need to save the doctor for the connection to be made? > > > > d.save > > No, that didn''t make a difference...... > > > -- > Posted via http://www.ruby-forum.com/. > _______________________________________________ > Rails mailing list > Rails@lists.rubyonrails.org > http://lists.rubyonrails.org/mailman/listinfo/rails >-------------- next part -------------- An HTML attachment was scrubbed... URL: http://wrath.rubyonrails.org/pipermail/rails/attachments/20060510/a1ba0afe/attachment.html
Dan Tenenbaum
2006-May-10 01:22 UTC
[Rails] Re: Re: Re: enforcing special behavior of child rows in HABT
Eden Brandeis wrote:> Dan, > > Did you switch to a join model? If so, then this probably answers your > question: > > http://blog.hasmanythrough.com/articles/read/150Awesome...that answers the question quite thoroughly. I had no idea there was a whole site devoted to has_many :through. -- Posted via http://www.ruby-forum.com/.