[Python] Lecture du code source Django Vue à partir de zéro ①

Afficher la version ② FormView

introduction

"Lire le code source" est l'un des moyens efficaces d'apprendre la programmation. Le célèbre Become a Hacker (titre original: How To Become A Hacker) dit également:

Mais disons simplement que ce n'est pas bon pour les livres et les cours en atelier. De nombreux hackers, voire la plupart, ont étudié à leur manière. Il est utile (a) de lire le code et (b) d'écrire le code.

Cependant, j'estime qu'il y a beaucoup de gens qui "écrivent souvent du code" mais ne sont pas très doués pour "lire du code". (Il est souvent plus difficile de "lire" en termes de difficulté ...) Dans cet article, je vais lire Django, un framework web majeur de Python, et vous donner une atmosphère de lecture du code source. J'espère que cela augmentera le nombre de personnes souhaitant lire le code open source!

Cette fois, comme première étape, nous suivrons le flux de la vue basée sur les classes.

Je ne sais pas comment l'écrire dans le premier message de Qiita, donc si vous faites une erreur en citant, il serait utile que vous puissiez le signaler dans les commentaires.

environnement

Nous procéderons dans l'environnement suivant. Au moment de la rédaction de cet article (novembre 2020), Django est le dernier 2.2.17, et vous n'avez pas besoin d'être trop précis sur la version Python, mais cette fois, nous allons procéder avec 3.8.5. Lors de la lecture du code source, il est recommandé d'utiliser votre éditeur ou IDE préféré qui peut utiliser des sauts de balises tels que PyCharm et Eclipse.

Pour le code source de Django, vérifiez l'emplacement d'installation avec $ python -c" import django; print (django .__ path__) ", ou vérifiez l'emplacement d'installation ou Official Repository Tirons de .17).

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

Connaissances préalables

--Comment comprendre Python 3.x --Comment comprendre le [Tutoriel] de Django (https://docs.djangoproject.com/ja/2.2/intro/)

Essayez de lire

Afficher les bases

Exemple de code: https://github.com/tsuperis/read_django_sample

Les vues peuvent être écrites dans les deux fonctions / classes de Django.

hoge/views.py


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


def function_based_view(request):
    """Vue basée sur les fonctions"""
    return HttpResponse('function_based_view')


class ClassBasedView(View):
    """Vue basée sur les classes"""
    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'),
]

c'est,

--Lorsque vous accédez à / func /, "function_based_view" est renvoyé quelle que soit la méthode de requête HTTP. --Lorsque vous demandez / cls / avec GET, "class_based_view GET" est renvoyé. --Lorsque vous faites une requête POST à / cls /, "class_based_view POST" est renvoyé.

Cela signifie que · · · Pourquoi les vues basées sur les fonctions et les vues basées sur les classes se comportent-elles différemment?

Tout d'abord, si vous regardez la vue de base de fonction simple function_based_view et urls.py, il semble que la fonction soit [django.urls.path](https://docs.djangoproject.com/en/2.2/ref/urls/# Il semble fonctionner comme une vue s'il est défini dans le deuxième argument de chemin).

Alors qu'en est-il des vues basées sur la classe? Si vous regardez urls.py, vous remarquerez que la plus grande différence est as_view (). Lisons d'ici.

View.as_view()

Tapez une commande dans l'interpréteur pour voir où elle se trouve, ou utilisez la fonction de saut de balise pour parcourir la classe directement.

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

