3

I'm creating a simple binary rating system for my website, it is a study materials website with summaries that can be rated +1 or -1.

I've defined to ManyToMany fields between my Summary model and User model like so:

users_rated_positive = models.ManyToManyField(
    User, blank=True, related_name='summaries_rated_positive')
users_rated_negative = models.ManyToManyField(
    User, blank=True, related_name='summaries_rated_negative')

However it is obvious that a given user can not rate a given summary more than once.

I tried setting unique=True but Django doesn't seem to let me do that.

Is there are more optimal way to store the ratings that aligns better with my intentions?

davegri
  • 2,206
  • 2
  • 26
  • 45
  • 1
    Did you check that you can effectively add duplicate relations ? This answer seems to imply that duplicate relations are not permitted: http://stackoverflow.com/questions/4870935/django-enforcing-manytomanyfield-unique-items – Dric512 Aug 25 '15 at 09:10
  • so I don't need to actually check this in my view? – davegri Aug 25 '15 at 09:11
  • I never tried it myself so cannot be sure, but this is what is described in the answer I linked. You may try to reproduce the test case that is in the answer to check. – Dric512 Aug 25 '15 at 09:17

1 Answers1

4

Consider using a ManyToMany through table.

class Summary(models.Model):
    user_ratings = models.ManyToManyField(
        'User', blank=True, through='UserRating',
    )


class UserRating(models.Model):
    user = models.ForeignKey('User')
    summary = models.ForeignKey('Summary')
    is_positive = models.BooleanField()

    class Meta:
        unique_together = ('user', 'summary')

Then you can query users who reviewed the summary positively with something like

summary.user_ratings.filter(is_positive=True)
summary.user_ratings.filter(is_positive=False)

The unique_together property ensures that a user can't review the same summary twice.

You could even retain similar syntax using a custom queryset. Here's a guess at what that might look like:

class SummaryQuerySet(models.QuerySet):

    def users_rated_negative(self):
        return self.user_ratings.filter(is_positive=False)

    def users_rated_positive(self):
        return self.user_ratings.filter(is_positive=True)


class Summary(models.Model):
    user_ratings = models.ManyToManyField(
        'User', blank=True, through='UserRating',
    )

    objects = SummaryQuerySet.as_manager()

Now the in-use syntax is summary.users_rated_positive

A similar custom queryset for User would give you the summaries_rated_positive property, if you needed that too.

Community
  • 1
  • 1
nimasmi
  • 3,978
  • 1
  • 25
  • 41