Reputation: 4267
I am trying to understand the concept of querying multiple level foreign key tables in Django, and I really hope someone can help me with it.
So, I am trying to create a List view in Django which shows players, their scores and some related information like their level of expertise. The database structure is something like below:
Players Table
-------------
id
name
<field 1>
<field 2>
<field N>
Then there is a table which stores the player's scores
Player Scores Table
-------------------
id
player_id (this refers to player.id field from table above)
score
level_id (this refers to a level.id field from table below)
And there is a level table like below:
Level Table
-----------
id
level_text
What I want is to create a list like below:
Player Name Player Score Player Level
----------- ------------- ------------
Blue_Marvel 10000 Maestro
Man_At_Arms 51 Noob
P_K_In_Debt 1230000 God
As you can guess the data for each of these three columns need to come from three different tables.
I am very new to Django and can only perform some basic queries. I know I can do Player.object.all()
and I get all the players (there is a player model defined). Similarly I can do Level.object.all()
and I get all the expertise levels.
However I am not sure how I can proceed to construct a list such as above with player name, score and level text.
Any help is appreciated. Assuming that there is a Level and Score model, is there an easy way for me to create above list? If these models are not available, does Django query have any nifty way to achieve this?
I would really be grateful for any pointers!
Upvotes: 1
Views: 1259
Reputation: 477160
For me, this looks like you want a ListView
that actually maps on the PlayerScore
table: for every row in the table there is one row in the output and vice versa.
The only think that makes this case more special, is that we want to fetch the player name as well. Typically you can do this by using a .annotate(..)
on the values you want to select. So we can here define a ListView
[doc]:
# views.py
from django.db.models import F
class PlayerScoreListView(ListView):
model = PlayerScore
template = '/app/playerscore_list.html'
queryset = PlayerScore.objects.annotate(
player_name=F('player__name'),
level_name=F('level__name')
)
So each time we will also fetch the player__name
column, as well as the level__name. If you defined
playerand
levelas
ForeignKeys to the
Playermodel and
Levelmodel respectively, Django will automatically make the JOINs at database level, and these are stored in the
PlayerScoreinstances as
.player_nameand
.level_name` attribute.
The only thing we still need to do, is define a template. For example:
{% app/templates/app/playerscore_list.html %}
<html>
<body>
<table>
<thead>
<tr>
<th>Player name</th>
<th>Score</th>
<th>Level name</th>
</tr>
</thead>
<tbody>
{% for item in object_list %}
<tr>
<td>{{ item.player_name }}</td>
<td>{{ item.score }}</td>
<td>{{ item.level_name }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</body>
</html>
Of course we also need to link a URL to this view:
# urls.py
from django.urls import path
from app.views import PlayerScoreListView
urlpatterns = [
path('playerscore/', PlayerScoreListView.as_view(), name='playerscorelist'),
]
Of course the above is a "raw" implementation. In order to show all relevant content, and make the page look "pleasant", there still is some work to do.
Upvotes: 3