Reputation: 470
Please reference these example models:
class Player(models.Model):
team = models.ForeignKey(Team, on_delete=models.CASCADE)
class Team(models.Model):
captain = models.ForeignKey(Manager, on_delete=models.CASCADE)
country = models.CharField()
class Manager(models.Model):
networth = models.FloatField()
I am trying to figure out if the following is faster than the alternative (ie. accesses the database less):
team = Team.objects.get()
Player.objects.filter(team_id=team.id)
Alternative:
team = Team.objects.get()
Player.objects.filter(team=team)
Upvotes: 1
Views: 1178
Reputation:
So to illustrate my point in the comments better - what I see often in the wild is url structures like this:
/teams/<int:pk>/players/
Accompanied by view code like this:
def players_view(request, pk):
team = Team.objects.get(pk=pk)
context = {
"players": Player.objects.filter(team=team)
}
...
while you can just do:
def players_view(request, pk):
context = {
"players": Player.objects.filter(team__pk=pk)
}
...
And extra points for:
def players_view(request, pk):
context = {
"players": Player.objects.filter(team__pk=pk).select_related("team")
}
...
And the template trick:
{% for player in players %}
<!-- team name only once -->
{% if forloop.first %}
<h1>Players of {{player.team.name}}</h1>
{% endif %}
<!-- data of player here -->
{% endfor %}
Upvotes: 1
Reputation: 477641
I am trying to figure out if the following is faster than the alternative
No. It will result in the same query. Except for a few cycles that Django spends on inspecting what you passed and accessing the primary key, it does not make any difference.
You can inspect this in The Django shell with:
>>> print(Player.objects.filter(team_id=team.id).query)
SELECT `app_name_player`.`id`, `app_name_player`.`team_id` FROM `app_name_player` WHERE `app_name_player`.`team_id` = 1
>>> print(Player.objects.filter(team=team).query)
SELECT `app_name_player`.`id`, `app_name_player`.`team_id` FROM `app_name_player` WHERE `app_name_player`.`team_id` = 1
So the two queries are identical. Probably it is more idiomatic to fetch these with:
team.player_set.all()
If you need to access the Player
s of a set of Team
s, you can make use of .prefetch_related(…)
[Django-doc] to fetch all the Player
s for a set of teams in one query, avoiding the N+1 problem:
teams = Team.objects.prefetch_related('player_set')
Here if you then iterate over the teams
, and fetch the player_set
of each Team
object, it will not make extra queries, since it has already fetched all the related Player
s in one extra query, and did the "joining" at the Python/Django layer.
Upvotes: 2