Hallo. Mein Name ist Ragnar und ich habe 1,5 Monate Erfahrung in Django (in der Praxis). Als ich mit Django anfing, habe ich einige Tutorials gemacht, aber als es darum ging, es tatsächlich zu machen, gab es viele neue Konzepte und ich war mir nicht sicher, was ich tun sollte.
Das erste, was stecken blieb, war die Beziehung. Bevor ich mich mit Django befasst habe, habe ich SQL geschrieben und DB betrieben, daher verstehe ich das Konzept von Queryset nicht gut. Kann ich damit Daten abrufen? Wie ist das? Es war in einem Zustand.
Diesmal ist es ein Artikel für diejenigen, die "versucht haben, Django mit dem Tutorial zu berühren, aber nicht gut verstanden haben".
Dieses Mal gehen wir von einer Website aus, auf der Sie eine Liste mit Fußballmannschaften, Spielern und Spielerinformationen sehen können. Ich habe eine Tabelle (Modell) von Teams, Positionen und Spielern erstellt.
sample/models.py
from django.db import models
FOOT_CHOICES = (
('right', 'Rechter Fuß'),
('left', 'linker Fuß'),
('both', 'Beide Füße'),
)
class Team(models.Model):
name = models.CharField(max_length=40)
country = models.CharField(max_length=40)
def __str__(self):
return self.name
class Meta:
#Der auf der Django-Administrationsseite angezeigte Name
verbose_name = "Mannschaft"
verbose_name_plural = "Mannschaft"
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)
#Die Athleten gehören einem Verein an. Dem Team gehören mehrere Spieler an. 1 bis n Struktur
#Spieler können nicht verbunden sein. auf_delete=models.SET_NULL
team = models.ForeignKey(Team, null=True, on_delete=models.SET_NULL, related_name='player')
#Athleten können mehrere Positionen einnehmen. Es gibt mehrere Spieler auf einer Position. n bis n Struktur
position = models.ManyToManyField(Position, related_name='player')
def __str__(self):
return self.name
class Meta:
verbose_name = "Spieler"
verbose_name_plural = "Spieler"
Registrieren Sie das Modell, damit Sie die Daten von der Django-Verwaltungssite aus registrieren können. (Ich habe die Daten entsprechend eingegeben.)
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)
Wir haben auch die Django-Debug-Symbolleiste eingeführt, um Abfragen zu überprüfen. Es ist keine Übertreibung zu sagen, dass es für die Entwicklung von Django unverzichtbar ist. Wenn Sie es also nicht aufgenommen haben, tun Sie dies bitte zu diesem Zeitpunkt.
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') #Diese App
]
if settings.DEBUG:
import debug_toolbar
urlpatterns = [
path('__debug__/', include(debug_toolbar.urls)),
] + urlpatterns
ListView Django hat eine Webanwendung namens Generic View. GET-Methode und Anzeigeseite empfangen, POST-Methode empfangen, Formular validieren, Datensatz aktualisieren, löschen ... usw. Es gibt eine Klasse mit sehr allgemeinem Verhalten.
Dieses Mal werden wir die Listenanzeige und die Behandlung von Beziehungen mit ListView zusammenfassen, das auf die Listenanzeige von Datensätzen spezialisiert ist.
Lassen Sie uns zuerst die Namen aller Spieler herausnehmen.
sample/views.py
from django.views.generic import ListView
from .models import SoccerPlayer
class PlayerListView(ListView):
model = SoccerPlayer #Modell zum Anzeigen einer 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>Name</th>
</tr>
{% for player in soccerplayer_list %}
<tr>
<td>{{ player.name }}</td>
</tr>
{% endfor %}
</table>
</body>
</html>
Das erste, woran Sie sich in ListView erinnern sollten, ist, dass View ein Objekt mit dem Namen ** [Modellname (unten)] _list ** an die Vorlage übergibt. (Sie können es auch selbst angeben.) Sie können dies mit for in der Vorlage und den Namen mit ** [Variablen, die mit for abgerufen wurden] .name ** abrufen. ** Name ** ist das Feld des Modells, das vom Modell definiert wird. Ich denke, ich habe es wahrscheinlich in Tutorials gemacht.
Wenn Sie auf "http: // localhost: 8000 / sample / list /" zugreifen, können Sie sehen, dass die Liste angezeigt werden kann.
Als nächstes nehmen wir das Team heraus, das dem Spieler zugeordnet ist. Selbst wenn Sie das Beziehungsziel angeben, entspricht dies bei OnetoOneField-Beziehungen (1 zu 1) und Foreignkey-Beziehungen (1 zu n) dem normalen Schreiben. Geben Sie einfach den Feldnamen des Beziehungsziels wie im Fall des Namens an.
templates/player_list.html
<!DOCTYPE html>
<html>
<head>
<title>sample</title>
</head>
<body>
<table>
<tr>
<th>Name</th>
<th>Mannschaft</th>
</tr>
{% for player in soccerplayer_list %}
<tr>
<td>{{ player.name }}</td>
<td>{{ player.team }}</td>
</tr>
{% endfor %}
</table>
</body>
</html>
Django erhält auch den dem Player-Objekt zugeordneten Datensatz, auch wenn Sie ihn nicht angeben. Zuerst verstand ich dieses Gefühl nicht und hatte es schwer, mich daran zu gewöhnen.
Als nächstes folgt ManytoManyField, eine n-zu-n-Verbindung.
Da es n bis n ist, werden mehrere Datensätze abgerufen. In diesem Fall gibt es mehrere Positionen pro Spieler.
Im Gegensatz zu 1 bis n usw. kann es nicht mit "player.position" erhalten werden. Sie müssen es player.position.all
machen und es mit for abrufen.
templates/player_list.html
<!DOCTYPE html>
<html>
<head>
<title>sample</title>
</head>
<body>
<table>
<tr>
<th>Name</th>
<th>Mannschaft</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>
(Die Spieler, die Sie einsetzen, sind zu geeignet und es gibt nur wenige Spieler auf mehreren Positionen.)
Als nächstes versuchen wir es mit der sogenannten umgekehrten Referenz Team-> Player.
Bei der umgekehrten Referenz ist das Feld im Gegensatz zum vorherigen Muster im Modell nicht definiert. Obwohl es nicht im Modellfeld enthalten ist, wird es ordnungsgemäß referenziert. Um es klar und verständlich zu machen, ist es besser, related_name zu definieren.
sample/models.py
#Teilweise weggelassen
class Team(models.Model):
name = models.CharField(max_length=40)
country = models.CharField(max_length=40)
#Team hat kein Spielerfeld!
class SoccerPlayer(models.Model):
name = models.CharField(max_length=40)
foot = models.CharField(max_length=5, choices=FOOT_CHOICES)
# related_Geben Sie bei umgekehrter Referenz mit Namen die zu verwendende Phrase an
team = models.ForeignKey(Team, null=True, on_delete=models.SET_NULL, related_name='player')
position...
Wenn Sie (Spieler vom Team) vom Gegenteil erhalten, können Sie es unter Verwendung des in diesem verwandten Namen festgelegten Namens erhalten. Da hier "Spieler" angegeben ist, kann der Datensatz des mit diesem Team verbundenen Spielers mit "team.player.all" abgerufen werden.
sample/views.py
#hinzufügen
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'),
]
Wenn Sie es mit team.player.all
bekommen, können Sie es einzeln mit for herausnehmen, was das gleiche wie zuvor ist.
templates/team_list.html
<!DOCTYPE html>
<html>
<head>
<title>sample</title>
</head>
<body>
<table>
<tr>
<th>Name</th>
<th>Anzahl der Personen</th>
<th>Spieler</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>
Wenn Sie auf "http: // localhost: 8000 / sample / list / team /" zugreifen, können Sie sehen, dass die jedem Team zugeordneten Spieler erworben wurden. (Wenn Sie Fußball nicht verstehen, kommt er möglicherweise nicht zu Ihnen. Es tut mir leid.)
Verwenden Sie auf der Teamlistenseite die Symbolleiste django-debug-toolbar, um die SQL zu überprüfen. Wenn Sie in der seitlich angezeigten Django-Debug-Symbolleiste auf das SQL-Bedienfeld klicken, sieht es so aus. Es ist klein und verwirrend, aber 13 SQLs werden ausgeführt, um die Informationen auf dieser Seite anzuzeigen. Dieses Phänomen ist darauf zurückzuführen, dass die Schleife in der Vorlage jedes Mal die Informationen des Beziehungsziels abruft. (Es heißt das N + 1-Problem) Bei dieser Rate steigt mit zunehmender Anzahl von Datensätzen die Anzahl der SQL-Ausführungen beim Laden der Seite, was zu einer extrem hohen Seite führt.
Verwenden Sie zur Lösung dieses Problems eine Methode namens prefetch_related.
Einfach gesagt prefetch_related ruft Datensätze aus der Datenbank im Voraus ab und ordnet sie jeweils Python (Django) zu. select_related wird von JOIN aus der DB erhalten Es ist eine Methode namens. (Ich entschuldige mich, wenn ich falsch liege.)
Im Fall von ListView verfügt View über eine Methode namens * get_queryset *, die neu geschrieben werden kann.
sample/views.py
class TeamList(ListView):
model = Team
template_name = 'team_list.html'
def get_queryset(self):
#Methode, mit der der Abfragesatz in der Ansicht angezeigt wird
qs = self.model.objects.prefetch_related('player') # team ->Da es sich um eine umgekehrte Referenz des Spielers handelt, verwandt_benutze den Namen
return qs
Auf diese Weise wurde die Anzahl der SQL-Ausführungen auf zwei reduziert.
Wenn Sie auch die erste Spielerlistenseite überprüfen, wurden 13 Abfragen ausgegeben.
Diese Seite erhält das Team und die Position vom Spieler. Auch in diesem Fall ist es in Ordnung, prefetch_related und select_related zu verbinden.
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
Dies reduzierte die Anzahl der ausgegebenen Abfragen auf 2.
Wenn Sie die SQL überprüfen und die Notation wie "6 ähnliche Abfragen" oder "4 Duplikate" sehen, vergessen Sie nicht, prefetch_related (select_related) zu verwenden.
Ich hoffe, Sie finden das hilfreich. Persönlich möchte ich die Ausgabe von Django erhöhen, das angeblich weniger japanische Informationen enthält.