I''m defining a table of profiles for a user. The user can have many profiles but only one of them can be flagged as "default". Right now, I just have a "is_default" boolean on the profiles table. I can see how: class Profile < ActiveRecord::Base validates_uniqueness_of :is_default, :scope => :user_id end would kinda work... but since it''s a boolean, I need one Profile record (per user!) to be true and the rest to be false. So I don''t think "validates_uniqueness_of" will work in this case. Also, is there a way to enforce this such that when a different profile is set as the default, the old default profile is updated automatically so it''s "is_default" field is set to false? Thoughts? Ideally, there''d be a plugin for this that would give a different slant on the uniqueness validator... maybe a "validates_singularity_of :is_default, :scope => :user_id" or something like that? -Dan --~--~---------~--~----~------------~-------~--~----~ 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 -~----------~----~----~----~------~----~------~--~---
Let me pose a question: Since a User can have many Profiles, and only one can be the Default Profile for a User at the moment, who (i.e., which model) should know that? A Profile doesn''t care whether it''s the current default for a User, the User does. I''d move the indicator for the default profile up to the User model, and make it a default_profile_id. -- 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 -~----------~----~----~----~------~----~------~--~---
Ar, Thanks for your reply... one way I''m using this is by defining Payment Profiles for users. In my case, there are debitable profiles and creditable profiles (i.e. one that you can take money from and one that you can pay money to). A user could have multiple of each but they would define one of them as the default. Then, I could do stuff like: user = User.find :first user.credit_profiles # => [PaymentProfile,...] user.default_credit_profile # => PaymentProfile (with the "is_default_credit" flag set to true) or even: user.payment_profiles # => [PaymentProfile,...] (debit and credits) Part of what makes this interesting is that a single payment profile can be both a debit profile and a credit profile. Otherwise, I would have just made each of them a model. Instead, I have PaymentProfile as a model and define "is_debit", "is_credit", "is_default_debit" and "is_default_credit" as booleans. Then, I use conditions on my has_one and has_many associations. Bottom line is: all of this works great (I even throw in polymorphism so things other than Users can have payment profiles). But, I have to manually manage the defaults. I.e. if the User sets PaymentProfile 5 to be the default debit, then I need to make sure that all other payment profiles for that user have the default debit set to false. It''s not much code, but I just wonder if there is already a validation for this thing. I would think it would be common enough that there would be... i.e. it''s a customized version of "validates_uniqueness_of" Does that help? -Dan On Mar 28, 12:37 pm, Ar Chron <rails-mailing-l...-ARtvInVfO7ksV2N9l4h3zg@public.gmane.org> wrote:> Let me pose a question: > > Since a User can have many Profiles, and only one can be the Default > Profile for a User at the moment, who (i.e., which model) should know > that? > > A Profile doesn''t care whether it''s the current default for a User, the > User does. I''d move the indicator for the default profile up to the > User model, and make it a default_profile_id. > -- > Posted viahttp://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 would handle this with a before_update filter. Basically you need to observe the field changing and unset all of the other profiles. I do this with an attribute accessor on the model: before_update :reset_is_default_if_changed attr_accessor :is_default_was def reset_is_default_if_changed if is_default and is_default != is_default_was user.profiles.select{|profile| profile.is_default==true}.each do | profile| profile.is_default=false end end end In the view you need a hidden field with the old is_default value named is_default_was Jon On Mar 28, 2:54 pm, Danimal <fightonfightw...-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> Ar, > > Thanks for your reply... one way I''m using this is by defining Payment > Profiles for users. In my case, there are debitable profiles and > creditable profiles (i.e. one that you can take money from and one > that you can pay money to). A user could have multiple of each but > they would define one of them as the default. Then, I could do stuff > like: > > user = User.find :first > user.credit_profiles # => [PaymentProfile,...] > user.default_credit_profile # => PaymentProfile (with the > "is_default_credit" flag set to true) > > or even: > > user.payment_profiles # => [PaymentProfile,...] (debit and credits) > > Part of what makes this interesting is that a single payment profile > can be both a debit profile and a credit profile. Otherwise, I would > have just made each of them a model. Instead, I have PaymentProfile as > a model and define "is_debit", "is_credit", "is_default_debit" and > "is_default_credit" as booleans. Then, I use conditions on my has_one > and has_many associations. > > Bottom line is: all of this works great (I even throw in polymorphism > so things other than Users can have payment profiles). But, I have to > manually manage the defaults. I.e. if the User sets PaymentProfile 5 > to be the default debit, then I need to make sure that all other > payment profiles for that user have the default debit set to false. > > It''s not much code, but I just wonder if there is already a validation > for this thing. I would think it would be common enough that there > would be... i.e. it''s a customized version of > "validates_uniqueness_of" > > Does that help? > > -Dan > > On Mar 28, 12:37 pm, Ar Chron <rails-mailing-l...-ARtvInVfO7ksV2N9l4h3zg@public.gmane.org> > wrote: > > > Let me pose a question: > > > Since a User can have many Profiles, and only one can be the Default > > Profile for a User at the moment, who (i.e., which model) should know > > that? > > > A Profile doesn''t care whether it''s the current default for a User, the > > User does. I''d move the indicator for the default profile up to the > > User model, and make it a default_profile_id. > > -- > > Posted viahttp://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 -~----------~----~----~----~------~----~------~--~---
Here is another option to consider. With this approach you wouldn''t need to keep track of a boolean value at all. Just let Rails associations keep track of the default user profile. Schema: ----------------------------------------- create_table "profiles", :force => true do |t| t.integer "user_id" t.string "name" t.datetime "created_at" t.datetime "updated_at" end create_table "users", :force => true do |t| t.string "username" t.string "first_name" t.string "last_name" t.integer "default_profile_id" t.datetime "created_at" t.datetime "updated_at" end ----------------------------------------- Models: ----------------------------------------- class Profile < ActiveRecord::Base belong_to :user end class User < ActiveRecord::Base has_many :profiles has_one :default_profile, :class_name => "Profile" end ----------------------------------------- On Mar 28, 5:31 pm, brewpoo <jloch...-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> I would handle this with a before_update filter. Basically you need > to observe the field changing and unset all of the other profiles. > > I do this with an attribute accessor on the model: > > before_update :reset_is_default_if_changed > > attr_accessor :is_default_was > > def reset_is_default_if_changed > if is_default and is_default != is_default_was > user.profiles.select{|profile| profile.is_default==true}.each do | > profile| > profile.is_default=false > end > end > end > > In the view you need a hidden field with the old is_default value > named is_default_was > > Jon > > On Mar 28, 2:54 pm, Danimal <fightonfightw...-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote: > > > Ar, > > > Thanks for your reply... one way I''m using this is by defining Payment > > Profiles for users. In my case, there are debitable profiles and > > creditable profiles (i.e. one that you can take money from and one > > that you can pay money to). A user could have multiple of each but > > they would define one of them as the default. Then, I could do stuff > > like: > > > user = User.find :first > > user.credit_profiles # => [PaymentProfile,...] > > user.default_credit_profile # => PaymentProfile (with the > > "is_default_credit" flag set to true) > > > or even: > > > user.payment_profiles # => [PaymentProfile,...] (debit and credits) > > > Part of what makes this interesting is that a single payment profile > > can be both a debit profile and a credit profile. Otherwise, I would > > have just made each of them a model. Instead, I have PaymentProfile as > > a model and define "is_debit", "is_credit", "is_default_debit" and > > "is_default_credit" as booleans. Then, I use conditions on my has_one > > and has_many associations. > > > Bottom line is: all of this works great (I even throw in polymorphism > > so things other than Users can have payment profiles). But, I have to > > manually manage the defaults. I.e. if the User sets PaymentProfile 5 > > to be the default debit, then I need to make sure that all other > > payment profiles for that user have the default debit set to false. > > > It''s not much code, but I just wonder if there is already a validation > > for this thing. I would think it would be common enough that there > > would be... i.e. it''s a customized version of > > "validates_uniqueness_of" > > > Does that help? > > > -Dan > > > On Mar 28, 12:37 pm, Ar Chron <rails-mailing-l...-ARtvInVfO7ksV2N9l4h3zg@public.gmane.org> > > wrote: > > > > Let me pose a question: > > > > Since a User can have many Profiles, and only one can be the Default > > > Profile for a User at the moment, who (i.e., which model) should know > > > that? > > > > A Profile doesn''t care whether it''s the current default for a User, the > > > User does. I''d move the indicator for the default profile up to the > > > User model, and make it a default_profile_id. > > > -- > > > Posted viahttp://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 -~----------~----~----~----~------~----~------~--~---
Incidentally this approach also works for multiple defaults. Just add as many foreign keys to User as needed. Give them unique names such as default_credit_id, default_debt_id, etc. On Mar 28, 7:45 pm, Robert Walker <r0b3rt4...-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> Here is another option to consider. With this approach you wouldn''t > need to keep track of a boolean value at all. Just let Rails > associations keep track of the default user profile. > > Schema: > ----------------------------------------- > create_table "profiles", :force => true do |t| > t.integer "user_id" > t.string "name" > t.datetime "created_at" > t.datetime "updated_at" > end > > create_table "users", :force => true do |t| > t.string "username" > t.string "first_name" > t.string "last_name" > t.integer "default_profile_id" > t.datetime "created_at" > t.datetime "updated_at" > end > ----------------------------------------- > > Models: > ----------------------------------------- > class Profile < ActiveRecord::Base > belong_to :user > end > > class User < ActiveRecord::Base > has_many :profiles > has_one :default_profile, :class_name => "Profile" > end > ----------------------------------------- > > On Mar 28, 5:31 pm, brewpoo <jloch...-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote: > > > I would handle this with a before_update filter. Basically you need > > to observe the field changing and unset all of the other profiles. > > > I do this with an attribute accessor on the model: > > > before_update :reset_is_default_if_changed > > > attr_accessor :is_default_was > > > def reset_is_default_if_changed > > if is_default and is_default != is_default_was > > user.profiles.select{|profile| profile.is_default==true}.each do | > > profile| > > profile.is_default=false > > end > > end > > end > > > In the view you need a hidden field with the old is_default value > > named is_default_was > > > Jon > > > On Mar 28, 2:54 pm, Danimal <fightonfightw...-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote: > > > > Ar, > > > > Thanks for your reply... one way I''m using this is by defining Payment > > > Profiles for users. In my case, there are debitable profiles and > > > creditable profiles (i.e. one that you can take money from and one > > > that you can pay money to). A user could have multiple of each but > > > they would define one of them as the default. Then, I could do stuff > > > like: > > > > user = User.find :first > > > user.credit_profiles # => [PaymentProfile,...] > > > user.default_credit_profile # => PaymentProfile (with the > > > "is_default_credit" flag set to true) > > > > or even: > > > > user.payment_profiles # => [PaymentProfile,...] (debit and credits) > > > > Part of what makes this interesting is that a single payment profile > > > can be both a debit profile and a credit profile. Otherwise, I would > > > have just made each of them a model. Instead, I have PaymentProfile as > > > a model and define "is_debit", "is_credit", "is_default_debit" and > > > "is_default_credit" as booleans. Then, I use conditions on my has_one > > > and has_many associations. > > > > Bottom line is: all of this works great (I even throw in polymorphism > > > so things other than Users can have payment profiles). But, I have to > > > manually manage the defaults. I.e. if the User sets PaymentProfile 5 > > > to be the default debit, then I need to make sure that all other > > > payment profiles for that user have the default debit set to false. > > > > It''s not much code, but I just wonder if there is already a validation > > > for this thing. I would think it would be common enough that there > > > would be... i.e. it''s a customized version of > > > "validates_uniqueness_of" > > > > Does that help? > > > > -Dan > > > > On Mar 28, 12:37 pm, Ar Chron <rails-mailing-l...-ARtvInVfO7ksV2N9l4h3zg@public.gmane.org> > > > wrote: > > > > > Let me pose a question: > > > > > Since a User can have many Profiles, and only one can be the Default > > > > Profile for a User at the moment, who (i.e., which model) should know > > > > that? > > > > > A Profile doesn''t care whether it''s the current default for a User, the > > > > User does. I''d move the indicator for the default profile up to the > > > > User model, and make it a default_profile_id. > > > > -- > > > > Posted viahttp://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 -~----------~----~----~----~------~----~------~--~---
Robert and Jon, Thank you both! It''s funny, I was leaning towards Jon''s idea because I was using polymorphism on the models that have profiles, but I think Robert''s approach may be simpler. It means I have to define a FK for each table that has a profile, but that will be fine. The cool thing is I can do both... I''m using polymorphism for some has_many references... i.e. a User has_many profiles but also a User has_one default_profile. Man, I love Rails! Woo! -Dan --~--~---------~--~----~------------~-------~--~----~ 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 -~----------~----~----~----~------~----~------~--~---
And now I''ve waffled back. I found the Recipe #29 in Advanced Rails Recipes: Create Meaningful Relationships Through Proxies. So now I''ve defined on the User class: has_many :profiles, :as => :profilable do def default(reload=false) @default_profile = nil if reload @default_profile ||= find_by_is_default(true) end end And this lets me do stuff like: User.find(:first).profiles.default # => a Profile object that is flagged with the :is_default boolean. (which is nicely similar to: User.find(:first).profiles.first and other such calls) The main reason is that I''m using polymorphism on the objects that have profiles, so I don''t have to define a FK on each table. I like the cleanness of this although it means I''ll need to do Jon''s logic to ensure that when a new default is set, the old is unset. But as the number of profiles per user should be very small I''m not worried. So functionally, it really does come back to: a) do I put a FK on each table that has a default Profile and use that for referencing the default Profile object? - upside: simple association assigment. Nice, neat. - downside: additional field on table, loses some functionality? b) do I put a boolean in the Profiles table and use the proxy idea for referencing the object? - upside: make for a nice call: i.e. like .first I can now do .default (for an association of Profiles). I can use polymorphism fully, so the User table doesn''t have anything referencing Profiles and it''s all handled in the models. - downside: need to handle "singularity" of the is_default field. Performance issues? Ultimately, I realize that either will work very well. so I dunno. I don''t want to think about it too much anymore. I still think that my original idea is interesting: building a "validates_singularity_of" helper. Then, it would just to Jon''s code, and could include scoping constraints, i.e. something like: validates_singularity_of :is_default, :scope => :user_id or in my case, where I''m using polymorphism I''d have: validates_singularity_of :is_default, :scope => :profilable_id and maybe a :force => true option that instead of returning a validation error would enforce the singularity, i.e. set the object and unset all the others within the scope. Hmmm... choices choices I guess I''ll build out my controllers and views for this and see if that guides it one way or the other. --~--~---------~--~----~------------~-------~--~----~ 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 -~----------~----~----~----~------~----~------~--~---