Solved

model

Posted on 2011-02-25
12
438 Views
Last Modified: 2012-05-11
Hi Experts, I am doing a new model to my project, however I am not so confident this is going to work.
I am trying to use just one enrollment table for user and guest, because this way I can have relations with only one table and the model will be smaller. Further I will have a payment relation with just enrollment.
Would you take a look and give me your thoughts about how would you do it and why.

  Thanks a lot.

  This is what I am thinking about. I have not tested this yet ...

event
    has_many :enrollments
    has_many :membersenrolled, :through =>:enrollments, :source=>:member
    has_many :guestsenrolled, :through =>:enrollments, :source=>:guest

enrollment
    belongs_to :event
    belongs_to :member, :condition => "type=1"
    belongs_to :guest, :condition => "type=2"

 member
    has_many :enrollments
    has_many :memberenrollment, :through =>:enrollments, :source=>:event

guest
    has_many :enrollments
    has_many :guestenrollment, :through =>:enrollments, :source=>:event

0
Comment
Question by:lusfernandos
  • 5
  • 5
  • 2
12 Comments
 
LVL 12

Expert Comment

by:JESii
ID: 34983709
Looks pretty interesting; nice thinking. I think I might make one change in your member and guest definitions and use the plural for has_many :memberenrollments and has_many :guestenrollments, but that's just a nit.

Nice use of a 'fixed' condition; I'm not sure that the exact syntax will work there to you might wind up having to do something like:
    :conditions => {'type = ?', 1}
and there could also be an issue with 'type' being used (I've seen is MS SQL, for example, that this is a reserved word so you can't use it as a bare column name).

Let us know how it goes and what you run into... and I'm sure there will be other opinions here as well.
0
 
LVL 4

Expert Comment

by:kristinalim
ID: 34992132
Up to Ruby 1.8.7, all objects have the deprecated method "type" which returns the object's class. This would be overridden by the accessor ActiveRecord would set up for the table's "type" column if present. I'm not a fan of the overlap, so I avoided the column name.

Within Rails, the 'type" column is also by default used to store the class name for Single-Table Inheritance. Another reason not to use this for other purposes.

I'm not quite clear on what Member and Guest are for. Are they members vs. guests of the website or members vs. guests of the organizing group for an event? Something like this might also work depending on the structure of the rest of your database:

Event
  has_many :enrollments
  has_many :attendees, :through => :enrollments
  has_many :members, :through => :enrollments, :source => :attendee,
    :conditions => {'attendees.type' => 'Member'}
  has_many :guests, :through => :enrollments, :source => :attendee,
    :conditions => {'attendees.type' => 'Guest'}
Enrollment
  belongs_to :event
  belongs_to :attendee
Attendee
  has_many :enrollments
  has_many :events, :through => :enrollments
Member < Attendee
Guest < Attendee

Open in new window


If you use single-table inheritance like, though, you would have to be careful with working with already cached associations:

event = Event.create             #=> #<Event id: 1>
event.attendees                  # => []
event.guests << Guest.create     #=> [#<Guest id: 1, type: "Guest">]
event.attendees                  #=> []
event.attendees.reload           #=> [#<Guest id: 1, type: "Guest">]
event = Event.create             #=> #<Event id: 2>
event.attendees                  #=> []
event.attendees << Guest.create  #=> [#<Guest id: 2, type: "Guest">]
event.attendees << Member.create #=> [#<Guest id: 2, type: "Guest">, #<Member id: 3, type: "Member">]
event.attendees                  #=> [#<Guest id: 2, type: "Guest">, #<Member id: 3, type: "Member">]

Open in new window

0
 
LVL 2

Author Comment

by:lusfernandos
ID: 34997513
Hi Kristinalim,

  All right I will not use type kkkkk.
  Your solution was what I had in mind, but I didn't know how to do it (Inheritance). I am worried about one thing. My current model already have a Member and a Guest. BTW This project is for a church and in a church we have members and people who once in a while visit the church (guest). Normally the Church does a lot of events and each event can have church members and visitors(people from other churchs, who will be in the database as guest as well).
  Creating this inheritance for Attendee (Member and Guest), is this a real table in my database or it will be just a facilitator? I am sorry if this is a basic question, but I am still a not very experienced in rails. Would this (Attendee) have any impact in my current modeling (The project is already running but with Members and Guests).
0
 
LVL 4

Expert Comment

by:kristinalim
ID: 34998141
From the section on Single-Table Inheritance:


Active Record allows inheritance by storing the name of the class in a column that by default is named “type” (can be changed by overwriting Base.inheritance_column). This means that an inheritance looking like this:

