Django Query: get frequency of related objects by name - django

I have a list of rating objects, where every rating is associated with a studyspace
I'm trying to write a Query to return a breakdown of the number of votes for each given StudySpace
e.g.
Maths Room: 10
Physics Room: 2
Art Room: 9
I've tried and come up with the following:
studyspace_rating_breakdown = ratings.values('studyspace').annotate(num_votes=Count('studyspace')).order_by('-num_votes')[:3]
However this returns the id of the studyspace.
[{'studyspace': 8, 'num_votes': 421}, {'studyspace': 7, 'num_votes': 91}, {'studyspace': 2, 'num_votes': 2}]
Is there a way I can modify the query to return the studyspace name field instead of the ID?
I don't want to have to write a templatetag to look up the studyspace name from the ID within the template
The models themselves:
class StudySpace(models.Model):
department = models.ForeignKey(Department, on_delete=models.CASCADE)
space_name = models.CharField(max_length=200, unique=True)
...
class Rating(models.Model):
studyspace = models.ForeignKey(StudySpace, on_delete=models.CASCADE)
student = models.ForeignKey(Student, on_delete=models.CASCADE)
rating = models.IntegerField(default=3)
...

Just try to put up the answer in comment section, you could easily do:
ratings.values('studyspace', 'studyspace__space_name')
to show the related fields. For more info, check django query api for values.

Related

Django - Sum related fields that belongs to the same FK, for every object

So I'm trying to get the sum of every score in all games related to the according player and how many game he has played
class Player(models.Model):
name = models.CharField()
class Game(models.Model):
points = models.IntegerField()
user = models.ForeignKey(User, on_delete=models.CASCADE)
My desired output is:
{
"name": "asdf",
"total_points": "9999"
"games_played": "12"
}
I thought I could do it getting a value list of my players then looking the sum of all games and count games played for every value on the list, but I don't know how to do it.
I know how to do it for an specific Player, but I want to get the entire list. I see this as a loop where every Player counts by its own.
Thanks
Your models.py seems like incomplete, So I'm assuming a there is a FK relationship between Player and Game as below
class Player(models.Model):
name = models.CharField(max_length=200)
class Game(models.Model):
points = models.IntegerField()
user = models.ForeignKey(Player, on_delete=models.CASCADE)
You can fetch all summary of your DB by using a Group By query as below,
from django.db.models import Count, Sum, F
Game.objects.annotate(name=F('user__name')).values('name').annotate(games_played=Count('id'), total_points=Sum('points'))
Sample output will be like this,
<QuerySet [{'name': 'name_1', 'games_played': 2, 'total_points': 6}, {'name': 'name_2', 'games_played': 1, 'total_points': 2}]>
QuerySet is a iterable similar to list. You can iterate over it if you want. Hence each of the item from the QuerySet be like this,
{'name': 'name_1', 'games_played': 2, 'total_points': 6}
Assuming that Player has a User stored as .user, you could do something like:
from django.db.models import Sum
games = Game.objects.filter(user=player.user)
output = {
'name': player.name,
'total_points': games.aggregate(Sum('points')),
'total_games': games.count()
}

How to calculate and store scores in django betting game?

I'm working on my first django project which is a sport betting game.
My models are:
class Game(models.Model):
home_team = models.CharField(max_length=200)
away_team = models.CharField(max_length=200)
home_goals = models.IntegerField(default=None)
away_goals = models.IntegerField(default=None)
class Bet(models.Model):
gameid = models.ForeignKey(Game, on_delete=models.CASCADE)
userid = models.ForeignKey(User, on_delete=models.CASCADE)
home_goals = models.IntegerField()
away_goals = models.IntegerField()
score = models.IntegerField(default=None, null=True)
logic for calculating scores is:
WHEN polls_bet.home_goals = polls_game.home_goals AND polls_bet.away_goals = polls_game.away_goals THEN 2
WHEN polls_game.home_goals > polls_game.away_goals AND polls_game.home_goals > polls_game.away_goals THEN 1
WHEN polls_bet.home_goals < polls_bet.away_goals AND polls_game.home_goals < polls_game.away_goals THEN 1
ELSE 0
I was able to solve it easily using database view that combines all data in one table but it seems that it does not play well with django migrations..
So I was thinking about sql trigger after update of game goals, but then I don't know how to pass conditions like user's id.
2nd idea is to create additional 'scores' table with:
gameid,
userid,
betid,
score
But then again I don't know how to calculate scores.
Please advice how this should be done properly, without using sql view. I appreciate all answers!
You can define a 'score' property on the Bet model to solve this easily. Please refer to the documentation here.
Your property implementation will be something like:
#property
def score(self):
if (self.home_goals == self.game__home_goals and
self.away_goals == self.game__away_goals):
return 2
if (self.game__home_goals > self.game__away_goals):
return 1
if (self.home_goals < self.away_goals and
self.game__home_goals < self.home_goals):
return 1
return 0
On a side note, the normal naming convension for a foreignkey relation is the model name in lowercase. So it becomes 'game' and 'user' instead of 'gameid' and 'userid'. Also I believe you have some typos on the second condition.

Django advanced join / query, how to filter foreign keys?

