Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
371 views
in Technique[技术] by (71.8m points)

ruby on rails - I need help properly forming this ActiveRecord association

Basically, a User can participate in one or more events as either (or potentially both) a vendor and as a member of the event's faculty.

A user is, by default, categorized as neither a vendor nor a faculty member but rather attains either (or both) status(es) within the context of an event (e.g., the user has been admitted to an event as a member of its faculty).

It seems like what I want to say is that the user has many events through either (or both) the vendors or the faculty linking tables, but I'm not sure I'd go about representing this in my Rails model. Here's what I've tried so far:

class User < ActiveRecord::Base
  has_many :events through => vendors
  has_many :events through => faculty
end

Here's a sample of a query that I think I'd need to make:

Select * from vendors where user_id = 1;
Select * from faculty where user_id = 1;

Could someone provide some direction as to how to properly form this ActiveRecord association?

Update:

So, I've tried using single-table inheritance to solve the issue, and I've ended up with a user table which records containing only a single user type. While still using single-table inheritance, how do I get my users to have multiple types? (I know this is essentially a many-to-many relationship; I'm just not sure of how to accomplish this using STI.)

id | first_name | last_name | birth_date | city | zip_code | email |  type   |         created_at         |         updated_at
----+------------+-----------+------------+------+----------+-------+---------+----------------------------+----------------------------
  1 | Akira      | Yamaoka   |            |      |          |       | Vendor  | 2014-08-30 14:58:26.917333 | 2014-08-30 14:58:26.917333
  2 | Pyramid    | Head      |            |      |          |       | Faculty | 2014-08-30 15:02:04.70209  | 2014-08-30 15:02:04.70209
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Single-table inheritance might be what you need. In a nutshell: it allows several classes with the same kind of data to be put into one table. The only requirement is that that table has a type column, a string.

Basically, it's about common sense. Let's say, a user can have passes to an event: a vendor's pass and a faculty member's pass. He might have both. Let's create a Pass model, bearing in mind that we'll need different kinds of it. But we'll use it later. For now let's just stick to has_many through:

rails g model Pass type:string user:references event:references

Migrate this and we won't need to modify our database anymore. We'll only modify Ruby. We should have got a class Pass, we'll need to mark its role in association:

class Pass < ActiveRecord::Base
  belongs_to :user
  belongs_to :event
end

All right. Then we'll have this sort of User and Event:

class Event < ActiveRecord::Base
  has_many :passes
  has_many :users, through: :passes
end

class User < ActiveRecord::Base
  has_many :passes
  has_many :events, through: :passes
end

Here's where the STI magic comes. Let's create two more classes.

rails g model VendorPass --no-migration --parent=Pass
rails g model FacultyPass --no-migration --parent=Pass

We've generated some classes without database tables (we don't need them). They are empty and we won't change it: they inherit a Pass and that's all we need. But we'll need to create some extra associations between our User, Event and the new passes. In the end, I've found this working:

class Event < ActiveRecord::Base
  # We already had this
  has_many :passes
  has_many :users, through: :passes

  # New stuff!
  has_many :vendor_passes
  has_many :vendors, through: :vendor_passes, source: :user

  has_many :faculty_passes
  has_many :faculty_members, through: :faculty_passes, source: :user
end

class User < ActiveRecord::Base
  # We already had this
  has_many :passes
  has_many :events, through: :passes

  # New stuff!
  has_many :vendor_passes
  has_many :vendor_events, through: :vendor_passes, source: :event

  has_many :faculty_passes
  has_many :faculty_events, through: :faculty_passes, source: :event
end

Rails maintains its own understanding of VendorPass which is "it's a Pass, whose type is VendorPass", same with FacultyPass.

The good parts:

  • Easy to imagine: data structure seems sane and logical
  • We're free to add more types of Passes without changing the database

The bad parts:

  • No way to add extra fields to only specific subclasses of Pass: they're all in the same table
  • Associations look a bit repetitive and cumbersome
  • Rails only allows type to be a string, not the fastest type to compare

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...