[PYTHON] [Django 2.2] Wie zeigen Sie das Beziehungsziel in Django an? [Listenansicht]

Einführung

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".

Umgebung

Voraussetzung Modell

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.

Spielerliste anzeigen

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. スクリーンショット 2019-12-16 23.16.56.png


Extraktion von durch Foreignkey verknüpften Werten

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>

スクリーンショット 2019-12-16 23.30.16.png

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.


In ManytoManyField verknüpfte Werte abrufen

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.) スクリーンショット 2019-12-16 23.45.25.png

Im Falle einer umgekehrten Bezugnahme

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.) スクリーンショット 2019-12-17 0.27.10.png

Überprüfen Sie die ausgegebene SQL

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. スクリーンショット 2019-12-17 0.33.10.png 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.

prefetch_related und select_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. スクリーンショット 2019-12-17 0.51.25.png

Wenn Sie auch die erste Spielerlistenseite überprüfen, wurden 13 Abfragen ausgegeben. スクリーンショット 2019-12-17 0.55.26.png

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. スクリーンショット 2019-12-17 0.59.54.png

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.

Zusammenfassung

Ich hoffe, Sie finden das hilfreich. Persönlich möchte ich die Ausgabe von Django erhöhen, das angeblich weniger japanische Informationen enthält.

Recommended Posts

[Django 2.2] Wie zeigen Sie das Beziehungsziel in Django an? [Listenansicht]
So führen Sie vom Server gesendete Ereignisse in Django durch
Python - Wie teilt man eine Liste in Python in gleich große Teile auf?
Wenn Sie View decorator in urls.py in Django schreiben, ist die Liste höher.
Verwendung von Bootstrap in der generischen Klassenansicht von Django
Hochladen von Dateien in der generischen Klassenansicht von Django
Wie sammelst du Informationen?
[Python] Wie man PCA mit Python macht
Wie man CSS in Django reflektiert
Wie viele Arten von Python haben Sie in Ihrem Windows 10? Ich hatte 5 Typen.
So finden Sie den Ansichtsnamen mit dem Namespace über die URL (path_info) in Django
So führen Sie eine arithmetische Verarbeitung mit der Django-Vorlage durch
Wenn base.html in Django nicht aufgerufen werden kann
So unterdrücken Sie Anzeigefehler in matplotlib
Django ~ Lass es uns im Browser anzeigen ~
So machen Sie R chartr () in Python
Zeigen Sie eine Liste der Alphabete in Python 3 an
So löschen Sie abgelaufene Sitzungen in Django
So zeigen Sie die neunundneunzig Tabelle in Python an
Ajax in Django (mit generischer Klassenansicht)
So zeigen Sie Hello World in Python an
So zeigen Sie Bilder in Djangos Admin an
So identifizieren Sie die Zugriffsquelle in der generischen Klassenansicht von Django eindeutig
Wenn Sie den Wert mithilfe von Auswahlmöglichkeiten in der Vorlage im Django-Modell anzeigen möchten
Was gefällt Ihnen an der Konvertierung eines Arrays (einer Liste) in einen String?