Analyse de la source pour Django - INSTALLED_APPS

templarbit-illustration-django-1a748506.jpg

Préface

Je vais vous expliquer le processus menant à la rédaction de cet article, j'avais cette question sur teratail Je ne sais pas comment enseigner le projet lors de l'activation du modèle Django

Le contenu de la question était le suivant.

Pourquoi le module INSTALLED_APPS de Django serait-il placé comme ʻapp name.apps.app name config, En termes de structure de répertoires, il devrait s'agir de ../app name / apps.app name config`.

Je voudrais donc expliquer le code source de Django pour ʻINSTALLED_APPS`. Si vous êtes intéressé par le cœur de Django, veuillez rester avec nous jusqu'à la fin. : détendu:

Préparation

Pour expliquer, je vais créer un projet simple appelé django_test, la structure des répertoires est la suivante.

djnago_test
|-- django_test
|-- |-- __init__.py
|-- |-- asgi.py
|-- |-- settings.py
|-- |-- urls.py
|-- |-- wsgi.py
|-- manage.py

runserver Lancez votre projet Django depuis la ligne de commande.

python manage.py runserver

Alors que s'est-il passé après cela, jetons un coup d'œil au contenu de manage.py.

manage.py


#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys


def main():
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_test.settings')
    try:
        from django.core.management import execute_from_command_line
    except ImportError as exc:
        raise ImportError(
            "Couldn't import Django. Are you sure it's installed and "
            "available on your PYTHONPATH environment variable? Did you "
            "forget to activate a virtual environment?"
        ) from exc
    execute_from_command_line(sys.argv)


if __name__ == '__main__':
    main()

--setdefault ajoute les données du dictionnaire de clé et de valeur à ʻenviron`, si la clé existe, obtient la valeur.

Tout d'abord, regardons le contenu de sys.argv, le chemin de manage.py et la commande d'exécution.

['manage.py', 'runserver']

Ensuite, regardez le contenu de ʻexecute_from_command_line (sys.argv) `.

management/__init__.py


def execute_from_command_line(argv=None):
    """Run a ManagementUtility."""
    utility = ManagementUtility(argv)
    utility.execute()

Le contenu est très simple, il prend la valeur de sys.argv, J'ai instancié l'utilitaire de gestion et j'ai exécuté la fonction «exécuter». Jetons un coup d'œil à la fonction __init__ de ManagementUtility.

management.py


    def __init__(self, argv=None):
        self.argv = argv or sys.argv[:]
        self.prog_name = os.path.basename(self.argv[0])
        if self.prog_name == '__main__.py':
            self.prog_name = 'python -m django'
        self.settings_exception = None

dans ce cas:

Regardons le suivant. Que fait la fonction ʻexecute` Le contenu de la fonction est très long, donc je n'expliquerai que les parties importantes.

management.py


try:
    subcommand = self.argv[1]
except IndexError:
    subcommand = 'help'

python manage.py runserver Lors de l'exécution, la valeur de la sous-commande sera runserver. Entrez ensuite le bloc ci-dessous.

management.py


...
if subcommand == 'runserver' and '--noreload' not in self.argv:
   try:
      autoreload.check_errors(django.setup)()
...

Jetons un œil au contenu de django.setup.

django/__init__.py


...
def setup(set_prefix=True):
    """
    Configure the settings (this happens as a side effect of accessing the
    first setting), configure logging and populate the app registry.
    Set the thread-local urlresolvers script prefix if `set_prefix` is True.
    """
    from django.apps import apps
    from django.conf import settings
    from django.urls import set_script_prefix
    from django.utils.log import configure_logging

    configure_logging(settings.LOGGING_CONFIG, settings.LOGGING)
    if set_prefix:
        set_script_prefix(
            '/' if settings.FORCE_SCRIPT_NAME is None else settings.FORCE_SCRIPT_NAME
        )
    apps.populate(settings.INSTALLED_APPS)

