开发者

Rails: Multiple "types" of one model through related models?

I have a User model in my app, which I would like to store basic user information, such as email address, first and last name, phone number, etc.

I also have many different types of users in my system, including sales agents, clients, guests, etc.

I would like to be able to use the same User model as a base for all the others, so that I don't have to include all the fields for all the related roles in one model, and can delegate as necessary (cutting down on duplicate database fields as well as providing easy mobility from changing one user of one type to another).

So, what I'd like is this:

User
-- first name
-- last name
-- email
--> is a "client", so
---- client field 1
---- client field 2
---- client field 3

User
-- first name
-- last name
-- email
--> is a "sales agent", so
---- sales agent field 1
---- sales agent field 2
---- sales agent field 3

and so on...

In addition, when a new user signs up, I want that new user to automatically be assigned the role of "client" (I'm talking about database fields here, not authorization, though I hope to eventually include this logic in my user authorization as well). I have a multi-step signup wizard I'm trying to build with wizardly. The first step is easy, since I'm simply calling the fields included in the base User model (such as first_name and email), but the second step is trickier since it should be calling in fields from the associated model (like--per my example above--the model client with f开发者_C百科ields client_field_1 or client_field_2, as if those fields were part of User).

Does that make sense? Let me know if that wasn't clear at all, and I'll try to explain it in a different way.

Can anyone help me with this? How would I do this?


STI is probably a good fit for your requirements, as suggested by tadman, if you are using ActiveRecord (from Rails 3, it is easy to change ORM). The basic information is available on the AR documentation page, but here is some extra information w.r.t. your target:

  • Define one model per file. Otherwise there are some initialization troubles. Assuming Client inherits from User all in a single file, Client objects cannot be created as long as a User constructor has not been called once. One file per model circumvents the problem.
  • All attributes through the hierarchy are defined one-shot in the top class. This is for performance issues, but it seems disturbing many people in blog posts. In short, the Ruby code is object-oriented and encapsulates properly the attributes. The DB contains everything in a single table, with the extra "type" column to distinguish where they belong in the hierarchy. This is only a "trick" to represent inheritance trees in relational databases. We must be aware that the ORM mapping is not straightforward. The image on this site from Martin Fowler may help understand the situation.

In addition, you would like any new user to be a client, where Client inherits from User. To do so, you may simply instantiate any new user as a client. In your controller for creating users:

user = Client.new
# Do something to user
user.save
#=> <Client id: 1, name: "Michael Bolton", email: "mike@bolton.net", created_at: "2010-05-30 03:27:39", updated_at: "2010-05-30 03:27:39">

All the above is still valid with Rails 3 when using ActiveRecords.


It looks like you have two reasonable approaches here, but it will depend on the nuances of your requirements.

You can use Single Table Inheritance (STI) to do what you want, where User is only the base class for others named SalesAgent or Client and so forth. Each of these sub-classes may define their own validations. All you need for this to work is a string column called "type" and ActiveRecord will do the rest:

class User < ActiveRecord::Base
end

class Agent < User
end

The alternative is to have a number of free-form fields where you store various bits of related data and simply interpret them differently at run-time. You may structure it like this:

class User < ActiveRecord::Base
  has_one :agent_role,
    :dependent => :destroy
end

class AgentRole < ActiveRecord::Base
  belongs_to :user

   # Represents the agent-specific role fields
end

That would have the advantage of allowing for multiple roles, and if you use has_many, then multiple roles of the same type.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