[Python] Lesen des Django-Quellcodes Ansicht ab Null ①

Version anzeigen ② FormView

Einführung

"Lesen des Quellcodes" ist eine der effizientesten Methoden, um das Programmieren zu lernen. Der berühmte Werde ein Hacker (Originaltitel: Wie man ein Hacker wird) sagt auch:

Aber sagen wir einfach, es ist nicht gut für Bücher und Workshop-Kurse. Viele oder vielleicht die meisten Hacker haben auf ihre eigene Weise studiert. Nützlich ist (a) Lesen des Codes und (b) Schreiben des Codes.

Ich habe jedoch das Gefühl, dass es viele Menschen gibt, die oft "Code schreiben", aber nicht sehr gut darin sind, "Code zu lesen". (Es ist oft schwieriger, in Bezug auf die Schwierigkeit zu "lesen" ...) In diesem Artikel werde ich Django lesen, ein wichtiges Webframework von Python, und Ihnen eine Atmosphäre zum Lesen des Quellcodes vermitteln. Ich hoffe, dies erhöht die Anzahl der Personen, die den Open Source Code lesen möchten!

Dieses Mal werden wir als ersten Schritt dem Fluss der klassenbasierten Ansicht folgen.

Ich weiß nicht, wie ich es in Qiitas erstem Beitrag schreiben soll. Wenn also ein Fehler beim Zitieren vorliegt, wäre es hilfreich, wenn Sie in den Kommentaren darauf hinweisen könnten.

Umgebung

Wir werden in der folgenden Umgebung fortfahren. Zum Zeitpunkt des Schreibens (November 2020) ist Django die neueste Version 2.2.17, und Sie müssen sich nicht zu sehr mit der Python-Version befassen, aber dieses Mal werden wir mit 3.8.5 fortfahren. Beim Lesen des Quellcodes wird empfohlen, Ihren bevorzugten Editor oder Ihre Lieblings-IDE zu verwenden, die Tag-Sprünge wie PyCharm und Eclipse verwenden können.

Überprüfen Sie für den Django-Quellcode den Installationsort mit $ python -c" import django; print (django .__ path__) " oder überprüfen Sie den Installationsort oder Official Repository Ziehen wir aus .17).

$ python --version
Python 3.8.5
$ python -m django --version
2.2.17

Vorausgesetztes Wissen

Versuche zu lesen

Grundlagen anzeigen

Beispielcode: https://github.com/tsuperis/read_django_sample

Ansichten können in beiden Funktionen / Klassen in Django geschrieben werden.

hoge/views.py


from django.http.response import HttpResponse
from django.views import View


def function_based_view(request):
    """Funktionsbasierte Ansicht"""
    return HttpResponse('function_based_view')


class ClassBasedView(View):
    """Klassenbasierte Ansicht"""
    def get(self, request):
        return HttpResponse('class_based_view GET')

    def post(self, request):
        return HttpResponse('class_based_view POST')

core/urls.py


from django.urls import path

from hoge import views as hoge_views

urlpatterns = [
    path('func/', hoge_views.function_based_view, name='hoge_func'),
    path('cls/', hoge_views.ClassBasedView.as_view(), name='hoge_cls'),
]

das ist,

Es bedeutet, dass · · · Warum verhalten sich funktionsbasierte Ansichten und klassenbasierte Ansichten unterschiedlich?

Wenn Sie sich zunächst die einfache Funktionsbasisansicht function_based_view und urls.py ansehen, scheint die Funktion django.urls.path zu sein Es scheint als Ansicht zu fungieren, wenn es im zweiten Argument von path festgelegt ist.

Was ist also mit klassenbasierten Ansichten? Wenn Sie sich urls.py ansehen, werden Sie feststellen, dass der größte Unterschied "as_view ()" ist. Lesen wir von hier aus.

View.as_view()

Geben Sie einen Befehl in den Interpreter ein, um zu sehen, wo er sich befindet, oder verwenden Sie die Tag-Jump-Funktion, um die Klasse direkt zu durchsuchen.

>>> from django.views import View
>>> View.__module__
'django.views.generic.base'

