I have the following models:-
class Group(models.Model):
name = models.CharField(max_length=200)
class Game(models.Model):
group = models.ForeignKey(Group, related_name='games')
date = models.DateField()
players = models.ManyToManyField(
Player, through='GameMembership', related_name='games'
)
class GameMembership(models.Model):
game = models.ForeignKey(Game, related_name='memberships')
player = models.ForeignKey(Player, related_name='memberships')
selected = models.BooleanField(default=False)
injured = models.BooleanField(default=False)
class Player(models.Model):
group = models.ForeignKey('groups.Group', related_name='players')
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='players')
I want to annotate all of the players in the group with a score which is calculated as follows:-
For the last 10 games for which a player wasn't injured, score 5 if they were selected.
I can do this using Sum/Case/When if I ignore the "wasn't injured" clause, by using a manager method on Player which looks something like this:-
def with_availability_scores(self, group):
for_games = group.games.reverse()[:10]
return self.annotate(
availability_score=Sum(Case(
When(
memberships__selected=True, memberships__game__in=for_games,
then=5)
default=0, output_field=IntegerField()))
)
But the addition of the "injured" clause means that I can't use that for_games variable like that to begin with.
I suspect it can be done using Subquery and OuterRef but I can't quite figure out the exact syntax I need.
Any ideas?
Related
I have a models.py file that more or less looks like this:
class Match(models.Model):
match_id = models.BigIntegerField()
class Team(models.Model):
team_id = models.CharField(max_length=4)
match = models.ForeignKey(
Match,
related_name='teams',
on_delete=models.CASCADE,
)
win = models.BooleanField()
class Player(models.Model):
player_id = models.IntegerField()
team = models.ForeignKey(
Team,
related_name='players'
on_delete=models.CASCADE,
)
For each match, I want to find the two teams that played, and for each team, I want to find the players that were on the team. This is what I tried so far:
match_id = 123456789
match_info = Match_model.objects.get(match_id=match_id)
red_info = Team_model.objects.get(match=match_info, team_id='Red')
blue_info = Team_model.objects.get(match=match_info, team_id='Blue')
red_players = Player_model.objects.filter(team=red_info)
blue_players = Player_model.objects.filter(team=blue_info)
but Django is giving me the error:
Team matching query does not exist.
How would I go about fixing this error? Any pointers on how to correct my queries would be greatly appreciated!
try:
red_info = Team_model.objects.get(match=match_info, team_id='Red')
blue_info = Team_model.objects.get(match=match_info, team_id='Blue')
except Team_model.DoesNotExist:
pass
else:
red_players = Player_model.objects.filter(team=red_info)
blue_players = Player_model.objects.filter(team=blue_info)
I have two models: Players and Tournaments.
Players have also a type of game they play (dota, lol, magic, etc). They can participate in many tournaments at the same time (only once per Tournament). To manage the inscriptions, I use another model called TournamentMatch, that creates a new object for every inscription with the ID of the tournament and the player.
class Player(models.Model):
name = models.CharField(_('name'), max_length=150, null=True, blank=True)
email = models.EmailField(_('email address'), unique=True)
is_dota_player = models.BooleanField(default=False)
is_lol_player = models.BooleanField(default=False)
is_magic_player = models.BooleanField(default=False)
class Tournament(models.Model):
name = models.CharField(max_length=200)
date_start = models.DateField()
date_end = models.DateField()
class TournamentMatch(models.Model):
tournament = models.ForeignKey(Tournament)
player = models.ForeignKey(Player)
date_added = models.DateTimeField(auto_now_add=True)
date_modified = models.DateTimeField(auto_now=True)
I want to count the number of Players that have more than two inscription to tournaments and are, for example, dota players.
I can easily achieve this with a for loop for every player, but for perfomance reasons, I want to achieve this using a Django query.
For example, if I want to count the dota players that have one or more inscription, I will do:
TournamentMatch.objects.filter(
player__is_dota_player=True
).distinct(
'player'
).count()
I'm sure is possible but I don't know how to count every ocurrence of distinct players on tournaments and only filter those that have more than one (and not only one).
I will appreciate any help or pointers!
# subquery for counting match per player (note OuterRef which will be linked to the outer query row)
player_match_count_subquery = TournamentMatch.objects.filter(player_id=OuterRef('pk'), ).\
annotate(match_count=Count('pk'), ).values('match_count', )[:1]
# the main (outer) query which uses subquery output for filtering
Players.objects.filter(is_dota_player=True, ).\
annotate(match_count=Subquery(player_match_count_subquery), ).\
filter(match_count__gt=2, )
https://docs.djangoproject.com/en/2.2/ref/models/expressions/#subquery-expressions
this code will produce logically this SQL query:
SELECT p.*, tm.match_count
FROM players p
CROSS APPLY (
SELECT COUNT(id) match_count
FROM tournamentmatch tm
WHERE tm.player_id = p.id
) tm
WHERE p.is_dota_player = $true
AND tm.match_count > 2
I have a model which looks like this
player_info, game, score, creation_date
I want to fetch the records with the highest score of each player for a particular game.
Any help is appreciated.
EDIT:
class Game(models.Model):
name = models.CharField(max_length=50)
description = models.TextField(max_length=1000)
logo = models.URLField()
resource_info = models.URLField()
cost = models.DecimalField(default=0.0, decimal_places=2, max_digits=10)
modified_date = models.DateTimeField(auto_now_add=True)
developer_info = models.ForeignKey(User, related_name='uploaded_games', on_delete=models.CASCADE)
class Score(models.Model):
game_info = models.ForeignKey(Game, related_name='game_info',on_delete=models.CASCADE)
player_info = models.ForeignKey(User, related_name='player_info', on_delete=models.CASCADE)
last_played = models.DateTimeField(auto_now=True)
score = models.BigIntegerField(default=0)
If I understand correctly:
You want to filter your Scores by a Game.
And then you want to GROUP BY User and get the MAX score
The GROUP BY part is a bit tricky, but you can achieve it using values + annotate.
Try something like this:
from django.db.models import Max
Score.objects.filter(game_info=...)
.values('player_info').annotate(max=Max('score'))
More info an examples: https://docs.djangoproject.com/en/1.10/topics/db/aggregation/
I have 3 model classes:
class Team(models.Model):
name = models.CharField(max_length=100, default="", blank=True, null=True)
number = models.IntegerField()
class Position(models.Model):
match = models.ForeignKey('Match')
color = models.CharField(max_length=5)
number = models.IntegerField()
team = models.ForeignKey('Team')
class Match(models.Model):
type = models.CharField(max_length=3)
number = models.IntegerField()
red_score = models.IntegerField(default=0)
blue_score = models.IntegerField(default=0)
match_events = models.TextField(max_length=1000)
Using the models as they are right now, how would I able to get a list of Matches a Team has won (i.e. if the Team belongs to a red Position, add it to the list if its Match has a red_score > blue_score)
Sorry if that's confusing. I can try to clarify if you have any specific questions.
Thanks!
Simplest way to do this:
Match.objects.filter(position__color='red', red_score__gt=F('blue_score'))
You may want to move Position model to the bottom, to remove apostrophes from foreign key related models names.
This is as well very good example to use ManyToMany relation:
class Team(models.Model):
name = models.CharField(max_length=100, default="", blank=True, null=True)
number = models.IntegerField()
class Match(models.Model):
type = models.CharField(max_length=3)
number = models.IntegerField()
red_score = models.IntegerField(default=0)
blue_score = models.IntegerField(default=0)
match_events = models.TextField(max_length=1000)
teams = models.ManyToManyField(Team, through='Position',
related_name='matches')
class Position(models.Model):
match = models.ForeignKey(Match)
color = models.CharField(max_length=5)
number = models.IntegerField()
team = models.ForeignKey(Team)
In this case, you will get few more options to simplify data access, e.g.:
if team is your actual team and match single match selected somewhere earlier in the code, this is valid:
team.matches.filter(red_score__gt=F('blue_score')) # all won matches of this team
match.teams.all() # teams involved in this match
Guys,
Is there an easy way to return different fields names from different models by chaining joins?
My model:
Class Item(models.Model):
item_code = models.CharField(max_length=10)
name = models.CharField(max_length=255)
...
Class Stock(models.Model):
item_code = models.ForeignKey( Item )
userid = models.ForeignKey( User )
qty = models.IntegerField()
...
I want to select " Item.Item_code, Item.name, Stock.qty where Stock.userid=2 and Item.item_code = Stock.Item_Code"
How do i do this in Django?
Gath
I want to select " Item.Item_code, Item.name, Stock.qty where Stock.userid=2 and Item.item_code = Stock.Item_Code"
You can pick these specific fields only using one SQL, provided you start from the Stock model. For instance
q = Stock.objects.select_related('userid', 'item_code').filter(
userid__id = 2).values('item_code__item_code', 'item_code__name', 'qty')
This will help if you want to limit the data and then number of queries. If you are not concerned with this then do:
q = Stock.objects.filter(userid__id = 2)
for stock in q:
print stock.item_code.item_code
print stock.item_code.name
print stock.qty
This will return a queryset with only those fields you have chose using values. You can then iterate through it.
PS: Re: your models.
Class Stock(models.Model):
item_code = models.ForeignKey( Item )
userid = models.ForeignKey( User )
qty = models.IntegerField()
...
It is a good idea to use the model name in lower case for FK relationships. For e.g. you ought to write:
Class Stock(models.Model):
item = models.ForeignKey( Item ) # Changed
user = models.ForeignKey( User ) # Changed
qty = models.IntegerField()
...
You can also use this:
Stock.objects.filter(user=2).values('item__item_code', 'item__name')
First of all change fileds names
Read this very carefuly http://docs.djangoproject.com/en/dev/ref/models/querysets/
Class Item(models.Model):
item_code = models.CharField(max_length=10)
name = models.CharField(max_length=255)
...
Class Stock(models.Model):
item = models.ForeignKey( Item )
user = models.ForeignKey( User )
qty = models.IntegerField()
...
#view
stocks = Stock.objects.filter(user=2)
for stock in stocks:
print stock.item.item_code, stock.item.name
#one query version