0

I'm working on a django based restaurant review site. When user sends reviews they fill in a form which consists of two choice fields. Currently I can display all reviews for a specific restaurant in a Detailview page. I would like to take the information from the choice fields from the comment form to display this data for the user in both the detail view (1 specific restaurant) as well as the list view (all restaurant listed):

Question:

  1. I would like to get the total number of "votes" for each choice and display it with for example Chart Js in the detail view. What is the prefered way or doing so in django? I have tried to do this filtering in javascript, however I know that this could be done more efficiently directly in the model or in the view.

Code
Models.py

class Restaurant(models.Model): 
      name        = models.CharField(max_length = 100)

class Comment(models.Model):
      STATUS_1 = (
               ('one', 'one'),
               ('two', 'two'),
               ('three', 'three'),
       ) 
       STATUS_2 = (
               ('cheap', 'cheap'),
               ('normal', 'normal'),
               ('expensive', 'expensive'),
       ) 
       restaurant = models.ForeignKey(Restaurant, on_delete = models.CASCADE, related_name='comments')
       comment    = models.TextField(max_length = 500)
       status_1   = models.CharField(max_length = 100, choices=STATUS_1)
       status_2   = models.CharField(max_length = 100, choices=STATUS_2)

views.py

class RestaurantDetailView(DetailView):
    model = Restaurant
    form_class = CommentForm
    template_name = 'core/restaurant_detail.html'

def get_context_data(self, **kwargs):
    context = super(RestaurantDetailView, self).get_context_data(**kwargs)
    comment_form = CommentForm
    context['comment_form'] = comment_form
    return context

def point_dataset(self, request):
    points = serialize('geojson', Restaurant.objects.all())
    return HttpResponse(points, content_type='json')


def get_queryset(self):
    return Restaurant.objects.annotate(avg_rating=Avg('comments__rate'))

For example:
If a specific restaurant has 3 reviews with results: STATUS_1 = 'one', 'one', 'two' and STATUS_2 = 'normal', 'cheap', 'normal', how do I get the following data passed to chart js:
STATUS_1: ['one': '2', 'two': '1', 'three': '0']
STATUS_2: ['cheap': '1', 'normal': '2', 'expensive': '0']

  • https://stackoverflow.com/questions/629551/how-to-query-as-group-by-in-django Does this answer your question? – Alvi15 Nov 02 '20 at 06:37

1 Answers1

0

The easiest solution is to use collection.Counter. For example, in your model add a property method:

from collections import Counter

class Restaurant(models.Model):
    ...
    @property
    def status_1(self):
      return Counter(list(self.comments.values_list('status_1',flat=True)))

    @property
    def status_2(self):
      return Counter(list(self.comments.values_list('status_2',flat=True)))

To reduce DB hits, you can use prefetch_related to preload comments queryset:

def get_queryset(self):
    return Restaurant.objects.annotate(avg_rating=Avg('comments__rate')).prefetch_related('comments')

Usage in template should be like this:

{{ restaurant.status_1 }}
{{ restaurant.status_2 }}

To get keys and values, you can try like this:

{{ restaurant.status_1.keys }}
{{ restaurant.status_1.values }}

Basically it is like a dictionary.

ruddra
  • 50,746
  • 7
  • 78
  • 101
  • Thank you Ruddra! In the javascript; var reviews = {{ restaurant.status_1|safe}} presents the information on the form Counter({ 'one': 2, 'two': 1, 'three': 0 }). Is there a quick way by using Counter that allows me to split this results into one list of labels and another list for values? This would be useful as I would like to display this data in a pie chart. I could do this by javascript alone but I'm fairly sure Counter could make my life easier. Thanks in advance for you assistans. – Robin Lundström Nov 03 '20 at 06:07
  • You can use `{{ restaurant.status_1.keys }} {{ restaurant.status_1.values }}` to get labels and values. I have updated the answer – ruddra Nov 03 '20 at 07:52
  • Hmm... It doesn’t work as expected. In the JavaScript {{ restaurant.status_1 }} give me all information of the dictionary, however {{ restaurant.status_1.keys }} and {{ restaurant.status_1.values }} both equals to zero. Do I need to change anything in my views.py file or is it something else? – Robin Lundström Nov 05 '20 at 06:54