(Django-Installationspfad)/django/views/generic/base.py


    # -- (A)
    @classonlymethod
    def as_view(cls, **initkwargs):
        """Main entry point for a request-response process."""
        # -- (B)
        for key in initkwargs:
            if key in cls.http_method_names:
                raise TypeError("You tried to pass in the %s method name as a "
                                "keyword argument to %s(). Don't do that."
                                % (key, cls.__name__))
            if not hasattr(cls, key):
                raise TypeError("%s() received an invalid keyword %r. as_view "
                                "only accepts arguments that are already "
                                "attributes of the class." % (cls.__name__, key))

        # -- (C)
        def view(request, *args, **kwargs):
            self = cls(**initkwargs)
            if hasattr(self, 'get') and not hasattr(self, 'head'):
                self.head = self.get
            self.setup(request, *args, **kwargs)
            if not hasattr(self, 'request'):
                raise AttributeError(
                    "%s instance has no 'request' attribute. Did you override "
                    "setup() and forget to call super()?" % cls.__name__
                )
            return self.dispatch(request, *args, **kwargs)
        view.view_class = cls
        view.view_initkwargs = initkwargs

        # -- (D)
        # take name and docstring from class
        update_wrapper(view, cls, updated=())

        # and possible attributes set by decorators
        # like csrf_exempt from dispatch
        update_wrapper(view, cls.dispatch, assigned=())
        return view

(A) Argument

Ignorieren Sie diesmal den Dekorateur "classonlymethod".

Erkennen Sie so etwas wie "Klassenmethode" (ein Dekorator, der an eine Methode angehängt ist, die in einer nicht instanziierten Klasse aufgerufen werden kann). Dies ist der Grund, warum Sie "ClassBasedView.as_view ()" anstelle von "ClassBasedView (). As_view ()" schreiben können.

Zurück zur Hauptzeile gibt es zwei Argumente

cls ist eine Konvention von Klassenmethoden und enthält ClassBasedView. Argumente, die mit ** beginnen, wie z. B. "** initkwargs", werden als Schlüsselwortargumente variabler Länge bezeichnet und durch Verwendung beliebiger benannter Argumente als Diktiertypen behandelt.

Wenn Sie beispielsweise "as_view (name =" view ", number = 1)" aufrufen, lautet der Inhalt von initkwargs "{'name": "view", number = 1} ".

>>> def kw(hoge, **kwargs):
...     print(f'kwargs: {kwargs}')
... 
>>> kw('hoge', name='view', number=123)
kwargs: {'name': 'view', 'number': 123}

(B) Überprüfen Sie die Argumente in einer Schleife

Es gibt keine schwierige Verarbeitung, also werde ich knusprig. Es scheint, dass initkwargs (dict-Typ) eine Schleife ist und der Argumentname überprüft wird.

Als Fehlerbedingung

  1. key (Argumentname) existiert in ClassBasedView.http_method_names
  2. "Schlüssel" ist in den Attributen der "ClassBasedView" -Klasse nicht vorhanden

Was ist der http_method_names von 1? Es wird am Anfang der View-Klasse definiert.

(Django-Installationspfad)/django/views/generic/base.py


class View:
    """
    Intentionally simple parent class for all views. Only implements
    dispatch-by-method and simple sanity checking.
    """

    http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']

Das heißt, in dieser Schleife alle Argumentnamen

  1. Nicht erhalten, posten, setzen, patchen, löschen, Kopf, Optionen, verfolgen
  2. Existiert als Attributname einer Klasse wie "as_view", "http_method_names"

Ich stellte fest, dass ich nachschaute.

(C) Erstellung der Ansichtsfunktion

Dies ist das Herz dieser Zeit. Definiert die Ansichtsfunktion. Wenn Sie sich auch den Rückgabewert von "as_view" ansehen, können Sie sehen, dass diese Funktion zurückgegeben wird.

Vergleichen wir die zuvor erstellte funktionsbasierte Ansicht mit der Ansichtsfunktion.

def function_based_view(request):
def view(request, *args, **kwargs):

Da * args als Argument variabler Länge bezeichnet wird und die Eingabe beliebig ist, ist das einzige erforderliche Argument der Ansichtsfunktion das erste Argument. Mit anderen Worten, es kann als "function_based_view" bezeichnet werden, sodass es als funktionsbasierte Ansicht verwendet werden kann.

Mal sehen, wie es weitergeht. Ich habe die Klasse instanziiert und dann die "Setup" -Versandmethode aufgerufen.

Instanziierung

__init__ sieht so aus. Ich habe das benannte Argument von "as_view" als Attribut der Instanz festgelegt, aber da ich dieses Mal kein Argument für "as_view" angegeben habe, werde ich es ignorieren.

