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 Pejic 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,

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?

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.

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.

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.