Search code examples
pythondjangodjango-formsrating-system

Django - Rating System View & Template


I have a content that I'd like to rate on multiple criteria.
Imagine this kind of model:

class Content(models.Model):
  name = models.CharField(max_length=50)

class Criterion(models.Model):
  name = models.CharField(max_length=50)
  content = models.ForeignKey(Content)

class ContRate(models.Model):
  user = models.ForeignKey(User, help_text="Who rated ?")
  crit = models.ForeignKey(Criterion)
  rate = models.DecimalField()

The user has a page displaying the content.
From this page, he can also rate the content on the criteria set

Rating Criteria

The rating will be done with Ajax.
Now I'm trying to implement the view & the template

view.py

#...
def viewcont(request, content_id):
  """The user can view a content & rate it"""
  content = get_object_or_404(Content, pk=content_id)
  RateFormSet = modelformset_factory(ContRate)
  formset = RateFormSet(queryset=ContRate.objects.filter(content=content, user=request.user))
  objs = {
    'content': content,
    'forms': formset,
  }
  return render_to_response('content/content_detail.html', objs
  , context_instance=RequestContext(request)
  )
#...

content_detail.html

<!-- ... -->
<div id="rating">
  <ul>
{% for crit in content.crit_set.all %}
    <li>
      {{ crit }}
      <div class="rateit"
        data-rateit-value="the_actual_rating_if_already_there"
        data-rateit-ispreset="true"
        crit-id="{{ crit.id }}"></div>
    </li>
{% endfor %}
  </ul>
</div>
<!-- ... -->

Now how can I use the forms formset to display the actual rates ?
And how can I draw an empty form to be posted by Ajax from any clicked star ?
(I know the javascript/jQuery part)


Solution

  • Not sure what the point of the formset is here. The rates are all available via the criteria object, using the reverse foreign key to ContRate in exactly the same way as you've done from Criteria to Content.

    To make this as efficient as possible, you probably want to get the relevant ratings in the view and bring them together into a single datastructure:

    content = get_object_or_404(Content, pk=content_id)
    criteria = content.criteria_set.all()
    user_ratings = ContRate.objects.filter(content=content, user=request.user)
    ratings_dict = dict((c.crit_id, c.rate) for c in user_ratings)
    for crit in criteria:
        crit.user_rating = ratings_dict.get(crit.id)
    

    Now you can pass criteria directly to your template, and there you can iterate through it to show the user_rating for each one.

    (Final point: "criteria" is plural, the singular is "criterion". :-)