(Django-Installationspfad)/django/views/generic/base.py


    def __init__(self, **kwargs):
        """
        Constructor. Called in the URLconf; can contain helpful extra
        keyword arguments, and other things.
        """
        # Go through keyword arguments, and either save their values to our
        # instance, or raise an error.
        for key, value in kwargs.items():
            setattr(self, key, value)

Einrichtungsmethode

Hier ist es einfach, also werde ich einfach den Code darauf setzen. Die "self.request", die häufig bei Verwendung der klassenbasierten Ansicht angezeigt wird, wird zu diesem Zeitpunkt festgelegt.

(Django-Installationspfad)/django/views/generic/base.py


    def setup(self, request, *args, **kwargs):
        """Initialize attributes shared by all view methods."""
        self.request = request
        self.args = args
        self.kwargs = kwargs

Versandart

request.method enthält buchstäblich den Namen der HTTP-Anforderungsmethode. Sie versuchen, mit getattr dieselbe Instanzmethode wie den Namen der HTTP-Anforderungsmethode abzurufen.

Mit anderen Worten

Scheint von hier zu kommen.

(Django-Installationspfad)/django/views/generic/base.py


    def dispatch(self, request, *args, **kwargs):
        # Try to dispatch to the right method; if a method doesn't exist,
        # defer to the error handler. Also defer to the error handler if the
        # request method isn't on the approved list.
        if request.method.lower() in self.http_method_names:
            handler = getattr(self, request.method.lower(), self.http_method_not_allowed)  #Hier
        else:
            handler = self.http_method_not_allowed
        return handler(request, *args, **kwargs)

Funktionsübersicht anzeigen

Ich werde den Verarbeitungsablauf der Ansichtsfunktion kurz zusammenfassen.

  1. Instanziieren Sie die View-Klasse
  2. Setze self.request`` self.kwargs`` self.args
  3. Rufen Sie dieselbe Instanzmethode wie den Namen der HTTP-Anforderungsmethode auf

(D) update_wrapper

Es ist nicht Djangos Code, aber es ist wichtig, wenn Sie originelle Dekorateure erstellen. Deshalb werde ich ihn kurz vorstellen.

console


>>> from functools import update_wrapper
>>> def view():
...     """Dies ist die Ansichtsfunktion"""
...     pass
... 
>>> def dispatch():
...     """Dies ist die Versandfunktion"""
...     pass
... 
>>> view.__doc__
'Dies ist die Ansichtsfunktion'
>>> update_wrapper(view, dispatch)
<function dispatch at 0x7f90328194c0>
>>> view.__doc__
'Dies ist die Versandfunktion'

Das Beispiel sieht so aus, verstehst du? Wenn Sie die Funktion aufrufen, wird view .__ doc__ durch dispatch .__ doc__ ersetzt. Zusätzlich zu "doc" gibt es zu überschreibende Attribute, aber im Grunde die Metadaten Es wird als Ersatz verwendet.

In diesem Fall wird es einer neuen Funktion namens view ermöglicht, die Metadaten des Klassenkörpers und der Versandmethode zu erben.

Zusammenfassung

Die einfache Zusammenfassung des Prozesses "as_view" lautet "Erstellen Sie eine Ansichtsfunktion, die die Instanzmethode aufruft, die der HTTP-Anforderungsmethode entspricht."

Ich habe versucht, dem Quellcode in Eile zu folgen, aber als ich ihn tatsächlich gelesen habe

Ich denke, das ist der Punkt. Sie können die detaillierte Verarbeitung verfolgen, aber wenn Sie das Ganze nicht verstehen, wissen Sie nicht, was Sie getan haben.

nächstes Mal

Ich habe mich nicht besonders entschieden, aber ich werde FormView oder Form lesen. Ich würde auch gerne Model lesen, aber es scheint aufgrund der Metaprogrammierung lang zu sein, also werde ich es versuchen, nachdem ich mich ein bisschen mehr an Qiita gewöhnt habe. .. ..

Ich habe die Fortsetzung geschrieben

Version anzeigen ② FormView

Recommended Posts

