Integration
This page covers how to integrate django-distill into your Django project once you have installed it.
Limitations!
django-distill generates static pages and therefore only views which allow
GET
requests that return an HTTP 200
status code are supported.
It is assumed you are using URI parameters such as /blog/123-abc
and not
querystring parameters such as /blog?post_id=123&title=abc
. Querystring
parameters do not make sense for static page generation for obvious reasons.
Additionally with one-off static pages dynamic internationalisation won't work
so all files are generated using the LANGUAGE_CODE
value in your
settings.py
.
Step 1: wrap your URLs
Open the urls.py
for any Django project or Django app. Add in an import for
django_distill.distill_path
which replaces Django's django.urls.path
. The
syntax for django_distill.distill_path
is identical to django.urls.path
but supports two new keyword arguments, distill_func
and distill_file
(and an optional third argument that's usually not required,
distill_status_codes
).
The distill_func
argument should be provided with a function or callable
class that returns an iterable or None. The distill_file
argument allows you
to override the URL that would otherwise be generated from the reverse of the
URL path or regex which allows you to rename URLs like /example
to any other
name like example.html
which may be more useful for static pages. Depending
on your URLs, both arguments can be optional. As of django-distill v0.8 any
URIs ending in a slash /
are automatically modified to end in /index.html
.
Usage of distill_func
is covered in step 2.
An example urls.py
for some static pages for a Django static site would be:
# Replaces the standard django.urls.path, identical syntax
from django_distill import distill_path
# Import some views from your Django app
from app.views import IndexView, PageView
urlpatterns = [
# The index URL on /, render this as 'index.html'
distill_path('',
IndexView.as_view(),
name='index',
# / is not a valid file name! override it to index.html
distill_file='index.html'),
# A single static page, render this as 'page.html'
distill_path('page.html',
PageView.as_view(),
name='page')
]
Your site will still function identically with the above changes. Internally
the distill_func
and distill_file
parameters are removed and the URL is
passed back to Django for normal processing. This has no runtime performance
impact as this happens only once upon starting the application.
While most static site projects return HTML from views you can return any data such as JSON or binary data and django-distill will handle it properly.
Step 2: URLs with parameters
If your URLs have parameters, such as you have a blog with URLs in the format
of /post/[slug-of-post]_[date].html
(for example
/post/my-blog-post_2010-10-10.html
) you need to tell django-distill which
pages to render from URL parameters. This is what the optional distill_func
argument is for and a distill_func
argument is only required when your URL
has parameters. If required distill_func
should point to a function which
returns an iterable (e.g. a list, tuple or yield) which matches the format of
your URL path.
An example urls.py
setup for a theoretical blogging app would be:
# 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 (or not specify a
# distill_func at all)
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 used by
# 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():
# Note 'blog_id' and 'blog_title' match the URL parameter names
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 = [
# Index URL on /, render this as 'index.html'
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'),
# Blog post page, for example /post/123_my-blog-post.html
# using named parameters
distill_path('post/<int:blog_id>_<slug:blog_title>.html',
PostView.as_view(),
name='blog-post',
distill_func=get_all_blogposts),
# Blog posts for a year, for example /posts-by-year/2014.html
# using positional parameters
distill_path('posts-by-year/<int:year>.html',
PostYear.as_view(),
name='blog-year',
distill_func=get_years)
]
If you only want to render some static pages for a view you can just alter the
URL parameters returned by your distill_func
function for a URL. For example
using the above urls.py
if you only wanted to render blog post pages written
after 2020 you would modify the Django query in your distill_func
:
from datetime import datetime
posts_after = datetime(year=2020, month=1, day=1)
def get_all_blogposts():
# This function now returns an iterable of dicts but filtered to just
# return URL paramters for 2020 or later (assuming the Post model has
# a datetime field called post_date)
for post in Post.objects.filter(post_date__gte=posts_after):
yield {'blog_id': post.id, 'blog_title': post.title}
The functions specified by any distill_func
kwargs are only ever executed
when you run either python manage.py distill-local
or
python manage.py distill-publish
on the command line. They have no other
impact on your Django project which will continue to operate as normal. These
commands are covered in the deployment documentation.
Regular expression URLs
If you prefer to use regex based URL patterns in your Django project or you're
working on an older project that uses regex URL patterns you can use
django_distill.distill_re_path
instead of django_distill.distill_path
. This
is identical to Django's django.urls.re_path
. It's usage is identical to
django_distill.distill_path
as detailed above. As an example:
from django_distill import distill_re_path
urlpatterns = (
distill_re_path(r'^posts-by-year\/(?P<year>[0-9]{4})\.html$''
PostYear.as_view(),
name='blog-year',
distill_func=get_years),
)
django-distill will attempt to match the URL format support of the version
if Django you are using, so in very old Django projects using version 1.x you
may be able to import and use django_distill.distill_url
which replaces the
deprectiated django.urls.url
function. While this is tested and supported for
older versions of Django it is discouraged in favour of using distill_path
and distill_re_path
where available.
django-distill will mirror whatever your installed version of Django
supports, therefore the distill_url
function will cease working once your
project is upgraded past Django 2.x which depreciated django.conf.urls.url
and django.urls.url
functions. You can use distill_re_path
as a drop-in
replacement.
Non-200 status codes
All views rendered by django-distill into static pages must return an HTTP
200 status code. If for any reason you need to render a view as static html
which does not return an HTTP 200 status code, for example you also want to
statically generate a 404 page which has a view which (correctly) returns an
HTTP 404 status code but also returns valid content you need to render to a
file you can use the distill_status_codes
optional argument to a view. For
example:
from django_distill import distill_path
from app.views import Error404View
urlpatterns = [
distill_path('error404.html',
Error404View.as_view(),
name='error404',
distill_status_codes=(200, 404))
]
The optional distill_status_codes
argument accepts a tuple of status codes as
integers which are permitted for the view to return without raising an error.
By default this is set to (200,)
but you can override it if you need to for
your project.
Now you have wrapped your URLs with django-distill you can begin with deployment of the output of your Django application as a static site.