class Company < ActiveRecord::Base; end
class Firm < Company; end
class Client < Company; end
class PriorityClient < Client; end

When you do Firm.create(:name => "37signals"), this record will be saved in the companies table with type = “Firm”. You can then fetch this row again using Company.where(:name => '37signals').first and it will return a Firm object.

In your case Attendee, Member, and Guest would all use the single and same table "attendees". Whether an Attendee record is a Member or a Guest would be determined with the class name in the "type" column. Attendee would cover both Member and Guest. Member and Guest would scope the attendees table accordingly.

All of these will be done automatically if you define Member and Guest as subclasses of Attendee and add a string column "type" in the "attendees" table:


# Migration
create_table :attendees do |t|
  t.string :type
end

# Models
class Attendee < ActiveRecord::Base ; end
class Member < Attendee ; end
class Guest < Attendee ; end

Open in new window


Member.create  # ...
Guest.create   # ...
Attendee.count #=> 2
Member.count   #=> 1
Guest.count    #=> 1

Open in new window


Your current setup probably uses two tables for members and guests. If you want to use single-table inheritance, you will have to migrate the data to the parent table (eg. attendees), set the column "type" to the original class name (eg. "Member", "Guest"), set these classes to inherit from Attendee, and generalize.
0
 
LVL 2

Author Comment

by:lusfernandos
ID: 34998314
Okay I see it clearer now, Thanks for your explanation.

Can I do this inheritance using 3 tables (maybe)? Using just one table it would have a lot of unused information for guest.
0
 
LVL 4

Expert Comment

by:kristinalim
ID: 34999126
You can create MemberProfile with the behaviour and attributes only for Member and/or GuestProfile with the behaviour and attributes only for Guest, and associate with Attendee/Guest/Member with has_one.

class MemberProfile < ActiveRecord::Base
  belongs_to :attendee, :foreign_key => :member_id
end
class Attendee < ActiveRecord::Base
  has_one :member_profile, :foreign_key => :member_id
end
class Member < Attendee ; end
class Guest < Attendee ; end

Open in new window


If you want to keep the members and guests tables entirely separate without single-table inheritance, you can put common behaviour in a module and include from the classes:

module AttendeeBehaviourHere ; end
class Member < ActiveRecord::Base
  include AttendeeBehaviourHere
end
class Guest < ActiveRecord::Base
  include AttendeeBehaviourHere
end

Open in new window


Many ways to approach this, really depends on your preferences and the specs.
0
Top 6 Sources for Identifying Threat Actor TTPs

Understanding your enemy is essential. These six sources will help you identify the most popular threat actor tactics, techniques, and procedures (TTPs).

 
LVL 2

Author Comment

by:lusfernandos
ID: 35000176
Hey JES and kristinalim,

  I am giving a try in my own suggestion, after all I thought it was worth a try.
  While considering Kristinalim suggestion I saw I could use the polymorphic association to my model, so I tried to do that, however I am getting problem, perhaps you have some ideia about what is going on.

  I am able to add a user and a member to an event. However when trying to figure what members or guest are in the event I get the following error:

class Event < ActiveRecord::Base
  has_many :enrollments, :dependent=>:destroy 
  has_many :membersenrolled, :through =>:enrollments, :source=>:member
  has_many :guestsenrolled, :through =>:enrollments, :source=>:guest
end

class Enrollment < ActiveRecord::Base
  belongs_to :event
  belongs_to :user, :polymorphic => true
  #belongs_to :member, :conditions => "type='Member'", :polimorphic => true
  #belongs_to :guest, :conditions => "type='Guest'", :polimorphic => true
end

class Member
    has_many :enrollments, :as=>:user
    has_many :memberenrollments, :through =>:enrollments, :source=>:event
end

class Guest
  has_many :enrollments, :as=>:user
  has_many :guestenrollments, :through =>:enrollments, :source=>:event
end


# Here is the Error