[Python] Lesen des Django-Quellcodes Ansicht ab Null ①
Code Wars Kata ab Null
Lassen Sie Code Day58 ab Null "20. Gültige Klammern"
Lassen Sie Code Day16 von vorne beginnen "344. Reverse String"
Lassen Sie Code Day49 ab Null "1323. Maximum 69 Number".
Aufbau einer explosiven Python-Umgebung ab Null (Mac)
Lassen Sie Code Day89 "62. Unique Paths" ab Null
Lassen Sie Code Tag 55 "22. Klammern erzeugen" ab Null
Codewars Kata ab Null, Nampre
Lassen Sie die Codetabelle von Null beginnen
Lassen Sie Code Day18 ab Null "53. Maximum Subarray"
Lassen Sie Code Tag 13 "338. Bits zählen" ab Null
Lassen Sie Code Day71 ab Null "1496. Pfadkreuzung"
Lassen Sie Code Tag 61 "7. Reverse Integer" ab Null
Lassen Sie Code Tag 82 "392. Ist Folge" ab Null
Lassen Sie Code Day51 "647. Palindromic Substrings" ab Null
Lassen Sie Code Tag 50 "739. Tägliche Temperaturen" ab Null
Lassen Sie Code Day15 ab Null "283. Nullen verschieben"
Lassen Sie Code Day14 ab Null "136. Single Number"
Installieren Sie Python von der Quelle
Lesen von CSV-Daten aus dem Python-Code des DSX-Objektspeichers
Lassen Sie Code Day 43 von vorne beginnen "5. Längster palindromischer Teilstring"
Lassen Sie Code Day74 ab Null "12. Integer to Roman"
Lassen Sie Code Day 42 "2. Add Two Numbers" von vorne beginnen
Lassen Sie Code Day57 ab Null "35. Search Insert Position"
Lassen Sie Code Day47 von vorne beginnen "14. Längstes gemeinsames Präfix"
Lassen Sie Code Day78 von vorne beginnen "206. Reverse Linked List"
Django von vorne anfangen (Teil: 2)
Django von vorne anfangen (Teil: 1)
Installieren Sie ansible aus dem Quellcode
ChIP-seq-Analyse ab Null
Stoppen Sie Omxplayer vom Python-Code
Lassen Sie Code Day 44 "543. Durchmesser des Binärbaums" von vorne beginnen
Lassen Sie Code Tag 64 ab Null "287. Finden Sie die doppelte Nummer"
[Tweepy] Re: Twitter Bot Entwicklungsleben ab Null # 1 [Python]
Entfernen Sie einzeilige Kommentare einschließlich Japanisch aus dem Quellcode in Python
Lassen Sie Code Day 84 ab Null "142. Linked List Cycle II"
Lassen Sie Code Day24 ab Null "21. Zwei sortierte Listen zusammenführen"
Lassen Sie Code Day12 von vorne beginnen "617. Zwei binäre Bäume zusammenführen"
Lassen Sie Code Day2 von vorne beginnen "1108. IP-Adresse löschen"
Lassen Sie Code Day70 ab Null "295. Median aus Datenstrom suchen"
Lassen Sie Code Day81 "347. Top K Frequent Elements" ab Null
Lassen Sie Code Day48 ab Null "26. Duplikate aus sortiertem Array entfernen"
Lassen Sie Code Day87 ab Null "1512. Anzahl der guten Paare"
Installieren Sie Python von der Quelle mit Ansible
Lassen Sie Code Day67 von vorne beginnen "1486. XOR-Operation in einem Array"
Lassen Sie Code Day56 ab Null "5453. Laufende Summe von 1d Array"
Lassen Sie Code Day7 ab Null "104. Maximale Tiefe des Binärbaums"
Lassen Sie Code Day86 ab Null "33. Suche in gedrehtem sortiertem Array"
Lassen Sie Code Day92 ab Null "4. Median von zwei sortierten Arrays"
Lassen Sie Code Day5 ab Null "1266. Mindestzeit für den Besuch aller Punkte"
Anzeigen von Mac-Desktop-Benachrichtigungen in Python
Führen Sie Python-Code über die C # -GUI aus
[Python] Lesen Sie den Flask-Quellcode
Betreff: Wettbewerbsfähiges Programmierleben ab Null Kapitel 1.2 "Python der Tränen"
Lassen Sie Code Tag 35 "160. Schnittpunkt zweier verknüpfter Listen" von vorne beginnen
Lassen Sie Code Day83 ab Null "102. Order Traversal auf Binäre Baumebene"
Lassen Sie Code Tag 40 ab Null "114. Binärbaum auf verknüpfte Liste reduzieren"
Lassen Sie Code Tag 91 "153. Minimum in gedrehtem sortiertem Array finden" ab Null
Lassen Sie Code Day59 ab Null "1221. Teilen Sie einen String in ausgeglichene Strings"
Lassen Sie Code Tag 11 ab Null "1315. Summe der Knoten mit gleichwertigen Großeltern"