(chemin d'installation de django)/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

Ignorez cette fois le décorateur classonlymethod.

Reconnaissez quelque chose comme classmethod (un décorateur attaché à une méthode qui peut être appelée dans une classe non instanciée). C'est la raison pour laquelle vous pouvez écrire ClassBasedView.as_view () au lieu de ClassBasedView (). As_view ().

Pour revenir à la ligne principale, il y a deux arguments

cls est une convention de méthodes de classe et contient ClassBasedView. Les arguments qui commencent par , tels que « initkwargs», sont appelés arguments de mot-clé de longueur variable et sont traités comme des types de dict en prenant des arguments nommés arbitraires.

Par exemple, si vous appelez as_view (name = 'view', number = 1), le contenu de initkwargs sera {'name': 'view', number = 1}.

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

(B) Vérifier les arguments dans une boucle

Il n'y a pas de traitement difficile, donc je vais devenir croustillant. Il semble que initkwargs (type dict) soit en boucle et que le nom de l'argument soit vérifié.

Comme condition d'erreur

  1. key (nom de l'argument) existe dans ClassBasedView.http_method_names
  2. key n'existe pas dans les attributs de la classe ClassBasedView

Quel est le http_method_names de 1? Il est défini au début de la classe View.

(chemin d'installation de django)/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']

Autrement dit, dans cette boucle tous les noms d'argument

  1. Ne pas obtenir, publier, mettre, patcher, supprimer, tête, options, trace
  2. Existe en tant que nom d'attribut d'une classe telle que as_view, http_method_names

J'ai constaté que je vérifiais.

(C) Création de la fonction de visualisation

C'est le cœur de cette époque. Définit la fonction d'affichage. Si vous regardez également la valeur de retour de as_view, vous pouvez voir que cette fonction est retournée.

Comparons la vue basée sur la fonction créée précédemment avec la fonction de vue.

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

Puisque * args est appelé un argument de longueur variable et que l'entrée est arbitraire, le seul argument requis de la fonction de vue est le premier argument. En d'autres termes, il peut être appelé comme function_based_view, donc il peut être utilisé comme une vue basée sur une fonction.

Voyons la suite. J'ai instancié la classe puis appelé la méthode setup`` dispatch.

Instanciation

«init» ressemble à ceci. J'ai défini l'argument nommé de as_view comme attribut de l'instance, mais comme je n'ai pas spécifié d'argument pour as_view cette fois, je l'ignorerai.

(chemin d'installation de django)/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)

méthode de configuration

C'est facile ici, donc je vais juste mettre le code dessus. Le self.request, qui est souvent vu lors de l'utilisation de la vue basée sur les classes, est défini à ce moment.

(chemin d'installation de django)/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

méthode d'expédition

request.method contient littéralement le nom de la méthode de requête HTTP. Vous essayez d'obtenir la même méthode d'instance que le nom de la méthode de requête HTTP avec getattr.

En d'autres termes

--Lorsque vous demandez / cls / avec GET, "class_based_view GET" est renvoyé. --Appeler self.get --Lorsque vous faites une requête POST vers / cls /, "class_based_view POST" est renvoyé. --Appeler self.post

Semble venir d'ici.

(chemin d'installation de django)/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)  #ici
        else:
            handler = self.http_method_not_allowed
        return handler(request, *args, **kwargs)

afficher le résumé de la fonction

Je résumerai brièvement le flux de traitement de la fonction d'affichage.

  1. Instanciez la classe View
  2. Définissez self.request`` self.kwargs`` self.args
  3. Appelez la même méthode d'instance que le nom de la méthode de requête HTTP

(D) update_wrapper

Ce n'est pas le code de Django, mais c'est important lors de la création de décorateurs originaux, je vais donc le présenter brièvement.

console


>>> from functools import update_wrapper
>>> def view():
...     """C'est la fonction d'affichage"""
...     pass
... 
>>> def dispatch():
...     """C'est la fonction d'expédition"""
...     pass
... 
>>> view.__doc__
'C'est la fonction d'affichage'
>>> update_wrapper(view, dispatch)
<function dispatch at 0x7f90328194c0>
>>> view.__doc__
'C'est la fonction d'expédition'

L'exemple ressemble à ceci, comprenez-vous? Lorsque vous appelez la fonction, view .__ doc__ est remplacé par dispatch .__ doc__. En plus de __doc__, il y a Attributs à écraser, mais essentiellement les métadonnées Il est utilisé en remplacement.

Dans ce cas, il est utilisé pour permettre à une nouvelle fonction appelée view d'hériter des métadonnées du corps de la classe et de la méthode dispatch.

Sommaire

Un simple résumé du processus as_view est" Créer une fonction de vue qui appelle la méthode d'instance correspondant à la méthode de requête HTTP. "

J'ai essayé de suivre le code source à la hâte, mais quand je l'ai lu

--Lire après avoir confirmé l'opération réelle ――Pour les parties qui ne sont pas liées à la ligne principale, jetez un coup d'œil au nom et au contenu de la fonction pour en avoir une idée.

Je pense que c'est le point. Vous pouvez suivre le traitement détaillé, mais si vous ne comprenez pas tout, vous ne saurez pas ce que vous faisiez.

la prochaine fois

Je n'ai pas décidé en particulier, mais je vais lire FormView ou Form. J'aimerais aussi lire Model, mais cela semble long à cause de la métaprogrammation, je vais donc l'essayer après m'être habitué un peu plus à Qiita. .. ..

J'ai écrit la suite

Afficher la version ② FormView

Recommended Posts

[Python] Lecture du code source Django Vue à partir de zéro ①
Code wars kata à partir de zéro
Soit Code Day58 à partir de zéro "20. Parenthèses valides"
Soit Code Day16 à partir de zéro "344. Reverse String"
Soit Code Day49 à partir de zéro "1323. Maximum 69 Number"
Construction d'environnement explosif Python à partir de zéro (Mac)
Let Code Day89 "62. Chemins uniques" à partir de zéro
Let Code Day 55 "22. Générer des parenthèses" à partir de zéro
Codewars kata à partir de zéro, Nampre
Let Code table à partir de zéro
Soit Code Day18 à partir de zéro "53. Maximum Subarray"
Let Code Day 13 "338. Comptage des bits" à partir de zéro
Let Code Day71 À partir de zéro "1496. Traversée de chemin"
Let Code Day 61 "7. Integer Integer" à partir de zéro
Let Code Day 82 "392. Is Subsequence" Partant de zéro
Let Code Day51 "647. Sous-chaînes palindromiques" à partir de zéro
Let Code Day 50 "739. Températures quotidiennes" à partir de zéro
Let Code Day 15 "283. Move Zeroes" à partir de zéro
Soit Code Day14 à partir de zéro "136. Numéro unique"
Installer Python à partir de la source
Lecture de données CSV à partir du code Python de stockage d'objets DSX
Let Code Day 43 à partir de zéro "5. Le plus long substrat palindromique"
Soit Code Day74 à partir de zéro "12. Integer to Roman"
Let Code Day 42 "2. Add Two Numbers" en partant de zéro
Let Code Day57 À partir de zéro "35. Rechercher Insérer la position"
Soit Code Day47 à partir de zéro "14. Préfixe commun le plus long"
Soit Code Day78 à partir de zéro "206. Liste liée inversée"
Django à partir de zéro (partie: 2)
Django à partir de zéro (partie: 1)
Installer ansible à partir du code source
Analyse ChIP-seq à partir de zéro
Arrêtez Omxplayer à partir du code Python
Soit Code Day 44 "543. Diamètre de l'arbre binaire" à partir de zéro
Soit Code Jour 64 à partir de zéro "287. Trouver le numéro en double"
[Tweepy] Re: Développement de Twitter Bot à partir de zéro # 1 [python]
Supprimer les commentaires sur une ligne, y compris le japonais du code source en Python
Soit Code Jour 84 à partir de zéro "142. Cycle de liste liée II"
Let Code Day24 À partir de zéro "21. Fusionner deux listes triées"
Laissez Code Day12 partir de zéro "617. Fusionner deux arbres binaires"
Soit Code Day2 à partir de zéro "1108. Defanging an IP Address"
Let Code Day70 À partir de zéro "295. Trouver la médiane à partir du flux de données"
Let Code Day81 "347. Top K éléments fréquents" à partir de zéro
Let Code Day48 Starting from Zero "26. Supprimer les doublons du tableau trié"
Soit Code Day87 à partir de zéro "1512. Nombre de bonnes paires"
Installez Python à partir des sources avec Ansible
Soit Code Day67 à partir de zéro "1486. Opération XOR dans un tableau"
Let Code Day56 À partir de zéro "5453. Somme exécutée de 1d Array"
Let Code Day7 À partir de zéro "104. Profondeur maximale de l'arbre binaire"
Let Code Day86 à partir de zéro "33. Recherche dans un tableau trié avec rotation"
Soit Code Day92 à partir de zéro "4. Médiane de deux tableaux triés"
Let Code Day5 À partir de zéro "1266. Durée minimale de visite de tous les points"
Afficher les notifications du bureau Mac à partir de Python
Exécuter du code Python à partir de l'interface graphique C #
[Python] Lire le code source de Flask
Re: Vie de programmation compétitive à partir de zéro Chapitre 1.2 "Python of tears"
Let Code Day 35 "160. Intersection de deux listes liées" à partir de zéro
Let Code Day83 À partir de zéro "102. Traversée de l'ordre au niveau de l'arborescence binaire"
Soit Code Day 40 à partir de zéro "114. Aplatir l'arbre binaire à la liste liée"
Let Code Day 91 "153. Find Minimum in Rotated Sorted Array" à partir de zéro
Soit Code Day59 à partir de zéro "1221. Fractionner une chaîne en chaînes symétriques"
Let Code Day 11 À partir de zéro "1315. Somme des nœuds avec un grand-parent pair"