views:

46

answers:

4

In writing an application for my school's yearbook committee, I've hit a bit of a dead end with modeling a specific relation. Currently I have a photo class

class Photo(models.Model):
 photo = models.ImageField(upload_to="user_photos/")
 name = models.CharField(blank=True, max_length=50)

 rating = models.IntegerField(default=1000)

 wins = models.IntegerField(default=0)
 matches = models.IntegerField(default=0)

and a user class

class UserProfile(models.Model):
 user = models.ForeignKey(User, unique=True)
 group = models.CharField(max_length=50)

both of which are working swimmingly. What I'd like to do is break it up so that a Photo will have global rating derived from votes of the entire userbase as well as a rating based only on the users votes on that photo. Unfortunately, I'm at a loss on how to structure this. My first thought was a ManyToMany field, but I was also thinking that something like breaking rating into its own model like this:

class Rating(models.Model)
     photo = models.ManyToOne(Photo)
     rating = models.IntegerField(default=1500)

could work.

Could a Django (or really, anyone who's slightly competent, because I know I'm not) guru point me in the proper direction on approaching this simple conundrum?

+2  A: 

You want a through table.

Ignacio Vazquez-Abrams
This is exactly what I was looking for, can't believe I missed it in the documentation. Thank you!
Nick Barnwell
A: 

I do not exactly understand your question, but for represeting a many-to-one-relation in django you use a ForeignKey-field! But as Ignacio already pointed out a many-to-many relation with an intermediary model might also be useful for you!

lazerscience
+1  A: 

you want to have a many-to-many field, but custom defined.

class Rating(models.Model):
    photo = models.ForeignKey(Photo)
    user = models.ForeignKey(User)
    rating = models.IntegerField(default=1500)

class Photo(models.Model):
    photo = models.ImageField(upload_to="user_photos/")
    name = models.CharField(blank=True, max_length=50)

    rating = models.ManyToManyField(User, through='Rating')

    wins = models.IntegerField(default=0)
    matches = models.IntegerField(default=0)

you can then query it either having a photo object or a user object.

This approach is naive and won't scale well. On a very popular website the Ratings model would be overloaded with requests. What would a more professional website do is denormalise the dependency by introducing a redundant integer field such as rating_total and set up a cron job to update it periodically so that you don't need to construct a query through a many-to-many relationship, but get the result from a field straight away.

guruslan
>This approach is naive and won't scale wellI thought as much, but right now the userbase is fairly small and this is for A/B testing, so it might be premature to worry about that. The way I have my models now borrows the Through table and ManyToMany, but also has a globalRating similar to the field you suggested w/ the cron job. On the site, users vote on which of two photos is better, and their ratings are adjusted using a tweaked Elo formula, but users wanted to see how their ratings compared to the ratings of the userbase as a whole, thus my question
Nick Barnwell
A: 

I don't understand well this sentence : "Photo will have global rating derived from votes of the entire userbase as well as a rating based only on the users votes on that photo."

But if you mean that you want to create a field rating for where you have the sum of all users rate and also saving another one that contain user photo rate ; you should first make a think is it worth it because for calculated fields you have the choice between physically storing (global rate) such values in the database or calculating them as needed. you should make the decision based on how often the computed value is needed and how much information is needed to calculate it.

Note that storing the computed value means computing it each time you do a change in the users rate.

singularity