e = Event.first
=> #<Event id: 1, title: "Primeiro Evento", description: nil, created_at: "2011-02-28 18:08:13", updated_at: "2011-02-28 18:08:13">
e.enrollments
=> [#<Enrollment id: 1, event_id: 1, user_type: "Member", user_id: 1, created_at: "2011-02-28 18:08:56", updated_at: "2011-02-28 18:08:56">, #<Enrollment id: 2, event_id: 1, user_type: "Guest", user_id: 1, created_at: "2011-02-28 18:47:04", updated_at: "2011-02-28 18:47:04">]
e.membersenrolled
ActiveRecord::HasManyThroughSourceAssociationNotFoundError: Could not find the source association(s) :member in model Enrollment.  Try 'has_many :membersenrolled, :through => :enrollments, :source => <name>'.  Is it one of :user or :event?
	from C:/Ruby/gems/gems/activerecord-2.3.5/lib/active_record/reflection.rb:289:in `check_validity!'
	from C:/Ruby/gems/gems/activerecord-2.3.5/lib/active_record/associations/has_many_through_association.rb:5:in `initialize'
	from C:/Ruby/gems/gems/activerecord-2.3.5/lib/active_record/associations.rb:1300:in `new'
	from C:/Ruby/gems/gems/activerecord-2.3.5/lib/active_record/associations.rb:1300:in `membersenrolled'

Open in new window

0
 
LVL 4

Accepted Solution

by:
kristinalim earned 500 total points
ID: 35000442
A polymorphic association definitely works too.

class Event < ActiveRecord::Base
  has_many :enrollments, :dependent => :destroy

  # This may return a collection of users and members.
  has_many :users, :through => :enrollments, :source => :user
end
class Enrollment < ActiveRecord::Base
  belongs_to :event
  belongs_to :user, :polymorphic => true
end
class Member < ActiveRecord::Base
  has_many :enrollments, :as => :user
  has_many :events, :through => :enrollments, :source => :event
end
class Guest < ActiveRecord::Base
  has_many :enrollments, :as => :user
  has_many :events, :through => :enrollments, :source => :event
end

Open in new window

0
 
LVL 2

Author Comment

by:lusfernandos
ID: 35000524
I tried that:
 
class Event < ActiveRecord::Base
  has_many :enrollments, :dependent=>:destroy 
  has_many :membersenrolled, :through =>:enrollments, :source=>:user
  #has_many :guestsenrolled, :through =>:enrollments, :source=>:guest
end

Open in new window


But it gives the error:
 
e = Event.first
=> #<Event id: 1, title: "Primeiro Evento", description: nil, created_at: "2011-02-28 18:08:13", updated_at: "2011-02-28 18:08:13">
e.membersenrolled
ActiveRecord::HasManyThroughAssociationPolymorphicError: Cannot have a has_many :through association 'Event#membersenrolled' on the polymorphic object 'User#user'.
	from C:/Ruby/gems/gems/activerecord-2.3.5/lib/active_record/reflection.rb:297:in `check_validity!'
	from C:/Ruby/gems/gems/activerecord-2.3.5/lib/active_record/associations/has_many_through_association.rb:5:in `initialize'
	from C:/Ruby/gems/gems/activerecord-2.3.5/lib/active_record/associations.rb:1300:in `new'
	from C:/Ruby/gems/gems/activerecord-2.3.5/lib/active_record/associations.rb:1300:in `membersenrolled'
	from (irb):2

Open in new window

0
 
LVL 4

Expert Comment

by:kristinalim
ID: 35000783
Oops. Trouble with has_many association :through a :polymorphic association.

class Event < ActiveRecord::Base
  has_many :enrollments
  has_many :members, :through => :enrollments, :source => :user, :source_type => 'Member'
  has_many :guests, :through => :enrollments, :source => :user, :source_type => 'Guest'

  # This may return a collection of users and members.
  # AFAIK not yet implemented in ActiveRecord.
  def users
    self.members + self.guests
  end
end

Open in new window


See ActiveRecord::Base has_many.
0
 
LVL 2

Author Closing Comment

by:lusfernandos
ID: 35000904
Some other comments helped solving this problem.
0
 
LVL 12

Expert Comment

by:JESii
ID: 35007095
Nicely done...
0

Featured Post

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

I recently rediscovered rails when I needed a holiday project and decided to build a management dashboard for the company where I work.  With it being a project done in my free time, I could focus my time on learning the basics rather than trying to…
In Ruby, Call or invoke a API DLL library is easily via Win32API class, win32-api gem or other gems. For general DLL API call, there are quite a few references, some good tips list below: http://www.rubytips.org/2008/05/13/accessing-windows-api-fro…
Get a first impression of how PRTG looks and learn how it works.   This video is a short introduction to PRTG, as an initial overview or as a quick start for new PRTG users.
This video shows how to remove a single email address from the Outlook 2010 Auto Suggestion memory. NOTE: For Outlook 2016 and 2013 perform the exact same steps. Open a new email: Click the New email button in Outlook. Start typing the address: …

746 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

13 Experts available now in Live!

Get 1:1 Help Now