Bonjour. Je m'appelle Ragnar et j'ai 1,5 mois d'expérience à Django (en pratique). Quand j'ai commencé Django, j'ai fait quelques tutoriels, mais quand il s'agissait de le faire, il y avait beaucoup de nouveaux concepts et je ne savais pas quoi faire.
La première chose qui est restée bloquée a été la relation. Avant de traiter avec Django, j'ai écrit SQL et utilisé DB, donc je ne comprends pas bien le concept de Queryset, puis-je obtenir des données avec ça? Comment c'est? C'était dans un état.
Cette fois, c'est un article pour ceux qui "j'ai essayé de toucher Django au tutoriel mais je n'ai pas bien compris".
Cette fois, nous supposerons un site où vous pouvez voir une liste des équipes de football, des joueurs et des informations sur les joueurs. J'ai créé un tableau (modèle) des équipes, des positions et des joueurs.
sample/models.py
from django.db import models
FOOT_CHOICES = (
('right', 'Pied droit'),
('left', 'pied gauche'),
('both', 'Les deux pieds'),
)
class Team(models.Model):
name = models.CharField(max_length=40)
country = models.CharField(max_length=40)
def __str__(self):
return self.name
class Meta:
#Le nom affiché sur le site d'administration de Django
verbose_name = "équipe"
verbose_name_plural = "équipe"
class Position(models.Model):
name = models.CharField(max_length=20)
def __str__(self):
return self.name
class Meta:
verbose_name = "position"
verbose_name_plural = "position"
class SoccerPlayer(models.Model):
name = models.CharField(max_length=40)
foot = models.CharField(max_length=5, choices=FOOT_CHOICES)
#Les athlètes appartiennent à un seul club. Plusieurs joueurs appartiennent à l'équipe. 1 à n structure
#Les joueurs peuvent ne pas être affiliés. sur_delete=models.SET_NULL
team = models.ForeignKey(Team, null=True, on_delete=models.SET_NULL, related_name='player')
#Les athlètes peuvent prendre plusieurs positions. Il y a plusieurs joueurs dans une même position. structure n à n
position = models.ManyToManyField(Position, related_name='player')
def __str__(self):
return self.name
class Meta:
verbose_name = "joueur"
verbose_name_plural = "joueur"
Enregistrez le modèle afin de pouvoir enregistrer les données depuis le site d'administration de Django. (J'ai entré les données correctement.)
sample/admin.py
from django.contrib import admin
from . import models
admin.site.register(models.Position)
admin.site.register(models.SoccerPlayer)
admin.site.register(models.Team)
Nous avons également introduit django-debug-toolbar pour vérifier les requêtes. Il n'est pas exagéré de dire que c'est indispensable pour le développement de Django, donc si vous ne l'avez pas inclus, veuillez le faire maintenant.
config/urls.py
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
urlpatterns = [
path('admin/', admin.site.urls),
path('sample/', include('sample.urls'), name='sample') #Cette application
]
if settings.DEBUG:
import debug_toolbar
urlpatterns = [
path('__debug__/', include(debug_toolbar.urls)),
] + urlpatterns
ListView Django a une application Web appelée Generic View. Recevoir la méthode GET et afficher la page, recevoir la méthode POST, valider le formulaire, mettre à jour l'enregistrement, supprimer, etc. Il existe une classe au comportement très général.
Cette fois, nous résumerons l'affichage de la liste et la gestion des relations à l'aide de ListView, qui est spécialisé pour l'affichage de liste d'enregistrements.
Tout d'abord, retirons les noms de tous les joueurs.
sample/views.py
from django.views.generic import ListView
from .models import SoccerPlayer
class PlayerListView(ListView):
model = SoccerPlayer #Modèle pour afficher une liste
template_name = 'player_list.html' # template
player_list = PlayerListView.as_view()
sample/urls.py
from django.urls import path
from . import views
app_name = 'sample'
urlpatterns = [
path('list/', views.player_list, name='player_list'),
]
templates/player_list.html
<!DOCTYPE html>
<html>
<head>
<title>sample</title>
</head>
<body>
<table>
<tr>
<th>Nom</th>
</tr>
{% for player in soccerplayer_list %}
<tr>
<td>{{ player.name }}</td>
</tr>
{% endfor %}
</table>
</body>
</html>
La première chose à retenir dans ListView est que View passe un objet à Template avec le nom ** [nom du modèle (inférieur)] _list **. (Vous pouvez également le spécifier vous-même.) Vous pouvez le récupérer avec for dans le modèle et le nom avec ** [variables récupérées avec for] .name **. ** name ** est le champ du modèle défini par model. Je pense que je l'ai probablement fait dans des tutoriels.
Si vous accédez à http: // localhost: 8000 / sample / list /
, vous pouvez voir que la liste peut être affichée.
Ensuite, sortons l'équipe associée au joueur. Même si vous dites la destination de la relation, dans le cas des relations OnetoOneField (1 à 1) et Foreignkey (1 à n), c'est la même chose que l'écriture normale. Spécifiez simplement le nom du champ de la destination de la relation comme dans le cas du nom.
templates/player_list.html
<!DOCTYPE html>
<html>
<head>
<title>sample</title>
</head>
<body>
<table>
<tr>
<th>Nom</th>
<th>équipe</th>
</tr>
{% for player in soccerplayer_list %}
<tr>
<td>{{ player.name }}</td>
<td>{{ player.team }}</td>
</tr>
{% endfor %}
</table>
</body>
</html>
Django obtiendra également l'enregistrement associé à l'objet Player, même si vous ne le spécifiez pas. Au début, je n'ai pas compris ce sentiment et j'ai eu du mal à m'y habituer.
Vient ensuite le cas de ManytoManyField, une connexion n-to-n.
Puisqu'il va de n à n, plusieurs enregistrements sont récupérés. Dans ce cas, il y a plusieurs positions par joueur.
Contrairement à 1 à n etc., il ne peut pas être obtenu avec player.position
. Vous devez le créer player.position.all
et le récupérer avec for.
templates/player_list.html
<!DOCTYPE html>
<html>
<head>
<title>sample</title>
</head>
<body>
<table>
<tr>
<th>Nom</th>
<th>équipe</th>
<th>position</th>
</tr>
{% for player in soccerplayer_list %}
<tr>
<td>{{ player.name }}</td>
<td>{{ player.team }}</td>
<td>
{% for position in player.position.all %}
{{ position.name }}
{% endfor %}
</td>
</tr>
{% endfor %}
</table>
</body>
</html>
(Les joueurs que vous mettez sont trop adaptés et il y a peu de joueurs à plusieurs positions)
Ensuite, essayons la soi-disant référence inversée, Team-> Player.
Dans le cas de la référence inverse, contrairement au modèle précédent, le champ n'est pas défini dans le modèle. Cependant, bien qu'il ne soit pas inclus dans le champ modèle, il est correctement référencé. Pour être clair et facile à comprendre, il est préférable de définir related_name.
sample/models.py
#Partiellement omis
class Team(models.Model):
name = models.CharField(max_length=40)
country = models.CharField(max_length=40)
#l'équipe n'a pas de champ de joueur!
class SoccerPlayer(models.Model):
name = models.CharField(max_length=40)
foot = models.CharField(max_length=5, choices=FOOT_CHOICES)
# related_En cas de référence inversée avec nom, précisez la phrase à utiliser
team = models.ForeignKey(Team, null=True, on_delete=models.SET_NULL, related_name='player')
position...
Lorsque vous obtenez (Player from Team) de l'opposé, vous pouvez l'obtenir en utilisant le nom défini dans ce related_name. Ici, puisque "player" est spécifié, le record du joueur associé à cette équipe peut être obtenu avec "team.player.all".
sample/views.py
#ajouter à
from .models import Team
class TeamList(ListView):
model = Team
template_name = 'team_list.html'
sample/urls.py
from django.urls import path
from . import views
app_name = 'sample'
urlpatterns = [
path('list/', views.player_list, name='player_list'),
path('list/team/', views.team_list, name='team_list'),
]
Si vous l'obtenez avec team.player.all
, vous pouvez le retirer un par un avec for, ce qui est le même qu'avant.
templates/team_list.html
<!DOCTYPE html>
<html>
<head>
<title>sample</title>
</head>
<body>
<table>
<tr>
<th>Nom</th>
<th>Nombre de personnes</th>
<th>joueur</th>
</tr>
{% for team in team_list %}
<tr>
<td>{{ team.name }}</td>
<td>{{ team.player.all.count }}</td>
<td>
{% for player in team.player.all %}
{{ player }}
{% endfor %}
</td>
</tr>
{% endfor %}
</table>
</body>
</html>
Si vous accédez à http: // localhost: 8000 / sample / list / team /
, vous pouvez voir que les joueurs associés à chaque équipe ont été acquis. (Si vous ne comprenez pas le football, cela ne vous viendra peut-être pas. Je suis désolé.)
Sur la page de la liste des équipes, utilisez django-debug-toolbar pour vérifier le SQL. Si vous cliquez sur le panneau SQL de django-debug-toolbar affiché sur le côté, cela ressemble à ceci. C'est petit et déroutant, mais 13 SQL sont en cours d'exécution pour afficher les informations sur cette page. Ce phénomène est dû au fait que la boucle du modèle obtient à chaque fois les informations de la destination de la relation. (C'est ce qu'on appelle le problème N + 1) À ce rythme, à mesure que le nombre d'enregistrements augmente, le nombre d'exécutions SQL lors du chargement de la page augmente, ce qui entraîne une page extrêmement lourde.
Pour résoudre ce problème, utilisez une méthode appelée prefetch_related.
Tout simplement prefetch_related récupère les enregistrements de la base de données à l'avance et les associe chacun à python (Django) select_related est obtenu par JOIN à partir de DB C'est une méthode appelée. (Je m'excuse si je me trompe.)
Dans le cas de ListView, View a une méthode appelée * get_queryset *, qui peut être réécrite.
sample/views.py
class TeamList(ListView):
model = Team
template_name = 'team_list.html'
def get_queryset(self):
#Méthode pour obtenir le jeu de requêtes à afficher dans View
qs = self.model.objects.prefetch_related('player') # team ->Puisqu'il s'agit d'une référence inverse du joueur, liée_Nom d'utilisation
return qs
En faisant cela, le nombre d'exécutions SQL a été réduit à deux.
Si vous consultez également la première page de la liste des joueurs, 13 requêtes ont été émises.
Cette page récupère l'équipe et la position du joueur. Même dans ce cas, vous pouvez connecter prefetch_related et select_related.
sample/views.py
class PlayerListView(ListView):
model = SoccerPlayer
template_name = 'player_list.html'
def get_queryset(self):
qs = self.model.objects.prefetch_related('position').select_related('team')
return qs
Cela a réduit le nombre de requêtes émises à 2.
Si vous vérifiez le SQL et voyez une notation telle que «6 requêtes similaires» ou «4 doublons», n'oubliez pas d'utiliser prefetch_related (select_related).
J'espère que ça t'as aidé. Personnellement, j'aimerais augmenter la production de Django, qui aurait moins d'informations japonaises.
Recommended Posts