django-distill

repository·master·Indexed 19 days ago

https://github.com/meeb/django-distill

A minimal configuration static site generator and publisher for Django that allows developers to export existing Django sites as fully functional static sites for Jamstack-style architectures. It provides tools like distill_path, distill-local, and distill-publish to render views to HTML and deploy them to targets including Amazon S3, Google Cloud Storage, and Microsoft Azure Blob Storage.

Tokens
5.5K
Snippets
21
Records
23
Agent score
67%

What's inside django-distill

  1. Enable Internationalization (i18n) for static generation

    master

    To generate a multi-language static site, configure your settings.py and use i18n_patterns in your urls.py.

    1. In settings.py: Set USE_I18N = True and define DISTILL_LANGUAGES (or use the standard LANGUAGES setting).
    2. In urls.py: Wrap your distill_path definitions with i18n_patterns.

    django-distill will then generate URLs with the appropriate language prefixes (e.g., /en/some-file.html, /fr/some-file.html).

    # settings.py
    USE_I18N = True
    DISTILL_LANGUAGES = ['en', 'fr', 'de']
    
    # urls.py
    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)
    )
  2. Configure django-distill settings

    master

    You can customize django-distill behavior in your settings.py using the following variables:

    • DISTILL_DIR (string): The directory where the site is exported. Defaults to a default path.
    • DISTILL_PUBLISH (dict): Configuration for publishing destinations, structured like Django's DATABASES setting. Supports a default key.
    • DISTILL_SKIP_ADMIN_DIRS (bool): If True (default), static files in static/admin are skipped. Set to False to include them.
    • DISTILL_SKIP_STATICFILES_DIRS (list): A list of directory names within your static/ directory to ignore (e.g., ['some_dir'] ignores static/some_dir).
    • DISTILL_LANGUAGES (list): A list of language codes (e.g., ['en', 'fr']) to attempt rendering URLs for.
    DISTILL_DIR = '/path/to/export/directory'
    
    DISTILL_PUBLISH = {
        'default': {
            'ENGINE': '...',
            # ... other options ...
        },
    }
    
    DISTILL_SKIP_ADMIN_DIRS = True
    DISTILL_SKIP_STATICFILES_DIRS = ['some_dir']
    DISTILL_LANGUAGES = ['en', 'fr', 'de']
  3. Use `distill_path` to define static URLs

    master

    To enable static site generation for specific views, replace Django's standard path function with distill_path in your urls.py.

    distill_path supports two key keyword arguments:

    • distill_func: A callable (function or class) that returns an iterable of data used to generate the pages.
      • For paths with named parameters, it must return an iterable of dictionaries where keys match the URL parameter names.
      • For paths with positional parameters, it can return an iterable of values (e.g., strings or integers).
      • For paths with no parameters, it can return None or be omitted (defaults to None).
    • distill_file: (Optional) Overrides the filename generated from the URL.
      • Supports Python string formatting (e.g., {param_name} or {}).
      • URIs ending in / are automatically modified to end in /index.html unless overridden.

    Note: distill_path has identical syntax to Django's path and has no runtime performance impact on your live Django application.

    from django_distill import distill_path
    from blog.views import PostView
    from blog.models import Post
    
    def get_all_blogposts():
        # Returns iterable of dicts for named parameters
        for post in Post.objects.all():
            yield {'blog_id': post.id, 'blog_title': post.title}
    
    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"),
    ]
  4. Generate a list of all distilled URLs

    master

    If you need to programmatically list all URLs that are being statically generated (for example, to build a sitemap.xml), use the distilled_urls() helper.

    It returns an iterable of tuples containing the complete URI and the corresponding file name on disk. Note that this helper only works after all URLs in urls.py have been loaded.

    from django_distill import distilled_urls
    
    for uri, file_name in distilled_urls():
        # uri: e.g., /blog/my-post-123/
        # file_name: e.g., /blog/my-post-123/index.html
        print(uri, file_name)
  5. Configure non-standard HTTP status codes for views

    master

    By default, all views rendered by django-distill must return an HTTP 200 status code. If you need to statically generate pages that return other codes (like a 404 page), use the distill_status_codes argument in your URL definition.

    Pass a tuple of integers representing the permitted status codes.

    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),
    ]
  6. Use `distill_re_path` and `distill_url` for regex-based URLs

    master

    If your project uses regular expression-based URL routing, use the following drop-in replacements for Django's regex functions:

    • For modern Django: Use distill_re_path to replace django.urls.re_path.
    • For Django 1.x: Use distill_url to replace django.conf.urls.url or django.urls.url.

    Both functions support the same distill_func and distill_file arguments as distill_path.

    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),
    ]
  7. Render a single file using render_single_file

    master

    Since version 3.0.0, you can use django_distill.renderer.render_single_file to write a specific view's output to disk. This is ideal for hybrid sites where only certain parts (like blog posts) are statically generated, often triggered via Django signals (e.g., post_save).

    The syntax follows Django's reverse() pattern. It automatically creates any required sub-directories. URLs ending in / are saved as index.html to ensure compatibility with physical file systems.

    from django_distill.renderer import render_single_file
    
    # Example: Writing a blog post file when the model is saved
    @receiver(post_save, sender=SomeBlogPostModel)
    def write_blog_post_static_file_post_save(sender, **kwargs):
        render_single_file(
            '/path/to/output/directory',
            'blog-post',
            blog_id=sender.pk,
            blog_slug=sender.slug
        )
  8. Configure DISTILL_PUBLISH settings

    master

    The distill-publish command relies on a DISTILL_PUBLISH dictionary in your Django settings. Each entry in this dictionary represents a target destination. At a minimum, each target must specify an ENGINE key which determines the publishing backend used.

    # Example configuration in settings.py
    DISTILL_PUBLISH = {
        'production': {
            'ENGINE': 'your_backend_engine_name',
            # ... other backend-specific configuration keys
        },
        'default': {
            'ENGINE': 'some_other_engine',
        }
    }
  9. Configure Google Cloud Storage publishing target

    master

    To publish to Google Cloud Storage, use the django_distill.backends.google_storage engine. This requires google-api-python-client and google-cloud-storage (pip install django-distill[google]). The bucket must be configured to host a public static website.

    DISTILL_PUBLISH = {
        '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',  # Optional
        },
    }
  10. Configure Amazon S3 publishing target

    master

    To publish to Amazon S3, use the django_distill.backends.amazon_s3 engine. This requires the boto3 library (pip install django-distill[amazon]). The bucket must already exist.

    DISTILL_PUBLISH = {
        'some-s3-container': {
            'ENGINE': 'django_distill.backends.amazon_s3',
            'PUBLIC_URL': 'http://.../',
            'ACCESS_KEY_ID': '...',
            'SECRET_ACCESS_KEY': '...',
            'BUCKET': '...',
            'ENDPOINT_URL': 'https://.../',  # Optional
            'DEFAULT_CONTENT_TYPE': 'application/octet-stream',  # Optional
        },
    }
  11. Understand django-distill limitations

    master

    When using django-distill to generate a static site, be aware of the following constraints:

    • HTTP Methods: Only views that support GET requests and return an HTTP 200 status code are supported.
    • URL Parameters: The tool assumes you use URI parameters (e.g., /blog/123-abc) rather than querystring parameters (e.g., /blog?post_id=123). Querystring parameters are not supported for static page generation.
    • Static Media: Static files (images, stylesheets) are copied from the directory defined in STATIC_ROOT. You must ensure static files are collected before distillation. While django-distill does not chain this by default, you can use the --collectstatic argument to automate it.