Anthony
Anthony

Reputation: 979

Get average of user input ratings for different model instances (Django)

Ok, so here is the deal. I'm working on this web-app that lets you create users, beers, and reviews for beers.

models.py:

from django.db import models
from django.conf import settings

class BeerModel(models.Model):

    user = models.ForeignKey(User, default=1)
    name = models.CharField(max_length=254, default="")
    style = models.CharField(max_length=254, default="")
    ibu = models.IntegerField(default="")
    calories = models.IntegerField(default="")
    abv = models.IntegerField(default="")
    location = models.CharField(max_length=254, default="")

    class Meta:
        verbose_name_plural = 'Beers'

    def __str__(self):
        return self.name

    def avg(self):
        return


class RateModel(models.Model):

    FIVE_REVIEWS = (
        ('5', '5'),
        ('4', '4'),
        ('3', '3'),
        ('2', '2'),
        ('1', '1'),
    )

    TEN_REVIEWS= (
        ('10', '10'),
        ('9', '9'),
        ('8', '8'),
        ('7', '7'),
        ('6', '6'),
        ('5', '5'),
        ('4', '4'),
        ('3', '3'),
        ('2', '2'),
        ('1', '1'),
    )

    user = models.ForeignKey(User, default=1)
    beer = models.ForeignKey(BeerModel)
    aroma = models.PositiveIntegerField(max_length=2, choices=FIVE_REVIEWS, default="5")
    appearance = models.PositiveIntegerField(max_length=2, choices=FIVE_REVIEWS, default="5")
    taste = models.PositiveIntegerField(max_length=2, choices= TEN_REVIEWS, default= "10")

    class Meta:
        verbose_name_plural = 'Ratings'

    def __str__(self):
        return str(self.beer)

As you can tell, you can create a review for different beers using different users.

My goal is to have django output on the HTML, an "Average" value for "Aroma, appearance, and taste" for each beer.

This is what I have in my views.py:

@login_required
def home(request):
    beers = BeerModel.objects.order_by('name')
    rates = RateModel.objects.order_by('user')
    avg = RateModel.objects.aggregate(aroma = Avg('aroma'), appearance = Avg('appearance'), taste = Avg('taste'))
    dict = {'records': beers, 'rates': rates, 'avg': avg}

    return render(request, 'beer_tracker/home.html', context=dict)

This is what I have on my home.html:

<h2>Overall Rating</h2>
    {% for beers in records %}
        <div class="table">
            <h4><b>Beer's Overall Ratings: </b>{{ beers.name}}</h4>
            <ul>
                <li><b>Aroma: </b>{{ avg.aroma }}</li>
                <li><b>Appearance: </b>{{ avg.appearance }}</li>
                <li><b>Taste: </b>{{ avg.taste }}</li>
            </ul>
        </div>
     {% endfor %}

Unfortunately, as you can tell, I get the average for all beers. I need the average for each specific beer.

So if I have a beer, let's say Blue moon, and I have 2 users give 2 reviews:

User1 Aroma: 3, Appearance: 3, Taste: 8,

User2 Aroma: 5, Appearance: 5, Taste: 10,

I would like to get for Blue Moon:

Aroma: 4, Appearance: 4, Taste: 9,

But still keeping the reviews values for other beers independently.

Thank you for your time.

Anthony

Upvotes: 2

Views: 2335

Answers (1)

Alasdair
Alasdair

Reputation: 308909

Instead of using aggregate, you should annotate your beers queryset with the averages.

from django.db.models import Avg

beers = BeerModel.objects.order_by('name').annotate(
    avg_aroma=Avg('ratemodel__aroma'),
    avg_appearance=Avg('ratemodel__appearance'),
    avg_taste=Avg('ratemodel__taste'), 
)

Then, when you loop through the beers queryset you can access the annotated fields.

{% for beer in beers %}
{{ beer.avg_aroma }}
{{ beer.avg_appearance} } 
{{ beer.avg_taste }}
{% endfor %} 

See the docs for joins and aggregates for more examples.

Upvotes: 3

Related Questions