django-distill
a désormais un site web. En savoir plus sur :
django-distill
est un générateur et éditeur de sites statiques à configuration minimale pour Django. La plupart des versions de Django sont prises en charge, mais des versions à jour sont conseillées, y compris les versions de Django 3.x. django-distill
à partir de la version 1.7 ne prend en charge que Python 3. La prise en charge de Python 2 a été abandonnée. Si vous avez besoin de la prise en charge de Python 2, veuillez épingler django-distill
à la version 1.6 dans votre fichier requis.txt ou Pipfile. Python 3.6 ou supérieur est conseillé.
django-distill
étend les sites Django existants avec la possibilité d'exporter des sites statiques entièrement fonctionnels. Il convient aux sites tels que les blogs qui ont un front-end principalement statique mais qui souhaitent néanmoins utiliser un CMS pour gérer le contenu.
django-distill
parcourt les URL de votre projet Django en utilisant des fonctions itérables faciles à écrire pour générer les paramètres des pages que vous souhaitez enregistrer au format HTML statique. Ces fichiers statiques peuvent être automatiquement téléchargés vers un conteneur distant de type bucket tel qu'Amazon S3, Googe Cloud Files, Microsoft Azure Storage ou écrits dans un répertoire local en tant que version statique locale entièrement fonctionnelle de votre projet. La génération de sites, ou processus de distillation, peut être facilement intégrée aux flux de travail CI/CD pour déployer automatiquement des sites statiques lors de la validation. django-distill
peut être défini comme une extension de Django pour rendre les projets Django compatibles avec l'architecture de site de style "Jamstack".
django-distill
se connecte directement au framework Django existant sans avoir besoin d'écrire des moteurs de rendu personnalisés ou d'autres codes plus détaillés. Vous pouvez également intégrer django-distill
aux sites dynamiques existants et générer simplement des pages statiques pour une petite sous-section de pages plutôt que pour l'ensemble du site.
Pour les fichiers statiques sur les CDN, vous pouvez utiliser la bibliothèque « cache buster » suivante pour permettre des mises à jour rapides des médias statiques lors de l'envoi de modifications :
? meeb/django-cachekiller
Il existe un exemple de site complet qui crée un blog statique et utilise django-distill
avec django-cachekiller
via un déploiement continu sur Netlify disponible ici :
? meeb/django-distill-exemple
Installer à partir de pip :
$ pip install django-distill
Ajoutez django_distill
à votre INSTALLED_APPS
dans votre settings.py
:
INSTALLED_APPS = [
# ... other apps here ...
'django_distill' ,
]
C'est ça.
django-distill
génère des pages statiques et donc seules les vues autorisant les requêtes GET
renvoyant un code d'état HTTP 200
sont prises en charge.
Il est supposé que vous utilisez des paramètres URI tels que /blog/123-abc
et non des paramètres de chaîne de requête tels que /blog?post_id=123&title=abc
. Les paramètres de chaîne de requête n'ont pas de sens pour la génération de pages statiques pour des raisons évidentes.
Les fichiers multimédias statiques tels que les images et les feuilles de style sont copiés à partir de votre répertoire multimédia statique défini dans STATIC_ROOT
. Cela signifie que vous souhaiterez exécuter ./manage.py collectstatic
avant d'exécuter ./manage.py distill-local
si vous avez apporté des modifications au support statique. django-distill
n'enchaîne pas cette requête de par sa conception, mais vous pouvez l'activer avec l'argument --collectstatic
.
En supposant que vous ayez un projet Django existant, modifiez un urls.py
pour inclure la fonction distill_path
qui remplace la fonction path
standard de Django et prend en charge les nouveaux arguments de mot-clé distill_func
et distill_file
.
L'argument distill_func
doit être fourni avec une fonction ou une classe appelable qui renvoie un itérable ou None
.
L'argument distill_file
est entièrement facultatif et vous permet de remplacer l'URL qui serait autrement générée à partir de l'inverse de l'expression régulière d'URL. Cela vous permet de renommer des URL comme /example
en n'importe quel autre nom comme example.html
. Depuis la version 0.8, tous les URI se terminant par une barre oblique /
sont automatiquement modifiés pour se terminer par /index.html
. Vous pouvez utiliser des paramètres de chaîne de format dans le distill_file
pour personnaliser le nom du fichier, les valeurs d'argument de l'URL seront remplacées, par exemple {}
pour les arguments de position ou {param_name}
pour les arguments nommés.
Un exemple de configuration de distillation pour une application de blog théorique serait :
# Replaces the standard django.conf.path, identical syntax
from django_distill import distill_path
# Views and models from a theoretical blogging app
from blog . views import PostIndex , PostView , PostYear
from blog . models import Post
def get_index ():
# The index URI path, '', contains no parameters, named or otherwise.
# You can simply just return nothing here.
return None
def get_all_blogposts ():
# This function needs to return an iterable of dictionaries. Dictionaries
# are required as the URL this distill function is for has named parameters.
# You can just export a small subset of values here if you wish to
# limit what pages will be generated.
for post in Post . objects . all ():
yield { 'blog_id' : post . id , 'blog_title' : post . title }
def get_years ():
# You can also just return an iterable containing static strings if the
# URL only has one argument and you are using positional URL parameters:
return ( 2014 , 2015 )
# This is really just shorthand for ((2014,), (2015,))
urlpatterns = (
# e.g. / the blog index
distill_path ( '' ,
PostIndex . as_view (),
name = 'blog-index' ,
# Note that for paths which have no paramters
# distill_func is optional
distill_func = get_index ,
# '' is not a valid file name! override it to index.html
distill_file = 'index.html' ),
# e.g. /post/123-some-post-title using named parameters
distill_path ( 'post/<int:blog_id>-<slug:blog_title>.html' ,
PostView . as_view (),
name = 'blog-post' ,
distill_func = get_all_blogposts ),
# e.g. /posts-by-year/2015 using positional parameters
# url ends in / so file path will have /index.html appended
distill_path ( 'posts-by-year/<int:year>/' ,
PostYear . as_view (),
name = 'blog-year' ,
distill_func = get_years ),
)
Votre site fonctionnera toujours de la même manière avec les modifications ci-dessus. En interne, les paramètres distill_func
et distill_file
sont supprimés et l'URL est renvoyée à Django pour un traitement normal. Cela n'a aucun impact sur les performances d'exécution, car cela ne se produit qu'une seule fois au démarrage de l'application.
Si votre chemin n'a pas de paramètres URI, tels que /
ou /some-static-url
vous n'êtes pas obligé de spécifier le paramètre distill_func
si vous ne le souhaitez pas. Quant aux chemins sans paramètres, distill_func
renvoie toujours None
, ceci est défini comme comportement par défaut pour distill_func
s.
Vous pouvez également utiliser la fonction distill_re_path
, qui remplace la fonction par défaut django.urls.re_path
. Son utilisation est identique à celle ci-dessus :
from django_distill import distill_re_path
urlpatterns = (
distill_re_path ( r'some/regex'
SomeOtherView . as_view (),
name = 'url-other-view' ,
distill_func = some_other_func ),
)
Si vous utilisez une ancienne version de Django de la série 1.x, vous pouvez utiliser la fonction distill_url
à la place qui remplace les fonctions django.conf.urls.url
ou django.urls.url
. Son utilisation est identique à celle ci-dessus :
from django_distill import distill_url
urlpatterns = (
distill_url ( r'some/regex'
SomeView . as_view (),
name = 'url-view' ,
distill_func = some_func ),
)
Vous pouvez également utiliser le formatage de chaîne Python standard dans distill_file
pour vous permettre de modifier le chemin du fichier de sortie d'un fichier si vous le souhaitez. Notez que cela ne met pas à jour l'URL utilisée par Django, donc si vous l'utilisez, assurez-vous que votre modèle path
correspond au modèle distill_file
, sinon vos liens pourraient ne pas fonctionner dans Django. Un exemple :
# Override file path with parameters. Values are taken from the URL pattern
urlpatterns = (
distill_path ( 'post/<int:blog_id>-<slug:blog_title>.html' ,
PostView . as_view (),
name = 'blog-post' ,
distill_func = get_all_blogposts ,
distill_file = "post/{blog_id}-{blog_title}.html"
)
Toutes les vues rendues par django-distill
dans des pages statiques doivent renvoyer un code d'état HTTP 200. Si, pour une raison quelconque, vous devez afficher une vue qui ne renvoie pas de code d'état HTTP 200, par exemple, vous souhaitez également générer statiquement une page 404 comportant une vue qui renvoie (correctement) un code d'état HTTP 404, vous pouvez utiliser les codes distill_status_codes
argument facultatif à une vue. Par exemple:
from django_distill import distill_url
urlpatterns = (
distill_url ( r'some/regex'
SomeView . as_view (),
name = 'url-view' ,
distill_status_codes = ( 200 , 404 ),
distill_func = some_func ),
)
L'argument facultatif distill_status_codes
accepte un tuple de codes d'état sous forme d'entiers qui sont autorisés à renvoyer la vue sans générer d'erreur. Par défaut, cette valeur est définie sur (200,)
mais vous pouvez la remplacer si vous en avez besoin pour votre site.
django-distill
reflétera la version prise en charge par votre version installée de Django, donc à un moment donné, la fonction distill_url
cessera de fonctionner à l'avenir lorsque Django 2.x lui-même dépréciera les fonctions django.conf.urls.url
et django.urls.url
. Vous pouvez utiliser distill_re_path
comme remplacement immédiat. Il est conseillé d'utiliser distill_path
ou distill_re_path
si vous créez un nouveau site maintenant.
L'internationalisation n'est prise en charge que pour les URL, le contenu de la page ne peut pas être traduit dynamiquement. Par défaut, votre site sera généré en utilisant la valeur LANGUAGE_CODE
dans votre settings.py
. Si vous définissez également settings.USE_I18N
sur True
, puis définissez d'autres codes de langue dans votre valeur settings.DISTILL_LANGUAGES
et enregistrez les URL avec i18n_patterns(...)
, votre site sera généré dans plusieurs langues. Cela suppose que votre site multilingue fonctionne comme prévu avant d'ajouter django-distill
.
Par exemple, si vous définissez settings.LANGUAGE_CODE = 'en'
votre site sera généré dans une seule langue.
Si vous avez plutôt quelque chose comme ceci dans votre settings.py
:
USE_I18N = True
DISTILL_LANGUAGES = [
'en' ,
'fr' ,
'de' ,
]
Tout en utilisant également i18n_patterns
dans votre urls.py
comme ceci :
from django . conf . urls . i18n import i18n_patterns
from django_distill import distill_path
urlpatterns = i18n_patterns (
distill_path ( 'some-file.html' ,
SomeView . as_view (),
name = 'i18n-view' ,
distill_func = some_func
)
)
Ensuite, vos vues seront générées sous la forme /en/some-file.html
, /fr/some-file.html
et /de/some-file.html
. Ces URL devraient déjà fonctionner (et être traduites) par votre site. django-distill
ne fait aucune magie de traduction, il appelle simplement les URL avec le préfixe du code de langue.
Remarque Bien que la méthode suggérée par défaut consiste à utiliser settings.DISTILL_LANGUAGES
pour garder les choses séparées, django-distill
vérifiera également settings.LANGUAGES
pour les codes de langue.
Vous devrez peut-être générer une liste de toutes les URL enregistrées avec django-distill
. Par exemple, vous disposez d'un blog généré statiquement avec quelques centaines de pages et vous souhaitez répertorier facilement toutes les URL dans un sitemap.xml
ou une autre liste similaire de toutes les URL. Vous pouvez envelopper la vue de votre plan de site dans distill_path
puis répliquer toute votre logique de génération d'URL en important vos vues distill_func
s à partir de votre urls.py
et en les générant toutes manuellement, mais étant donné que c'est assez compliqué, il existe un assistant intégré pour générer tous vos Des URL qui seront distillées pour vous.
from django_distill import distilled_urls
for uri , file_name in distilled_urls ():
# URI is the generated, complete URI for the page
print ( uri ) # for example: /blog/my-post-123/
# file_name is the actual file name on disk, this may be None or a string
print ( file_name ) # for example: /blog/my-post-123/index.html
Notez que distilled_urls()
ne renverra les URL qu'une fois que toutes vos URL dans urls.py
auront été chargées avec distill_path(...)
.
distill-local
Une fois que vous avez encapsulé les URL que vous souhaitez générer de manière statique, vous pouvez maintenant générer un site statique fonctionnel complet avec :
$ ./manage.py distill-local [optional /path/to/export/directory]
Sous le capot, cela itère simplement toutes les URL enregistrées avec distill_url
et génère les pages pour elles en utilisant des parties du framework de test Django pour usurper les requêtes. Une fois les pages du site rendues, les fichiers de STATIC_ROOT
sont copiés. Les fichiers existants portant le même nom sont remplacés dans le répertoire cible et les fichiers orphelins sont supprimés.
distill-local
prend en charge les arguments facultatifs suivants :
--collectstatic
: Exécutez automatiquement collectstatic
sur votre site avant le rendu, il s'agit simplement d'un raccourci pour vous éviter de taper une commande supplémentaire.
--quiet
: désactive toutes les sorties autres que les questions de confirmation.
--force
: supposez « oui » à toutes les questions de confirmation.
--exclude-staticfiles
: Ne copiez aucun fichier statique, affichez uniquement la sortie des vues Django.
--parallel-render [number of threads]
: Rendre les fichiers en parallèle sur plusieurs threads, cela peut accélérer le rendu. La valeur par défaut est 1
thread.
--generate-redirects
: Tentative de génération de redirections statiques stockées dans l'application django.contrib.redirects
. Si vous avez une redirection de /old/
vers /new/
l'utilisation de cet indicateur créera une redirection de style HTML statique <meta http-equiv="refresh" content="...">
de /old/index.html
vers /new/
.
Remarque Si l'une de vos vues contient une erreur Python, le rendu échouera, puis la trace de la pile sera imprimée sur le terminal et la commande de rendu se terminera avec un code d'état de 1.
distill-publish
$ ./manage.py distill-publish [optional destination here]
Si vous avez configuré au moins une destination de publication (voir ci-dessous), vous pouvez utiliser la commande distill-publish
pour publier le site sur un emplacement distant.
Cela effectuera une synchronisation complète, supprimant tous les fichiers distants qui ne sont plus présents dans le site statique généré et téléchargeant tous les fichiers nouveaux ou modifiés. Le site sera intégré localement dans un répertoire temporaire lors de la publication, qui sera supprimé une fois le site publié. Chaque fichier sera vérifié qu'il a été correctement publié en le demandant via la PUBLIC_URL
.
distill-publish
prend en charge les arguments facultatifs suivants :
--collectstatic
: Exécutez automatiquement collectstatic
sur votre site avant le rendu, il s'agit simplement d'un raccourci pour vous éviter de taper une commande supplémentaire.
--quiet
: désactive toutes les sorties autres que les questions de confirmation.
--force
: supposez « oui » à toutes les questions de confirmation.
--exclude-staticfiles
: Ne copiez aucun fichier statique, affichez uniquement la sortie des vues Django.
--skip-verify
: Ne teste pas si les fichiers sont correctement téléchargés sur le serveur.
--ignore-remote-content
: Ne récupère pas la liste des fichiers distants. Cela signifie que tous les fichiers seront téléchargés et qu'aucun fichier distant existant ne sera supprimé. Cela peut être utile si vous avez beaucoup de fichiers sur le serveur distant et que vous savez que vous souhaitez mettre à jour la plupart d'entre eux et que vous ne vous souciez pas de savoir si d'anciens fichiers restent sur le serveur.
--parallel-publish [number of threads]
: Publiez les fichiers en parallèle sur plusieurs threads, cela peut accélérer la publication. La valeur par défaut est 1
thread.
--parallel-render [number of threads]
: Rendre les fichiers en parallèle sur plusieurs threads, cela peut accélérer le rendu. La valeur par défaut est 1
thread.
--generate-redirects
: Tentative de génération de redirections statiques stockées dans l'application django.contrib.redirects
. Si vous avez une redirection de /old/
vers /new/
l'utilisation de cet indicateur créera une redirection de style HTML statique <meta http-equiv="refresh" content="...">
de /old/index.html
vers /new/
.
Notez que cela signifie que si vous utilisez --force
et --quiet
, tous les fichiers ne faisant pas partie de l'exportation du site seront supprimés du répertoire de sortie sans aucune confirmation.
Remarque Si l'une de vos vues contient une erreur Python, le rendu échouera, puis la trace de la pile sera imprimée sur le terminal et la commande de rendu se terminera avec un code d'état de 1.
distill-test-publish
$ ./manage.py distill-test-publish [optional destination here]
Cela se connectera à votre cible de publication, s'authentifiera auprès de celle-ci, téléchargera un fichier nommé de manière aléatoire, vérifiera qu'il existe sur PUBLIC_URL
, puis le supprimera à nouveau. Utilisez-le pour vérifier que vos paramètres de publication sont corrects.
distill-test-publish
n’a aucun argument.
Vous pouvez définir les variables settings.py
facultatives suivantes :
DISTILL_DIR : chaîne, répertoire par défaut vers lequel exporter :
DISTILL_DIR = '/path/to/export/directory'
DISTILL_PUBLISH : le dictionnaire, comme settings.DATABASES
de Django, prend en charge default
:
DISTILL_PUBLISH = {
'default' : {
... options ...
},
'some-other-target' : {
... options ...
},
}
DISTILL_SKIP_ADMIN_DIRS : bool, la valeur par défaut est True
DISTILL_SKIP_ADMIN_DIRS = True
Définissez DISTILL_SKIP_ADMIN_DIRS
sur False
si vous souhaitez que django-distill
copie également les fichiers statiques dans le répertoire static/admin
. Habituellement, ceux-ci ne sont ni requis ni souhaités pour les sites générés de manière statique. Le comportement par défaut consiste à ignorer les fichiers d'administration statiques.
DISTILL_SKIP_STATICFILES_DIRS : liste, par défaut : []
DISTILL_SKIP_STATICFILES_DIRS = [ 'some_dir' ]
Définissez DISTILL_SKIP_STATICFILES_DIRS
sur une liste de noms de répertoires pour lesquels vous souhaitez que django-distill
ignore les répertoires de votre répertoire static/
défini. Vous pouvez l'utiliser pour ignorer la copie de répertoires contenant des fichiers provenant d'applications que vous n'utilisez pas et qui sont regroupés dans votre répertoire static/
par collect-static
. Par exemple, si vous définissez DISTILL_SKIP_STATICFILES_DIRS
sur ['some_dir']
le répertoire de fichiers statiques static/some_dir
sera ignoré.
DISTILL_LANGUAGES : liste, par défaut []
DISTILL_LANGUAGES = [
'en' ,
'fr' ,
'de' ,
]
Définissez DISTILL_LANGUAGES
sur une liste de codes de langue avec lesquels tenter de restituer les URL. Voir la section « Internationalisation » pour plus de détails.
Si vous utilisez un environnement de développement local prenant en charge HTTPS, vous devrez peut-être ajouter SECURE_SSL_REDIRECT = False
à votre settings.py
pour éviter qu'une CommandError
ne soit générée lorsqu'une requête renvoie une redirection 301 au lieu du code de réponse HTTP/200 attendu.
Depuis la version 3.0.0
django-distill
, vous pouvez utiliser la méthode django_distill.renderer.render_single_file
pour écrire un seul fichier sur le disque à l'aide de django_distill
. Ceci est utile pour écrire des fichiers uniques sur le disque, par exemple, vous avez un site Django qui contient des fichiers statiques dans un répertoire écrit par django_distill
mais le reste du site est un site Django dynamique normal. Vous pouvez mettre à jour un fichier HTML statique chaque fois qu'une instance de modèle est enregistrée. Vous pouvez utiliser l'écriture d'un seul fichier avec des signaux pour y parvenir. Par exemple:
# in models.py
from django . db . models . signals import post_save
from django . dispatch import receiver
from django_distill . renderer import render_single_file
@ receiver ( post_save , sender = SomeBlogPostModel )
def write_blog_post_static_file_post_save ( sender , ** kwargs ):
render_single_file (
'/path/to/output/directory' ,
'blog-post-view-name' ,
blog_id = sender . pk ,
blog_slug = sender . slug
)
La syntaxe de render_single_file
est similaire à celle de Django url.reverse
. L'interface d'utilisation complète est :
render_single_file (
'/path/to/output/directory' ,
'view-name-set-in-urls-py' ,
* view_args ,
** view_kwargs
)
Par exemple, si l'URL d'un article de blog était définie comme :
# in urls.py
distill_path ( 'post/<int:blog_id>_<slug:blog_slug>.html' ,
PostView . as_view (),
name = 'blog-post' ,
distill_func = get_all_blogposts ),
Votre utilisation serait :
render_single_file (
'/path/to/output/directory' ,
'blog-post' ,
blog_id = 123 ,
blog_slug = 'blog-title-slug' ,
)
qui écrirait le contenu de /post/123_blog-title-slug.html
dans /path/to/output/directory
en tant que fichier /path/to/output/directory/post/123_blog-title-slug.html
. Notez que tous les sous-répertoires requis ( /path/to/output/directory/post
dans cet exemple) seront automatiquement créés s'ils n'existent pas déjà. Toutes les règles django-distill
s'appliquent, telles que les URL se terminant par /
seront enregistrées sous /index.html
pour avoir un sens pour un fichier physique sur le disque.
Notez également que render_single_file
ne peut être importé et utilisé que dans un projet Django initialisé.
Vous pouvez publier automatiquement des sites sur diverses cibles distantes prises en charge via des backends, tout comme vous pouvez utiliser MySQL, SQLite, PostgreSQL, etc. avec Django en modifiant le moteur de base de données backend. Actuellement, les moteurs pris en charge par django-distill
sont :
django_distill.backends.amazon_s3 : publication dans un compartiment Amazon S3. Nécessite la bibliothèque Python boto3
( $ pip install django-distill[amazon]
). Le bucket doit déjà exister (utilisez le panneau de configuration AWS). Possibilités :
'some-s3-container' : {
'ENGINE' : 'django_distill.backends.amazon_s3' ,
'PUBLIC_URL' : 'http://.../' ,
'ACCESS_KEY_ID' : '...' ,
'SECRET_ACCESS_KEY' : '...' ,
'BUCKET' : '...' ,
'ENDPOINT_URL' : 'https://.../' , # Optional, set to use a different S3 endpoint
'DEFAULT_CONTENT_TYPE' : 'application/octet-stream' , # Optional
},
django_distill.backends.google_storage : publiez dans un bucket Google Cloud Storage. Nécessite les bibliothèques Python google-api-python-client
et google-cloud-storage
( $ pip install django-distill[google]
). Le bucket doit déjà exister et être configuré pour héberger un site Web statique public (utilisez le panneau de configuration Google Cloud). Possibilités :
'some-google-storage-bucket' : {
'ENGINE' : 'django_distill.backends.google_storage' ,
'PUBLIC_URL' : 'https://storage.googleapis.com/[bucket.name.here]/' ,
'BUCKET' : '[bucket.name.here]' ,
'JSON_CREDENTIALS' : '/path/to/some/credentials.json' ,
},
Notez que JSON_CREDENTIALS
est facultatif ; si ce n'est pas précisé, les bibliothèques google essaieront d'autres méthodes d'authentification, dans l'ordre de recherche décrit ici : https://cloud.google.com/docs/authentication/application-default-credentials (par exemple la variable d'environnement GOOGLE_APPLICATION_CREDENTIALS
, service attaché compte, etc.).
django_distill.backends.microsoft_azure_storage : publication dans un conteneur Microsoft Azure Blob Storage. Nécessite la bibliothèque Python azure-storage-blob
( $ pip install django-distill[microsoft]
). Le compte de stockage doit déjà exister et être configuré pour héberger un site Web statique public (utilisez le panneau de configuration Microsoft Azure). Possibilités :
'some-microsoft-storage-account' : {
'ENGINE' : 'django_distill.backends.microsoft_azure_storage' ,
'PUBLIC_URL' : 'https://[storage-account-name]...windows.net/' ,
'CONNECTION_STRING' : '...' ,
},
Notez que chaque compte de stockage Azure prend en charge un site Web statique utilisant le conteneur magique $web
, où django-distill
tentera de publier votre site.
Il existe une suite de tests minimale, vous pouvez l'exécuter en clonant ce référentiel, en installant les dépendances requises dans requirements.txt
puis en exécutant :
# ./run-tests.py
Toutes les demandes de tirage, problèmes et commentaires correctement formatés et sensés sont les bienvenus.