Hi all, I found what I think is a slick solution to a problem of mine but I''d like to know if there is a better way to accomplish what I did. I have 2 tables: Users: id category # can be either ''ADMIN'', ''AUDITOR'' or ''TENANT'' Audits: id auditor_id tenant_id I need the audits to belong to both an AUDITOR and a TENANT user: class User < ActiveRecord::Base has_many :audits end class Audit < ActiveRecord::Base belongs_to :user # !!! This does not work even using :class_name, etc. !!! end After tinkering for a while with it I couldn''t find an easy way of making it work. Then I had an idea that has worked and I think is pretty slick. I created 3 models: class Admin < User # Admin specific functionality here end class Auditor < User has_many :audits # Auditor specific functionality here end class Tenant < User has_many :audits # Tenant specific functionality here end When a user is retrieved for access and functionality validations, I re-retrieve the user through the specific user class as in: user = User.find(...) # The user is found and passes validations. # Now I re-retrieve it through the specific user class based in the category value. user = user.category.capitalize.constantize.find(user.id) if user From that moment on I have the user retrieved through its correct type and my associations work wonderfully. Is there a better way of making this work? Thanks. Pepe
> Users: > id > category # can be either ''ADMIN'', ''AUDITOR'' or ''TENANT'' > > Audits: > id > auditor_id > tenant_id >Looking at this original Table format you have two foreign_keys in auditor and tenant. auditor_id matches user.id tenant_id matches user.id admin_id matches user.id> class User < ActiveRecord::Base > has_many :audits > endYou can design your User table and use self-referential associations within a given model. class User < ActiveRecord::Base has_many :audits has_many :auditors, :through => :audits has_many :tenants, :through => :audits end> class Audit < ActiveRecord::Base > belongs_to :user # !!! This does not work even using :class_name, > etc. !!! > endclass Audit < ActiveRecord::Base belongs_to :user belongs_to :auditor, :class_name => "User" belongs_to :tenant, :class_name => "User" end Using this format you can define any method in your Audit model with something similar to: :joins => [:user, :auditor, :tenant] .. which will join those self-referential tables together And then in your controller if you use: @audits = Audit.all :include => [:user, :auditor, :tenant] In your view you can do: @audits.each do |audit| audit.auditor.category audit.tenant.category audit.user.category Notice that the above example basically will find out the category for each listing based on the foreign_key ids assigned through the associations. I only show this to you because it allows you to use multiple foreign_keys within one table and can help you rethink the design of your tables/models depending on the use. I hope this gives you a little bit of an idea.. -- Posted via http://www.ruby-forum.com/.
Thanks a lot. I''ll play with it. :) Pepe On Jul 12, 11:14 am, "Älphä Blüë" <rails-mailing-l...@andreas-s.net> wrote:> > Users: > > id > > category # can be either ''ADMIN'', ''AUDITOR'' or ''TENANT'' > > > Audits: > > id > > auditor_id > > tenant_id > > Looking at this original Table format you have two foreign_keys in > auditor and tenant. > > auditor_id matches user.id > tenant_id matches user.id > admin_id matches user.id > > > class User < ActiveRecord::Base > > has_many :audits > > end > > You can design your User table and use self-referential associations > within a given model. > > class User < ActiveRecord::Base > has_many :audits > has_many :auditors, :through => :audits > has_many :tenants, :through => :audits > end > > > class Audit < ActiveRecord::Base > > belongs_to :user # !!! This does not work even using :class_name, > > etc. !!! > > end > > class Audit < ActiveRecord::Base > belongs_to :user > belongs_to :auditor, :class_name => "User" > belongs_to :tenant, :class_name => "User" > end > > Using this format you can define any method in your Audit model with > something similar to: > > :joins => [:user, :auditor, :tenant] > > .. which will join those self-referential tables together > > And then in your controller if you use: > > @audits = Audit.all :include => [:user, :auditor, :tenant] > > In your view you can do: > > @audits.each do |audit| > audit.auditor.category > audit.tenant.category > audit.user.category > > Notice that the above example basically will find out the category for > each listing based on the foreign_key ids assigned through the > associations. > > I only show this to you because it allows you to use multiple > foreign_keys within one table and can help you rethink the design of > your tables/models depending on the use. > > I hope this gives you a little bit of an idea.. > > -- > Posted viahttp://www.ruby-forum.com/.
On Jul 12, 3:43 pm, pepe <P...-1PhG29ZdMB/g+20BJ0uB2w@public.gmane.org> wrote:> When a user is retrieved for access and functionality validations, I > re-retrieve the user through the specific user class as in: > user = User.find(...) > # The user is found and passes validations. > # Now I re-retrieve it through the specific user class based in the > category value. > user = user.category.capitalize.constantize.find(user.id) if user > > From that moment on I have the user retrieved through its correct type > and my associations work wonderfully.If you use single table inheritance then User.find(...) would automatically return an instance of Tenant, Auditor or Admin as appropriate and you wouldn''t need to refetch it. Fred> > Is there a better way of making this work? > > Thanks. > > Pepe
Have you tried, class Audit < ActiveRecord::Base belongs_to :auditor, :class_name => ''User'', :foreign_key => ''auditor_id'' belongs_to :tenant, :class_name => ''User'', :foreign_key => ''tenant_id'' end class User < ActiveRecord::Base has_many :auditors has_many :tenants end On Jul 12, 9:43 pm, pepe <P...-1PhG29ZdMB/g+20BJ0uB2w@public.gmane.org> wrote:> Hi all, > > I found what I think is a slick solution to a problem of mine but I''d > like to know if there is a better way to accomplish what I did. > > I have 2 tables: > > Users: > id > category # can be either ''ADMIN'', ''AUDITOR'' or ''TENANT'' > > Audits: > id > auditor_id > tenant_id > > I need the audits to belong to both an AUDITOR and a TENANT user: > > class User < ActiveRecord::Base > has_many :audits > end > > class Audit < ActiveRecord::Base > belongs_to :user # !!! This does not work even using :class_name, > etc. !!! > end > > After tinkering for a while with it I couldn''t find an easy way of > making it work. Then I had an idea that has worked and I think is > pretty slick. I created 3 models: > > class Admin < User > # Admin specific functionality here > end > > class Auditor < User > has_many :audits > # Auditor specific functionality here > end > > class Tenant < User > has_many :audits > # Tenant specific functionality here > end > > When a user is retrieved for access and functionality validations, I > re-retrieve the user through the specific user class as in: > user = User.find(...) > # The user is found and passes validations. > # Now I re-retrieve it through the specific user class based in the > category value. > user = user.category.capitalize.constantize.find(user.id) if user > > From that moment on I have the user retrieved through its correct type > and my associations work wonderfully. > > Is there a better way of making this work? > > Thanks. > > Pepe
You can try something on these lines too class User < ActiveRecord::Base has_many :audits_as_auditor, :class_name => ''Audit'', :foreign_key => ''auditor_id'' has_many :audits_as_tenant, :class_name => ''Audit'', :foreign_key => ''tenant_id'' end Pradeep On Jul 13, 11:07 am, Jitu <Ashrafuzzaman...-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> Have you tried, > > class Audit < ActiveRecord::Base > belongs_to :auditor, :class_name => ''User'', :foreign_key => > ''auditor_id'' > belongs_to :tenant, :class_name => ''User'', :foreign_key => > ''tenant_id'' > end > > class User < ActiveRecord::Base > has_many :auditors > has_many :tenants > end > > On Jul 12, 9:43 pm, pepe <P...-1PhG29ZdMB/g+20BJ0uB2w@public.gmane.org> wrote: > > > Hi all, > > > I found what I think is a slick solution to a problem of mine but I''d > > like to know if there is a better way to accomplish what I did. > > > I have 2 tables: > > > Users: > > id > > category # can be either ''ADMIN'', ''AUDITOR'' or ''TENANT'' > > > Audits: > > id > > auditor_id > > tenant_id > > > I need the audits to belong to both an AUDITOR and a TENANT user: > > > class User < ActiveRecord::Base > > has_many :audits > > end > > > class Audit < ActiveRecord::Base > > belongs_to :user # !!! This does not work even using :class_name, > > etc. !!! > > end > > > After tinkering for a while with it I couldn''t find an easy way of > > making it work. Then I had an idea that has worked and I think is > > pretty slick. I created 3 models: > > > class Admin < User > > # Admin specific functionality here > > end > > > class Auditor < User > > has_many :audits > > # Auditor specific functionality here > > end > > > class Tenant < User > > has_many :audits > > # Tenant specific functionality here > > end > > > When a user is retrieved for access and functionality validations, I > > re-retrieve the user through the specific user class as in: > > user = User.find(...) > > # The user is found and passes validations. > > # Now I re-retrieve it through the specific user class based in the > > category value. > > user = user.category.capitalize.constantize.find(user.id) if user > > > From that moment on I have the user retrieved through its correct type > > and my associations work wonderfully. > > > Is there a better way of making this work? > > > Thanks. > > > Pepe
Yes - check out the Rails docs for discussion of Single-Table Inheritance. The major change will be that your ''category'' column will be renamed to ''type'', and you''ll be able to skip the second find step. --Matt Jones On Jul 12, 10:43 am, pepe <P...-1PhG29ZdMB/g+20BJ0uB2w@public.gmane.org> wrote:> Hi all, > > I found what I think is a slick solution to a problem of mine but I''d > like to know if there is a better way to accomplish what I did. > > I have 2 tables: > > Users: > id > category # can be either ''ADMIN'', ''AUDITOR'' or ''TENANT'' > > Audits: > id > auditor_id > tenant_id > > I need the audits to belong to both an AUDITOR and a TENANT user: > > class User < ActiveRecord::Base > has_many :audits > end > > class Audit < ActiveRecord::Base > belongs_to :user # !!! This does not work even using :class_name, > etc. !!! > end > > After tinkering for a while with it I couldn''t find an easy way of > making it work. Then I had an idea that has worked and I think is > pretty slick. I created 3 models: > > class Admin < User > # Admin specific functionality here > end > > class Auditor < User > has_many :audits > # Auditor specific functionality here > end > > class Tenant < User > has_many :audits > # Tenant specific functionality here > end > > When a user is retrieved for access and functionality validations, I > re-retrieve the user through the specific user class as in: > user = User.find(...) > # The user is found and passes validations. > # Now I re-retrieve it through the specific user class based in the > category value. > user = user.category.capitalize.constantize.find(user.id) if user > > From that moment on I have the user retrieved through its correct type > and my associations work wonderfully. > > Is there a better way of making this work? > > Thanks. > > Pepe
Ashrafuz Zaman wrote:> Have you tried, > > class Audit < ActiveRecord::Base > belongs_to :auditor, :class_name => ''User'', :foreign_key => > ''auditor_id'' > belongs_to :tenant, :class_name => ''User'', :foreign_key => > ''tenant_id'' > end > > class User < ActiveRecord::Base > has_many :auditors > has_many :tenants > endYou don''t have to specify the foreign_key in your example above. Rails will automatically assign a foreign key for the two models specified using model_id and since they are auditor that means auditor_id and tenant that means tenant_id. You only have to specify a foreign key of you are using something different. As an example, if you were going to custom_id in auditor and special_id in tenant you would have to specify the naming of the foreign keys. In your example: belongs_to :auditor, :class_name => ''User'' belongs_to :tenant, :class_name => ''User'' .. is enough. -- Posted via http://www.ruby-forum.com/.
Hi Fred, Just [re]discovered about STI right after my answer to the first reply. I made the necessary changes to the users table, tried it and it worked quite fine when I fixed the kinks trying to access the value with user.type. Once I figured that one out everything worked as expected. Thanks. Pepe On Jul 12, 4:35 pm, Frederick Cheung <frederick.che...-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> On Jul 12, 3:43 pm, pepe <P...-1PhG29ZdMB/g+20BJ0uB2w@public.gmane.org> wrote: > > > When a user is retrieved for access and functionality validations, I > > re-retrieve the user through the specific user class as in: > > user = User.find(...) > > # The user is found and passes validations. > > # Now I re-retrieve it through the specific user class based in the > > category value. > > user = user.category.capitalize.constantize.find(user.id) if user > > > From that moment on I have the user retrieved through its correct type > > and my associations work wonderfully. > > If you use single table inheritance then User.find(...) would > automatically return an instance of Tenant, Auditor or Admin as > appropriate and you wouldn''t need to refetch it. > > Fred > > > > > Is there a better way of making this work? > > > Thanks. > > > Pepe
Thanks to all for your replies. All very good. As I mentioned in my reply to Fred, I ended up using STI for this. Without knowing it is pretty much what I was doing manually. The built in functionality allows me to not have to do the final ''find'' by hand and retrieves the record through the correct model by itself, hence having the correct functionality. I almost went back to the original idea and column name in table ''users'' (category) after I started to run into problems trying to update the value of column "type" the ''regular'' way: my_user.type = ''whatever''. That gave me some trouble until I finally remembered about: my_user[:type] = ''whatever'' and cruised through. I still have a part of the code that is not as dry as I think it should. When I am updating a user and the type changes I am running the following code in the controller: # This work for all the attributes but ''type'', which does not get updated. @user = User.update(params[:id], params[:user]) # That is why I ended up having to run the following right after the line above. if @user.errors.empty? @user[:type] = params[:user][:type] @user.save end I chose to go this way route because I was it was shorter and easier than anything else I could think of. If there is a better way of doing this I''d appreciate a comment on it. Thanks to all. Pepe On Jul 12, 10:43 am, pepe <P...-1PhG29ZdMB/g+20BJ0uB2w@public.gmane.org> wrote:> Hi all, > > I found what I think is a slick solution to a problem of mine but I''d > like to know if there is a better way to accomplish what I did. > > I have 2 tables: > > Users: > id > category # can be either ''ADMIN'', ''AUDITOR'' or ''TENANT'' > > Audits: > id > auditor_id > tenant_id > > I need the audits to belong to both an AUDITOR and a TENANT user: > > class User < ActiveRecord::Base > has_many :audits > end > > class Audit < ActiveRecord::Base > belongs_to :user # !!! This does not work even using :class_name, > etc. !!! > end > > After tinkering for a while with it I couldn''t find an easy way of > making it work. Then I had an idea that has worked and I think is > pretty slick. I created 3 models: > > class Admin < User > # Admin specific functionality here > end > > class Auditor < User > has_many :audits > # Auditor specific functionality here > end > > class Tenant < User > has_many :audits > # Tenant specific functionality here > end > > When a user is retrieved for access and functionality validations, I > re-retrieve the user through the specific user class as in: > user = User.find(...) > # The user is found and passes validations. > # Now I re-retrieve it through the specific user class based in the > category value. > user = user.category.capitalize.constantize.find(user.id) if user > > From that moment on I have the user retrieved through its correct type > and my associations work wonderfully. > > Is there a better way of making this work? > > Thanks. > > Pepe