Forum: Ruby on Rails Rating Model and DB Relationships

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
4070d2480bc3296fb94599d907a81b8c?d=identicon&s=25 Jason (Guest)
on 2009-04-13 20:11
(Received via mailing list)
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
25d33a294d8b03b0eec187cb1dc4d8d7?d=identicon&s=25 Srdjan Pejic (batasrki)
on 2009-04-14 18:22
(Received via mailing list)
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.
4070d2480bc3296fb94599d907a81b8c?d=identicon&s=25 Jason (Guest)
on 2009-04-15 04:37
(Received via mailing list)
Thanks for the initial response. I look forward to the main answer.
Dd2d775dea75b381edb1bbf0600a0907?d=identicon&s=25 Marnen Laibow-Koser (marnen)
on 2009-04-15 05:42
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,
--
Marnen Laibow-Koser
http://www.marnen.org
marnen@marnen.org
4070d2480bc3296fb94599d907a81b8c?d=identicon&s=25 Jason (Guest)
on 2009-04-16 05:02
(Received via mailing list)
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?
Dd2d775dea75b381edb1bbf0600a0907?d=identicon&s=25 Marnen Laibow-Koser (marnen)
on 2009-04-16 07:08
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
marnen@marnen.org
25d33a294d8b03b0eec187cb1dc4d8d7?d=identicon&s=25 Srdjan Pejic (batasrki)
on 2009-04-16 16:39
(Received via mailing list)
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-
25d33a294d8b03b0eec187cb1dc4d8d7?d=identicon&s=25 Srdjan Pejic (batasrki)
on 2009-04-16 16:40
(Received via mailing list)
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-
Dd2d775dea75b381edb1bbf0600a0907?d=identicon&s=25 Marnen Laibow-Koser (marnen)
on 2009-04-16 16:53
Srdjan Pejic 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. :)

>
> 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
25d33a294d8b03b0eec187cb1dc4d8d7?d=identicon&s=25 Srdjan Pejic (batasrki)
on 2009-04-16 18:27
(Received via mailing list)
> 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-
This topic is locked and can not be replied to.