À ce stade, le processus lié à ʻINSTALLED_APPSest devenu clair, Jetons un coup d'œil à la source depopulate`, c'est assez long, donc je n'expliquerai que les parties importantes.

registry.py


...
    def populate(self, installed_apps=None):
        ...
            for entry in installed_apps:
                if isinstance(entry, AppConfig):
                    app_config = entry
                else:
                    app_config = AppConfig.create(entry)
                if app_config.label in self.app_configs:
                    raise ImproperlyConfigured(
                        "Application labels aren't unique, "
                        "duplicates: %s" % app_config.label)

                self.app_configs[app_config.label] = app_config
                app_config.apps = self
...

Le contenu de cette ʻinstalled_apps` est le suivant, boucle et sort un par un et lance le processus suivant. Une chose à noter est que le contenu comme `` 'django.contrib.admin' 'est de type ** string **.

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

Le contenu de la boucle est un jugement conditionnel de la relation d'héritage pour les éléments contenus dans ʻINSTALLED_APPS`.

if isinstance(entry, AppConfig):
    app_config = entry
else:
    app_config = AppConfig.create(entry)

Bien sûr, ce n'est plus qu'une chaîne maintenant, donc il va dans le bloc else.

app_config = AppConfig.create(entry)

Jetons un œil au contenu de create ().

config.py


    def create(cls, entry):
        ...
        try:
            module = import_module(entry)
        except ImportError:
            module = None
            mod_path, _, cls_name = entry.rpartition('.')

            if not mod_path:
                raise
        ...

À ce stade, je voudrais utiliser la fonction ʻimport_module, Créez une application ʻusers ʻapp et ajoutez-la à ʻINSTALLED_APPS.

python manage.py startapp users
INSTALLED_APPS= [
...
users or users.apps.UsersConfig
]

Lorsqu'il est ajouté en tant que «utilisateurs».

test.py


import os
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_test.settings')

from importlib import import_module
print(import_module("users"))
# <module 'users' from '/Users/user/django/django_test/users/__init__.py'>

Il a trouvé l'application correctement.

Lorsqu'il est ajouté en tant que users.apps.UsersConfig.

test.py


import os
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_test.settings')

from importlib import import_module
print(import_module("users.apps.UsersConfig"))
# ModuleNotFoundError: No module named 'users.apps.UsersConfig'; 'users.apps' is not a package

Je n'arrive pas à trouver l'application, je suis donc dans le bloc ci-dessous.

config.py


except ImportError:
    module = None
    mod_path, _, cls_name = entry.rpartition('.')

    if not mod_path:
       raise

mod_path, _, cls_name = entry.rpartition ('.') Est exécuté et La valeur de mod_path sera ʻusers.apps et cls_name sera ʻUsersConfig. Je vais le tester à nouveau.

main.py


import os
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_test.settings')

from importlib import import_module
mod_path, _, cls_name = "users.apps.UsersConfig".rpartition('.')
print(import_module(mod_path))
# <module 'users.apps' from '/Users/user/django/django_test/users/apps.py'>

Il a trouvé l'application.

Recommended Posts

Analyse de la source pour Django - INSTALLED_APPS
Structure des dossiers pour l'analyse
Python pour l'analyse des données Chapitre 4
10 Crawlers Web Open Source pour 2020
Python pour l'analyse des données Chapitre 2
Conseils et précautions lors de l'analyse des données
Python pour l'analyse des données Chapitre 3
Notes pour une analyse vidéo de basketball difficile
Modèle de prétraitement pour l'analyse des données (Python)
Analyse de données pour améliorer POG 3 ~ Analyse de régression ~
analyse de la source linux (noyau): appel système
Outil de visualisation Python pour le travail d'analyse de données
Programme d'analyse des tendances Twitter (mémo personnel)
JupyterLab Basic Setting 2 pour l'analyse des données (pip)
Configuration de base de JupyterLab pour l'analyse des données (pip)