Rating Model and DB Relationships

I am still learning Rails and the relationships are still a little
confusion. So I apologize in advance for the repetition of this
question.

I am building a user rating system for words and quotes. If I wanted a
user to be able to rate a word or quote only once, how can I set up
the ratings table so it will have the appropriate relationships. Since
the rating model is a bit polymorphic (rate words or quotes), I am not
sure if I need a “bridge table”. I want to be able to do things like

word.rating.value
quote.rating.value
user.rating.value

Any direction is greatly appreciated, and if you can throw in some
comments behind the code, even better. Also, would you suggest putting
the only one rating per user constraint in the db or as a
validate_unique…

Thanks,
Jason

To quickly answer your second question, you need both. The reason for
that is that validates_uniqueness_of does not guarantee fully unique
column values, since it’s at the application level. The unique
constraint in the db schema can be viewed as a backup.

I’ll answer your main question shortly.

Thanks for the initial response. I look forward to the main answer.

Srdjan P. wrote:
[…]

The unique
constraint in the db schema can be viewed as a backup.

I’ll answer your main question shortly.

A philosophical quibble: in my opinion, it is the unique constraint in
the DB that should be considered primary. The validation in the
application layer is only there to prevent the DB from throwing
duplicate key errors. Application-level validation cannot replace a
properly constructed database schema.

I’ll get off my soapbox now…I just want to make it clear that the
database layer is the only place where it is possible to have airtight
integrity checking.

Best,

Marnen Laibow-Koser
http://www.marnen.org
[email protected]

I appreciate the debate, and I agree the constraint should be the DB
as a primary.

Back to my main question though - how should the relationships be set
up for a rating system?

Hmm, let’s see…

Jason wrote:
[…]

I am building a user rating system for words and quotes. If I wanted a
user to be able to rate a word or quote only once, how can I set up
the ratings table so it will have the appropriate relationships. Since
the rating model is a bit polymorphic (rate words or quotes), I am not
sure if I need a “bridge table”.

A bridge table has to do with many-to-many relationships, not
polymorphic relationships, unless I’m misunderstanding.

I want to be able to do things like

word.rating.value
quote.rating.value
user.rating.value

How about using STI?

class Word < Rateable
has_many :ratings
end

class Quote < Rateable
and
class User < Rateable
likewise

class Rating < ActiveRecord::Base
belongs_to :rateable, :polymorphic => true
end

(Syntax may be slightly off…working from memory.)

There might be a plugin to make this easier.

Best,

Marnen Laibow-Koser
http://www.marnen.org
[email protected]

I don’t know if STI is appropriate here.

You should probably have a model called Rating which is the many side
of one-to-many with the User model. The Rating model should be
polymorphic so that you can use it to rate both Word and Quote
objects.

So, it would most likely look like this:

class User
:has_many => :ratings
end

class Rating
:belongs_to => :user
:belongs_to => :rated, :polymorphic => true
end

class Word
:has_one :rating, :as => :rated
end

class Quote
:has_one :rating, :as => :rated
end

The ratings table needs to have rated_id (integer) and rated_owner
(string) as columns in order to support the polymorphic behaviour.
What this will give you is a collection of ratings for a user (the
logic of adding this is something you need to figure out), and each
Rating instance will have a property called rated. That will reach
in and grab either the Quote or the Word being rated. The score, or
however you’re rating them, needs to be in the Rating model.

Let me know if you need more clarification.

On Apr 16, 1:08 am, Marnen Laibow-Koser <rails-mailing-l…@andreas-

Srdjan P. wrote:

I don’t know if STI is appropriate here.

On second thought, you’re absolutely right. I don’t know what the heck
I was thinking. :slight_smile:

You should probably have a model called Rating which is the many side
of one-to-many with the User model. The Rating model should be
polymorphic so that you can use it to rate both Word and Quote
objects.
[…]

Yeah, this is what I was trying for. I just introduced STI in an
inappropriate place.

Note, though, that not using STI makes foreign-key validation somewhat
harder.

So, it would most likely look like this:

class User
:has_many => :ratings
end

class Rating
:belongs_to => :user
:belongs_to => :rated, :polymorphic => true
end

class Word
:has_one :rating, :as => :rated
end

class Quote
:has_one :rating, :as => :rated
end

Those should be has_many…

Best,
Marnen

I don’t know if STI is appropriate here.

You should probably have a model called Rating which is the many side
of one-to-many with the User model. The Rating model should be
polymorphic so that you can use it to rate both Word and Quote
objects.

So, it would most likely look like this:

class User
:has_many => :ratings
end

class Rating
:belongs_to => :user
:belongs_to => :rated, :polymorphic => true
end

class Word
:has_one :rating, :as => :rated
end

class Quote
:has_one :rating, :as => :rated
end

The ratings table needs to have rated_id (integer) and rated_owner
(string) as columns in order to support the polymorphic behaviour.
What this will give you is a collection of ratings for a user (the
logic of adding this is something you need to figure out), and each
Rating instance will have a property called rated. That will reach
in and grab either the Quote or the Word being rated. The score, or
however you’re rating them, needs to be in the Rating model.

Let me know if you need more clarification.

On Apr 16, 1:08 am, Marnen Laibow-Koser <rails-mailing-l…@andreas-

Those should be has_many…

I’m not sure if I understand you here. You mean that Word and Quote
should have more than one rating per instance? That’s why I put them
as has_one.

On Apr 16, 10:53 am, Marnen Laibow-Koser <rails-mailing-l…@andreas-