Carmine Moleti
2007-Feb-07 16:31 UTC
How to Rippling validation effects from child to parent?
Hi to everyone, I''m new to rails and am experiencing problems with validations. I have the following models: class Client < ActiveRecord::Base validates_presence_of :cognome, :nome, :data_di_nascita,:badge has_one :badge end class Badge < ActiveRecord::Base belongs_to :client validates_presence_of :numero validates_numericality_of :numero, :only_integer => true validates_uniqueness_of :numero validates_presence_of :tipo private def validate errors.add(:client_id,"Badge già assegnato") if (self.client_id !nil) and (Badge.find(:all, :conditions => "client_id #{self.client_id}").size > 1) end end Whenever I try assigning the same client_id to two different Badge''s instances, and then issue a "save!", I get an exception as expected. If I try the following: c,d = Client.find(:all) b1,b2 = Badge.find(:all) c.badge = b1 d.badge = b1 c.save! d.save! No exception gets raised even though b1.errors.size == 1! Shouldn''t this error ripple back to the Client instances and raise an exception upon calling "save!"? I''ve found in the Rails API that there''s a "validates_associates" method that extends validations checking to model''s associates. Is this one the way to go? Thanks in advance for your help. Regards, Carmine -- Posted via http://www.ruby-forum.com/. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe@googlegroups.com For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
On Feb 7, 9:31 am, Carmine Moleti <rails-mailing-l...-ARtvInVfO7ksV2N9l4h3zg@public.gmane.org> wrote:> class Client < ActiveRecord::Base > validates_presence_of :cognome, :nome, :data_di_nascita,:badge > has_one :badge > > endWhen you validate the presence of an association refer directly to the column name: validates_presence_of :cognome, :nome, :data_di_nascita,:badge_id> def validate > errors.add(:client_id,"Badge già assegnato") if (self.client_id !> nil) and (Badge.find(:all, :conditions => "client_id > #{self.client_id}").size > 1) > endIt looks like your validate method is trying to ensure the uniqueness of client_id if it''s not nil. You can do that all with validates_uniqueness_of: validates_uniqueness_of :client_id, :allow_nil => true Aaron --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe@googlegroups.com For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
Carmine Moleti
2007-Feb-07 19:45 UTC
Re: How to Rippling validation effects from child to parent?
Aaron, first off thanks for your reply. Aaron wrote:> On Feb 7, 9:31 am, Carmine Moleti <rails-mailing-l...-ARtvInVfO7ksV2N9l4h3zg@public.gmane.org> > wrote: >> class Client < ActiveRecord::Base >> validates_presence_of :cognome, :nome, :data_di_nascita,:badge >> has_one :badge >> >> end > > When you validate the presence of an association refer directly to the > column name: > > validates_presence_of :cognome, :nome, :data_di_nascita,:badge_idIf I use the validates you proposed above, then upon validating Rails complains about "Client" not having any "badge_id". That means that writing something like: c,d = Client.find(:all) b1,b2 = Badge.find(:all) where both "c" and "d" have "badge" set to nil will produce: c.valid? -> false d.valid? -> false which is ok. While doing: c.badge = b1 d.badge = b2 c.valid? -> false which is not ok. :) This doesn''t happen if I exclude from "validates_presence_of" the ":badge_id" field.>> def validate >> errors.add(:client_id,"Badge gi� assegnato") if (self.client_id !>> nil) and (Badge.find(:all, :conditions => "client_id >> #{self.client_id}").size > 1) >> end > > It looks like your validate method is trying to ensure the uniqueness > of client_id if it''s not nil. You can do that all with > validates_uniqueness_of: > > validates_uniqueness_of :client_id, :allow_nil => trueThanks, this was nicer :) Sadly I''ve not solved my problem yet. What I would like to achieve is: - Each "Client" has to have one and only one Badge - Each "Badge" can be associated at one Client only, or it can be left unassociated It seemed to me that the way to go was to: - Declare that Client has_one :badge - Declare that Badge belongs_to :client - Validate uniqueness of :client_id (allowing for nil) on the Badge model Thus far I can''t work it out. -- Posted via http://www.ruby-forum.com/. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe@googlegroups.com For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
Sorry about the mistake on the has_one :badge association. I forgot that the foreign key reference is on the belongs_to side. I took another look at your models and what you want to accomplish and came up with this: class Client < ActiveRecord::Base has_one :badge # ensure client has a badge def validate errors.add_to_base(''Client must have a badge!'') if badge.nil? end end class Badge < ActiveRecord::Base belongs_to :client # ensure client has only one badge validates_uniqueness_of :client_id, :allow_nil => true end When validating that a Client has a Badge there is no attribute to associate the error with so use "add_to_base" to invalidate the entire object if the badge association is not present. Aaron --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
Carmine Moleti
2007-Feb-07 23:00 UTC
Re: How to Rippling validation effects from child to parent?
Aaron wrote:> Sorry about the mistake on the has_one :badge association. I forgot > that the foreign key reference is on the belongs_to side.Don''t worry Aaron :) I can only thank you for trying helping me.> I took another look at your models and what you want to accomplish and > came up with this: > > class Client < ActiveRecord::Base > has_one :badge > > # ensure client has a badge > def validate > errors.add_to_base(''Client must have a badge!'') if badge.nil? > end > end > > class Badge < ActiveRecord::Base > belongs_to :client > # ensure client has only one badge > validates_uniqueness_of :client_id, :allow_nil => true > end > > When validating that a Client has a Badge there is no attribute to > associate the error with so use "add_to_base" to invalidate the entire > object if the badge association is not present.This sounds nice. Still I''m back to square one. I forgot to say that no two or more different clients can have the same badge associated. This is something I think has to be worked out defining a validate in the Client model, this way: def validate errors.add_to_base("client must have a badge!") if badge.nil? errors.add_to_base("badge already taken!") if Badge.count(:conditions => "client_id = #{self.id}") > 0 end Doing this way, the tests produce no errors and the expected results. So, is this the only way to go, or are there any (perhaps better) one roads to choose? Thanks again for your precious help. Regards, Carmine -- Posted via http://www.ruby-forum.com/. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
Carmine Moleti
2007-Feb-07 23:19 UTC
Re: How to Rippling validation effects from child to parent?
> I forgot to say that no two or more different clients can have the same > badge associated. > This is something I think has to be worked out defining a validate in > the Client model, this way: > > def validate > errors.add_to_base("client must have a badge!") if badge.nil? > errors.add_to_base("badge already taken!") if Badge.count(:conditions > => "client_id = #{self.id}") > 0 > end > > Doing this way, the tests produce no errors and the expected results. > So, is this the only way to go, or are there any (perhaps better) one > roads to choose?Just spoke too soon :( As soon as "validate" gets called, a "save" has already been issued on the associated badge''s instance. Guess it''s time to sleep :( -- Posted via http://www.ruby-forum.com/. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
> I forgot to say that no two or more different clients can have the same > badge associated. > This is something I think has to be worked out defining a validate in > the Client model, this way: >If I understand your database schema correctly its impossible for two clients to have the same badge. Your badges table has a "client_id" column, right? So a particular badge can only be associated with 1 client at a time, there''s no need to validate something that can never happen. Your other concern is preventing a client from having more than 1 badge. That''s what this line in the Badge model takes care of: validates_uniqueness_of :client_id, :allow_nil => true Now you may want to prevent a client from taking a badge that is already in use by another client. That''s what it looks like your trying to do with this line: errors.add_to_base("badge already taken!") if Badge.count(:conditions => "client_id = #{self.id}") > 0 However, this line is checking that current client does not already have another badge assigned. Which is already addressed by the validates_uniqueness_of statement I mentioned above. This is not exactly pretty, but it should work: def validate if badge.nil? # don''t let the badge association be nil errors.add_to_base("client must have a badge!") else # pull the old version of badge from the database old_badge = Badge.find(badge.id) # if the old badge already had a client_id set and its not for this client then add an error errors.add_to_base("badge already taken!") if old_badge.client_id and old_badge.client_id != id end end Aaron --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
Carmine Moleti
2007-Feb-08 06:48 UTC
Re: How to Rippling validation effects from child to parent?
Your assumptions and understanding of what I wanted to do, are right. Aaron wrote:> Now you may want to prevent a client from taking a badge that is > already in use by another client. That''s what it looks like your > trying to do with this line: > errors.add_to_base("badge already taken!") if Badge.count(:conditions > => "client_id = #{self.id}") > 0Desperate attempt! :)> However, this line is checking that current client does not already > have another badge assigned. Which is already addressed by the > validates_uniqueness_of statement I mentioned above.> This is not exactly pretty, but it should work: > def validate > if badge.nil? > # don''t let the badge association be nil > errors.add_to_base("client must have a badge!") > else > # pull the old version of badge from the database > old_badge = Badge.find(badge.id) > # if the old badge already had a client_id set and its not for > this client then add an error > errors.add_to_base("badge already taken!") if old_badge.client_id > and old_badge.client_id != id > end > endNope, this doesn''t work. At least it doesn''t work the way I would like to. I mean, let''s say there are 2 clients: "c" and "d", and 2 badges "b1" and "b2". At first, we have c.badge == b1, and d.badge == nil. c.valid? -> true d.valid? ->false When I issue: d.badge = b1 (which is already been taken by "c") I obtain: c.valid? -> false d.valid? -> true Now it _seems_ that every and each conditions posed is respected _but_, rails messed up with other consolidate row in the database. When I did: d.badge = b1, I was expecting rails to leave "d" in a not valid state, not to change c and make d valid! :) Now, I''m a total newbie of Rails (sill reading the book from pragmatic programmers), but this thing is driving me insane :) Your turn ;) -- Posted via http://www.ruby-forum.com/. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
Carmine Moleti
2007-Feb-08 09:19 UTC
Re: How to Rippling validation effects from child to parent?
Well, it seems that overriding default accessors for badge in the Client model helps solving things. I mean, rewriting Client model like this: class Client < ActiveRecord::Base has_one :badge validates_presence_of :nome def badge @badge end def badge=(value) if Badge.find(:all, :conditions => "client_id <> #{self.id} and id = #{value.id}").size > 0 then errors.add_to_base("Badge is already taken!") @badge = nil else @badge = value end end private def validate errors.add_to_base("Badge can''t be blank!") if self.badge.nil? end end Almost works like I would like to. There''s only one thing to sort out. When assigning the same badge to two different clients, now, I would expect to have two error messages added to the "base". Well, this seems not to happen. A little step forward :) -- Posted via http://www.ruby-forum.com/. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
Carmine Moleti
2007-Feb-08 21:27 UTC
Re: How to Rippling validation effects from child to parent?
Let''s put the "End" word to this. My assumptions were completely wrong. I thought that "has_one" worked quite the opposite way it really does. Swapping "has_many" and "belongs_to" in the models mentioned in this thread, reduced the amount of code and, what''s more important, it made everything work as expected. That''s what means to be a newbie ;) -- Posted via http://www.ruby-forum.com/. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---