I have this two models.
class City(models.Model):
city = models.CharField(max_length=200)
country = models.CharField(max_length=200)
class CityTranslation(models.Model):
city = models.ForeignKey(City)
name = models.CharField(max_length=200)
lang = models.CharField(max_length=2)
prio = models.IntegerField()
Every city can have multiple translated names within one language.
So I want to get all City's with country="Poland". If a corresponding City has one or more CityTranslations with lang=name. I want to get only the first ordered by prio.
I am doing something like that now.
City.objects.filter(country="Poland", citytranslation__land="pl").annotate(transname=F("alt_names__name"))
But this is not working, because:
If there is a City without a CityTranslation it want be listed
If there are multiple CityTranslation's they all will be shown. But I just want the first. (... .ordered_by('prio').first())
Any idea?
EDIT:
Solved it by using a #property field, which is ordering my CityTranslation by prio and picks the first one:
#propert
def transcity(self):
return self.citytranslation.filter(lang="pl").order_by('-prio').first()
def magic(passed_country="Poland", passed_lang="pl")
# I want to get all City's with country="Poland".
cities = City.objects.filter(country=passed_country)
# If a corresponding City has one or more CityTranslations with lang=name. I want to get only the first ordered by prio.
suitable_cities = cities.filter(citytranslation__lang=passed_lang)
if suitable_cities.exists()
first_matching_city = suitable_cities.orderby('prio').first()
else:
first_matching_city = cities.orderby('prio').first()
return first_matching_city
May need to set up a relatedname on citytranslation.
May not need orderby if you plan on ordering by ID anyways.

How to get number of items grouped by a property of an intermediate model

I would like to have something like this
Adventure (4) | Sci-fi (12)
which are the books, in a bookshop, linked by a local price.
Say, Hobbit is $5 at Amazon and $6 at Barnes. So if I was listing the books in Amazon I will have Adventure (1) as the count of the books with a specified price in amazon.
If I do like this I get the correct Genres:
for u in Bookshop.objects.get(pk=1).BookBookshopLink_set.all():
print u.book.genre
which would print, e.g.:
Sci-fi
Sci-fi
Adventure
Here are the models:
from parler.models import TranslatableModel, TranslatedFields
from parler.managers import TranslationManager
class Genre(TranslatableModel):
translations = TranslatedFields(
name=models.CharField(max_length=200),
slug=models.SlugField(),
description=models.TextField(blank=True),
meta={'unique_together': [('language_code', 'slug')]},
)
published = models.BooleanField(default=False)
class Book(TranslatableModel):
translations = TranslatedFields(
name=models.CharField(max_length=200),
slug=models.SlugField(),
description=models.TextField(blank=True),
meta={'unique_together': [('language_code', 'slug')]},
)
genre = models.ForeignKey(Genre, blank=True, null=True)
published = models.BooleanField(default=False)
class Bookshop(TranslatableModel):
translations = TranslatedFields(
name=models.CharField(max_length=200),
description=models.TextField(_('Description'), default='', blank=True),
slug=models.SlugField(),
meta={'unique_together': [('slug', 'language_code')]},
)
booklist = models.ManyToManyField(Book, blank=True, through='BookBookshopLink')
class BookBookshopLink(TranslatableModel):
bookshop = models.ForeignKey(Bookshop)
book = models.ForeignKey(Book)
price = models.IntegerField(blank=True, null=True)
To do what you're trying to achieve in one query, you need to use Count, annotate and values_list
I'll show you a code example and then I'll try to explain it:
from django.db.models import Count
from your_project.models import *
Genre.objects.all().values_list('name').annotate(num_books=Count('book'))
.values_list('name'): This return a list of all genres by name
.annotate(num_books=Count('book')): This count books for each Genre
I have a similar models structure in my projects and when I execute that code, I get this as answer:
[(u'GENRE_NAME', 13), (u'GENRE_NAME', 14), (u'GENRE_NAME', 0),...]
You can parse the output of this query to fit your expectations
I also recomend you to check oficial documentation Django Agreggation
This loops over all your genres and prints out how many of them there are.
Even if there are 0 in a genre.
for a_genre in Gendre.objects.all():
print Book.objects.filter(genre=a_genre).count()
and if you want it printed with the genre aswel
for a_genre in Gendre.objects.all():
print "%s (%d)" % (a_genre, Book.objects.filter(genre=a_genre).count())
Documentation for filters in django : https://docs.djangoproject.com/en/1.7/topics/db/queries/#retrieving-specific-objects-with-filters

Access foreign key objects in Django

I have two models:
class Item(models.Model):
name = models.CharField()
class Param(models.Model):
product = models.ForeignKey(Item, related_name="param")
height = models.IntegerField()
price = models.IntegerField()
I want is to annotate item with average price, but only keep params with height > 60.
items = Item.objects.filter(param__height__gt=60).annotate(price=Avg('param__price'))
That's where I have problems. I want to fetch filtered Param objects from above query.
Is it possible to do?
I know that there is workaround:
for item in items:
item.params = item.param.filter(height__gt=60)
But there are a lot of additional queries.
So, my question whether can I access filtered param objects from items?
items = Item.objects.select_related('param').filter(param__height__gt=60).annotate(price=Avg('param__price')).all()
class Artist():
blah = models.TextField()
class Album()
blah = models.ForeignKey(blah)
the key is to be shaed in the class like this^
Also for example the key can be x,y points, time, user, or whatever you want!
https://github.com/Ry10p/django-Plugis/blob/master/courses/models.py
example line 52
-Cheers