summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCoprDistGit <infra@openeuler.org>2023-05-31 04:59:33 +0000
committerCoprDistGit <infra@openeuler.org>2023-05-31 04:59:33 +0000
commit2dad512441607ed820b2b4699fc8596208bad82a (patch)
treeafe7d510555864a10534ad169c00de3db80aff61
parent9c6298370660c29b955585387cea874554c3dd0a (diff)
automatic import of python-django-distill
-rw-r--r--.gitignore1
-rw-r--r--python-django-distill.spec1650
-rw-r--r--sources1
3 files changed, 1652 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index e69de29..153e06a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -0,0 +1 @@
+/django-distill-3.1.3.tar.gz
diff --git a/python-django-distill.spec b/python-django-distill.spec
new file mode 100644
index 0000000..9047981
--- /dev/null
+++ b/python-django-distill.spec
@@ -0,0 +1,1650 @@
+%global _empty_manifest_terminate_build 0
+Name: python-django-distill
+Version: 3.1.3
+Release: 1
+Summary: Static site renderer and publisher for Django.
+License: MIT
+URL: https://github.com/meeb/django-distill
+Source0: https://mirrors.nju.edu.cn/pypi/web/packages/d1/24/f2ae21a144b91eea0bf1083e267e819ca03c52cf1b477518f9775fd51787/django-distill-3.1.3.tar.gz
+BuildArch: noarch
+
+
+%description
+# django-distill
+
+`django-distill` now has a website. Read more at:
+
+## :link: https://django-distill.com/
+
+`django-distill` is a minimal configuration static site generator and publisher
+for Django. Most Django versions are supported, however up to date versions are
+advised including the Django 3.x releases. `django-distill` as of the 1.7 release
+only supports Python 3. Python 2 support has been dropped. If you require Python 2
+support please pin `django-distill` to version 1.6 in your requirements.txt or
+Pipfile. Python 3.6 or above is advised.
+
+`django-distill` extends existing Django sites with the ability to export
+fully functional static sites. It is suitable for sites such as blogs that have
+a mostly static front end but you still want to use a CMS to manage the
+content.
+
+`django-distill` iterates over URLs in your Django project using easy to write
+iterable functions to yield the parameters for whatever pages you want to save
+as static HTML. These static files can be automatically uploaded to a bucket-style
+remote container such as Amazon S3, Googe Cloud Files, Microsoft Azure Storage,
+or, written to a local directory as a fully working local static version of
+your project. The site generation, or distillation process, can be easily
+integrated into CI/CD workflows to auto-deploy static sites on commit.
+`django-distill` can be defined as an extension to Django to make Django
+projects compatible with "Jamstack"-style site architecture.
+
+`django-distill` plugs directly into the existing Django framework without the
+need to write custom renderers or other more verbose code. You can also integrate
+`django-distill` with existing dynamic sites and just generate static pages for
+a small subsection of pages rather than the entire site.
+
+For static files on CDNs you can use the following 'cache buster' library to
+allow for fast static media updates when pushing changes:
+
+[:link: meeb/django-cachekiller](https://github.com/meeb/django-cachekiller)
+
+There is a complete example site that creates a static blog and uses
+`django-distill` with `django-cachekiller` via continuous deployment on Netlify
+available here:
+
+[:link: meeb/django-distill-example](https://github.com/meeb/django-distill-example)
+
+
+# Installation
+
+Install from pip:
+
+```bash
+$ pip install django-distill
+```
+
+Add `django_distill` to your `INSTALLED_APPS` in your `settings.py`:
+
+```python
+INSTALLED_APPS = [
+ # ... other apps here ...
+ 'django_distill',
+]
+```
+
+That's 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`.
+
+Static media files such as images and style sheets are copied from your static
+media directory defined in `STATIC_ROOT`. This means that you will want to run
+`./manage.py collectstatic` **before** you run `./manage.py distill-local`
+if you have made changes to static media. `django-distill` doesn't chain this
+request by design, however you can enable it with the `--collectstatic`
+argument.
+
+
+# Usage
+
+Assuming you have an existing Django project, edit a `urls.py` to include the
+`distill_path` function which replaces Django's standard `path` function and
+supports the new keyword arguments `distill_func` and `distill_file`.
+
+The `distill_func` argument should be provided with a function or callable
+class that returns an iterable or `None`.
+
+The `distill_file` argument is entirely optional and allows you to override the
+URL that would otherwise be generated from the reverse of the URL regex. This
+allows you to rename URLs like `/example` to any other name like
+`example.html`. As of v0.8 any URIs ending in a slash `/` are automatically
+modified to end in `/index.html`. You can use format string parameters in the
+`distill_file` to customise the file name, arg values from the URL will be
+substituted in, for example `{}` for positional args or `{param_name}` for
+named args.
+
+An example distill setup for a theoretical blogging app would be:
+
+```python
+# 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),
+)
+```
+
+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.
+
+If your path has no URI paramters, such as `/` or `/some-static-url` you do
+not have to specify the `distill_func` parameter if you don't want to. As for
+paths with no parameters the `distill_func` always returns `None`, this is set
+as the default behaviour for `distill_func`s.
+
+You can use the `distill_re_path` function as well, which replaces the default
+`django.urls.re_path` function. Its usage is identical to the above:
+
+```python
+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),
+)
+
+```
+
+If you are using an older version of Django in the 1.x series you can use the
+`distill_url` function instead which replaces the `django.conf.urls.url` or
+`django.urls.url` functions. Its usage is identical to the above:
+
+```python
+from django_distill import distill_url
+
+urlpatterns = (
+ distill_url(r'some/regex'
+ SomeView.as_view(),
+ name='url-view',
+ distill_func=some_func),
+)
+```
+
+### Parameters in file names
+
+You can standard Python string formatting in `distill_file` as well to enable
+you to change the output file path for a file if you wish. Note this does not
+update the URL used by Django so if you use this make sure your `path` pattern
+matches the `distill_file` pattern or your links might not work in Django. An
+example:
+
+```python
+# 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"
+)
+```
+
+### Non-standard 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 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 you can use the
+`distill_status_codes` optional argument to a view. For example:
+
+```python
+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),
+)
+```
+
+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 site.
+
+### Tracking Django's URL function support
+
+`django-distill` will mirror whatever your installed version of Django supports,
+therefore at some point the `distill_url` function will cease working in the future
+when Django 2.x itself depreciates the `django.conf.urls.url` and `django.urls.url`
+functions. You can use `distill_re_path` as a drop-in replacement. It is advisable to
+use `distill_path` or `distill_re_path` if you're building a new site now.
+
+
+# The `distill-local` command
+
+Once you have wrapped the URLs you want to generate statically you can now
+generate a complete functioning static site with:
+
+```bash
+$ ./manage.py distill-local [optional /path/to/export/directory]
+```
+
+Under the hood this simply iterates all URLs registered with `distill_url` and
+generates the pages for them using parts of the Django testing framework to
+spoof requests. Once the site pages have been rendered then files from the
+`STATIC_ROOT` are copied over. Existing files with the same name are replaced in
+the target directory and orphan files are deleted.
+
+`distill-local` supports the following optional arguments:
+
+`--collectstatic`: Automatically run `collectstatic` on your site before
+rendering, this is just a shortcut to save you typing an extra command.
+
+`--quiet`: Disable all output other than asking confirmation questions.
+
+`--force`: Assume 'yes' to all confirmation questions.
+
+`--exclude-staticfiles`: Do not copy any static files at all, only render output from
+Django views.
+
+**Note** If any of your views contain a Python error then rendering will fail
+then the stack trace will be printed to the terminal and the rendering command
+will exit with a status code of 1.
+
+
+# The `distill-publish` command
+
+```bash
+$ ./manage.py distill-publish [optional destination here]
+```
+
+If you have configured at least one publishing destination (see below) you can
+use the `distill-publish` command to publish the site to a remote location.
+
+This will perform a full synchronisation, removing any remote files that are no
+longer present in the generated static site and uploading any new or changed
+files. The site will be built into a temporary directory locally first when
+publishing which is deleted once the site has been published. Each file will be
+checked that it has been published correctly by requesting it via the
+`PUBLIC_URL`.
+
+`distill-publish` supports the following optional arguments:
+
+`--collectstatic`: Automatically run `collectstatic` on your site before
+rendering, this is just a shortcut to save you typing an extra command.
+
+`--quiet`: Disable all output other than asking confirmation questions.
+
+`--force`: Assume 'yes' to all confirmation questions.
+
+`--exclude-staticfiles`: Do not copy any static files at all, only render output from
+Django views.
+
+`--skip-verify`: Do not test if files are correctly uploaded on the server.
+
+`--ignore-remote-content`: Do not fetch the list of remote files. It means that all
+files will be uploaded, and no existing remote file will be deleted. This can be
+useful if you have a lot of files on the remote server, and you know that you want
+to update most of them, and you don't care if old files remain on the server.
+
+`--parallel-publish [number of threads]`: Publish files in parallel on multiple
+threads, this can speed up publishing. Defaults to `1` thread.
+
+**Note** that this means if you use `--force` and `--quiet` that the output
+directory will have all files not part of the site export deleted without any
+confirmation.
+
+**Note** If any of your views contain a Python error then rendering will fail
+then the stack trace will be printed to the terminal and the rendering command
+will exit with a status code of 1.
+
+
+# The `distill-test-publish` command
+
+```bash
+$ ./manage.py distill-test-publish [optional destination here]
+```
+
+This will connect to your publishing target, authenticate to it, upload a
+randomly named file, verify it exists on the `PUBLIC_URL` and then delete it
+again. Use this to check your publishing settings are correct.
+
+`distill-test-publish` has no arguments.
+
+
+# Optional configuration settings
+
+You can set the following optional `settings.py` variables:
+
+**DISTILL_DIR**: string, default directory to export to:
+
+```python
+DISTILL_DIR = '/path/to/export/directory'
+```
+
+**DISTILL_PUBLISH**: dictionary, like Django's `settings.DATABASES`, supports
+`default`:
+
+```python
+DISTILL_PUBLISH = {
+ 'default': {
+ ... options ...
+ },
+ 'some-other-target': {
+ ... options ...
+ },
+}
+```
+
+**DISTILL_SKIP_ADMIN_DIRS**: bool, defaults to `True`
+
+```python
+DISTILL_SKIP_ADMIN_DIRS = True
+```
+
+Set `DISTILL_SKIP_ADMIN_DIRS` to `False` if you want `django-distill` to also copy over
+static files in the `static/admin` directory. Usually, these are not required or
+desired for statically generated sites. The default behaviour is to skip static admin
+files.
+
+
+# Writing single files
+
+As of `django-distill` version `3.0.0` you can use the
+`django_distill.renderer.render_single_file` method to write out a single file
+to disk using `django_distill`. This is useful for writing out single files to disk,
+for example, you have a Django site which has some static files in a directory
+written by `django_distill` but the rest of the site is a normal dynamic Django site.
+You can update a static HTML file every time a model instance is saved. You can
+use single file writing with signals to achieve this. For example:
+
+```python
+# 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
+ )
+```
+
+The syntax for `render_single_file` is similar to Django's `url.reverse`. The full
+usage interface is:
+
+```python
+render_single_file(
+ '/path/to/output/directory',
+ 'view-name-set-in-urls-py',
+ *view_args,
+ **view_kwargs
+)
+```
+
+For example, if you had a blog post URL defined as:
+
+```python
+ # in urls.py
+ distill_path('post/<int:blog_id>_<slug:blog_slug>.html',
+ PostView.as_view(),
+ name='blog-post',
+ distill_func=get_all_blogposts),
+```
+
+Your usage would be:
+
+```python
+render_single_file(
+ '/path/to/output/directory',
+ 'blog-post',
+ blog_id=123,
+ blog_slug='blog-title-slug',
+)
+```
+
+which would write out the contents of `/post/123_blog-title-slug.html` into
+`/path/to/output/directory` as the file
+`/path/to/output/directory/post/123_blog-title-slug.html`. Note any required
+sub-directories (`/path/to/output/directory/post` in this example) will be
+automatically created if they don't already exist. All `django-distill` rules
+apply, such as URLs ending in `/` will be saved as `/index.html` to make sense
+for a physical file on disk.
+
+Also note that `render_single_file` can only be imported and used into an
+initialised Django project.
+
+
+# Publishing targets
+
+You can automatically publish sites to various supported remote targets through
+backends just like how you can use MySQL, SQLite, PostgreSQL etc. with
+Django by changing the backend database engine. Currently the engines supported
+by `django-distill` are:
+
+**django_distill.backends.amazon_s3**: Publish to an Amazon S3 bucket. Requires
+ the Python library `boto3` (`$ pip install django-distill[amazon]`). The bucket
+ must already exist (use the AWS control panel). Options:
+
+```python
+'some-s3-container': {
+ 'ENGINE': 'django_distill.backends.amazon_s3',
+ 'PUBLIC_URL': 'http://.../',
+ 'ACCESS_KEY_ID': '...',
+ 'SECRET_ACCESS_KEY': '...',
+ 'BUCKET': '...',
+},
+```
+
+**django_distill.backends.google_storage**: Publish to a Google Cloud Storage
+ bucket. Requires the Python libraries `google-api-python-client` and
+ `google-cloud-storage`
+ (`$ pip install django-distill[google]`). The bucket
+ must already exist and be set up to host a public static website (use the
+ Google Cloud control panel). Options:
+
+```python
+'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',
+},
+```
+
+Note that `JSON_CREDENTIALS` is optional; if it is not specified, the google libraries
+will try other authentication methods, in the search order described here:
+https://cloud.google.com/docs/authentication/application-default-credentials (e.g. the
+`GOOGLE_APPLICATION_CREDENTIALS` environment variable, attached service account, etc).
+
+
+**django_distill.backends.microsoft_azure_storage**: Publish to a Microsoft
+ Azure Blob Storage container. Requires the Python library
+ `azure-storage-blob` (`$ pip install django-distill[microsoft]`). The storage
+ account must already exist and be set up to host a public static website
+ (use the Microsoft Azure control panel). Options:
+
+```python
+'some-microsoft-storage-account': {
+ 'ENGINE': 'django_distill.backends.microsoft_azure_storage',
+ 'PUBLIC_URL': 'https://[storage-account-name]...windows.net/',
+ 'CONNECTION_STRING': '...',
+},
+```
+
+Note that each Azure storage account supports one static website using the
+magic container `$web` which is where `django-distill` will attempt to
+publish your site.
+
+
+# Tests
+
+There is a minimal test suite, you can run it by cloing this repository,
+installing the required dependancies in `requirements.txt` then execuiting:
+
+```bash
+# ./run-tests.py
+```
+
+
+# Contributing
+
+All properly formatted and sensible pull requests, issues and comments are
+welcome.
+
+
+
+
+%package -n python3-django-distill
+Summary: Static site renderer and publisher for Django.
+Provides: python-django-distill
+BuildRequires: python3-devel
+BuildRequires: python3-setuptools
+BuildRequires: python3-pip
+%description -n python3-django-distill
+# django-distill
+
+`django-distill` now has a website. Read more at:
+
+## :link: https://django-distill.com/
+
+`django-distill` is a minimal configuration static site generator and publisher
+for Django. Most Django versions are supported, however up to date versions are
+advised including the Django 3.x releases. `django-distill` as of the 1.7 release
+only supports Python 3. Python 2 support has been dropped. If you require Python 2
+support please pin `django-distill` to version 1.6 in your requirements.txt or
+Pipfile. Python 3.6 or above is advised.
+
+`django-distill` extends existing Django sites with the ability to export
+fully functional static sites. It is suitable for sites such as blogs that have
+a mostly static front end but you still want to use a CMS to manage the
+content.
+
+`django-distill` iterates over URLs in your Django project using easy to write
+iterable functions to yield the parameters for whatever pages you want to save
+as static HTML. These static files can be automatically uploaded to a bucket-style
+remote container such as Amazon S3, Googe Cloud Files, Microsoft Azure Storage,
+or, written to a local directory as a fully working local static version of
+your project. The site generation, or distillation process, can be easily
+integrated into CI/CD workflows to auto-deploy static sites on commit.
+`django-distill` can be defined as an extension to Django to make Django
+projects compatible with "Jamstack"-style site architecture.
+
+`django-distill` plugs directly into the existing Django framework without the
+need to write custom renderers or other more verbose code. You can also integrate
+`django-distill` with existing dynamic sites and just generate static pages for
+a small subsection of pages rather than the entire site.
+
+For static files on CDNs you can use the following 'cache buster' library to
+allow for fast static media updates when pushing changes:
+
+[:link: meeb/django-cachekiller](https://github.com/meeb/django-cachekiller)
+
+There is a complete example site that creates a static blog and uses
+`django-distill` with `django-cachekiller` via continuous deployment on Netlify
+available here:
+
+[:link: meeb/django-distill-example](https://github.com/meeb/django-distill-example)
+
+
+# Installation
+
+Install from pip:
+
+```bash
+$ pip install django-distill
+```
+
+Add `django_distill` to your `INSTALLED_APPS` in your `settings.py`:
+
+```python
+INSTALLED_APPS = [
+ # ... other apps here ...
+ 'django_distill',
+]
+```
+
+That's 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`.
+
+Static media files such as images and style sheets are copied from your static
+media directory defined in `STATIC_ROOT`. This means that you will want to run
+`./manage.py collectstatic` **before** you run `./manage.py distill-local`
+if you have made changes to static media. `django-distill` doesn't chain this
+request by design, however you can enable it with the `--collectstatic`
+argument.
+
+
+# Usage
+
+Assuming you have an existing Django project, edit a `urls.py` to include the
+`distill_path` function which replaces Django's standard `path` function and
+supports the new keyword arguments `distill_func` and `distill_file`.
+
+The `distill_func` argument should be provided with a function or callable
+class that returns an iterable or `None`.
+
+The `distill_file` argument is entirely optional and allows you to override the
+URL that would otherwise be generated from the reverse of the URL regex. This
+allows you to rename URLs like `/example` to any other name like
+`example.html`. As of v0.8 any URIs ending in a slash `/` are automatically
+modified to end in `/index.html`. You can use format string parameters in the
+`distill_file` to customise the file name, arg values from the URL will be
+substituted in, for example `{}` for positional args or `{param_name}` for
+named args.
+
+An example distill setup for a theoretical blogging app would be:
+
+```python
+# 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),
+)
+```
+
+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.
+
+If your path has no URI paramters, such as `/` or `/some-static-url` you do
+not have to specify the `distill_func` parameter if you don't want to. As for
+paths with no parameters the `distill_func` always returns `None`, this is set
+as the default behaviour for `distill_func`s.
+
+You can use the `distill_re_path` function as well, which replaces the default
+`django.urls.re_path` function. Its usage is identical to the above:
+
+```python
+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),
+)
+
+```
+
+If you are using an older version of Django in the 1.x series you can use the
+`distill_url` function instead which replaces the `django.conf.urls.url` or
+`django.urls.url` functions. Its usage is identical to the above:
+
+```python
+from django_distill import distill_url
+
+urlpatterns = (
+ distill_url(r'some/regex'
+ SomeView.as_view(),
+ name='url-view',
+ distill_func=some_func),
+)
+```
+
+### Parameters in file names
+
+You can standard Python string formatting in `distill_file` as well to enable
+you to change the output file path for a file if you wish. Note this does not
+update the URL used by Django so if you use this make sure your `path` pattern
+matches the `distill_file` pattern or your links might not work in Django. An
+example:
+
+```python
+# 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"
+)
+```
+
+### Non-standard 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 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 you can use the
+`distill_status_codes` optional argument to a view. For example:
+
+```python
+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),
+)
+```
+
+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 site.
+
+### Tracking Django's URL function support
+
+`django-distill` will mirror whatever your installed version of Django supports,
+therefore at some point the `distill_url` function will cease working in the future
+when Django 2.x itself depreciates the `django.conf.urls.url` and `django.urls.url`
+functions. You can use `distill_re_path` as a drop-in replacement. It is advisable to
+use `distill_path` or `distill_re_path` if you're building a new site now.
+
+
+# The `distill-local` command
+
+Once you have wrapped the URLs you want to generate statically you can now
+generate a complete functioning static site with:
+
+```bash
+$ ./manage.py distill-local [optional /path/to/export/directory]
+```
+
+Under the hood this simply iterates all URLs registered with `distill_url` and
+generates the pages for them using parts of the Django testing framework to
+spoof requests. Once the site pages have been rendered then files from the
+`STATIC_ROOT` are copied over. Existing files with the same name are replaced in
+the target directory and orphan files are deleted.
+
+`distill-local` supports the following optional arguments:
+
+`--collectstatic`: Automatically run `collectstatic` on your site before
+rendering, this is just a shortcut to save you typing an extra command.
+
+`--quiet`: Disable all output other than asking confirmation questions.
+
+`--force`: Assume 'yes' to all confirmation questions.
+
+`--exclude-staticfiles`: Do not copy any static files at all, only render output from
+Django views.
+
+**Note** If any of your views contain a Python error then rendering will fail
+then the stack trace will be printed to the terminal and the rendering command
+will exit with a status code of 1.
+
+
+# The `distill-publish` command
+
+```bash
+$ ./manage.py distill-publish [optional destination here]
+```
+
+If you have configured at least one publishing destination (see below) you can
+use the `distill-publish` command to publish the site to a remote location.
+
+This will perform a full synchronisation, removing any remote files that are no
+longer present in the generated static site and uploading any new or changed
+files. The site will be built into a temporary directory locally first when
+publishing which is deleted once the site has been published. Each file will be
+checked that it has been published correctly by requesting it via the
+`PUBLIC_URL`.
+
+`distill-publish` supports the following optional arguments:
+
+`--collectstatic`: Automatically run `collectstatic` on your site before
+rendering, this is just a shortcut to save you typing an extra command.
+
+`--quiet`: Disable all output other than asking confirmation questions.
+
+`--force`: Assume 'yes' to all confirmation questions.
+
+`--exclude-staticfiles`: Do not copy any static files at all, only render output from
+Django views.
+
+`--skip-verify`: Do not test if files are correctly uploaded on the server.
+
+`--ignore-remote-content`: Do not fetch the list of remote files. It means that all
+files will be uploaded, and no existing remote file will be deleted. This can be
+useful if you have a lot of files on the remote server, and you know that you want
+to update most of them, and you don't care if old files remain on the server.
+
+`--parallel-publish [number of threads]`: Publish files in parallel on multiple
+threads, this can speed up publishing. Defaults to `1` thread.
+
+**Note** that this means if you use `--force` and `--quiet` that the output
+directory will have all files not part of the site export deleted without any
+confirmation.
+
+**Note** If any of your views contain a Python error then rendering will fail
+then the stack trace will be printed to the terminal and the rendering command
+will exit with a status code of 1.
+
+
+# The `distill-test-publish` command
+
+```bash
+$ ./manage.py distill-test-publish [optional destination here]
+```
+
+This will connect to your publishing target, authenticate to it, upload a
+randomly named file, verify it exists on the `PUBLIC_URL` and then delete it
+again. Use this to check your publishing settings are correct.
+
+`distill-test-publish` has no arguments.
+
+
+# Optional configuration settings
+
+You can set the following optional `settings.py` variables:
+
+**DISTILL_DIR**: string, default directory to export to:
+
+```python
+DISTILL_DIR = '/path/to/export/directory'
+```
+
+**DISTILL_PUBLISH**: dictionary, like Django's `settings.DATABASES`, supports
+`default`:
+
+```python
+DISTILL_PUBLISH = {
+ 'default': {
+ ... options ...
+ },
+ 'some-other-target': {
+ ... options ...
+ },
+}
+```
+
+**DISTILL_SKIP_ADMIN_DIRS**: bool, defaults to `True`
+
+```python
+DISTILL_SKIP_ADMIN_DIRS = True
+```
+
+Set `DISTILL_SKIP_ADMIN_DIRS` to `False` if you want `django-distill` to also copy over
+static files in the `static/admin` directory. Usually, these are not required or
+desired for statically generated sites. The default behaviour is to skip static admin
+files.
+
+
+# Writing single files
+
+As of `django-distill` version `3.0.0` you can use the
+`django_distill.renderer.render_single_file` method to write out a single file
+to disk using `django_distill`. This is useful for writing out single files to disk,
+for example, you have a Django site which has some static files in a directory
+written by `django_distill` but the rest of the site is a normal dynamic Django site.
+You can update a static HTML file every time a model instance is saved. You can
+use single file writing with signals to achieve this. For example:
+
+```python
+# 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
+ )
+```
+
+The syntax for `render_single_file` is similar to Django's `url.reverse`. The full
+usage interface is:
+
+```python
+render_single_file(
+ '/path/to/output/directory',
+ 'view-name-set-in-urls-py',
+ *view_args,
+ **view_kwargs
+)
+```
+
+For example, if you had a blog post URL defined as:
+
+```python
+ # in urls.py
+ distill_path('post/<int:blog_id>_<slug:blog_slug>.html',
+ PostView.as_view(),
+ name='blog-post',
+ distill_func=get_all_blogposts),
+```
+
+Your usage would be:
+
+```python
+render_single_file(
+ '/path/to/output/directory',
+ 'blog-post',
+ blog_id=123,
+ blog_slug='blog-title-slug',
+)
+```
+
+which would write out the contents of `/post/123_blog-title-slug.html` into
+`/path/to/output/directory` as the file
+`/path/to/output/directory/post/123_blog-title-slug.html`. Note any required
+sub-directories (`/path/to/output/directory/post` in this example) will be
+automatically created if they don't already exist. All `django-distill` rules
+apply, such as URLs ending in `/` will be saved as `/index.html` to make sense
+for a physical file on disk.
+
+Also note that `render_single_file` can only be imported and used into an
+initialised Django project.
+
+
+# Publishing targets
+
+You can automatically publish sites to various supported remote targets through
+backends just like how you can use MySQL, SQLite, PostgreSQL etc. with
+Django by changing the backend database engine. Currently the engines supported
+by `django-distill` are:
+
+**django_distill.backends.amazon_s3**: Publish to an Amazon S3 bucket. Requires
+ the Python library `boto3` (`$ pip install django-distill[amazon]`). The bucket
+ must already exist (use the AWS control panel). Options:
+
+```python
+'some-s3-container': {
+ 'ENGINE': 'django_distill.backends.amazon_s3',
+ 'PUBLIC_URL': 'http://.../',
+ 'ACCESS_KEY_ID': '...',
+ 'SECRET_ACCESS_KEY': '...',
+ 'BUCKET': '...',
+},
+```
+
+**django_distill.backends.google_storage**: Publish to a Google Cloud Storage
+ bucket. Requires the Python libraries `google-api-python-client` and
+ `google-cloud-storage`
+ (`$ pip install django-distill[google]`). The bucket
+ must already exist and be set up to host a public static website (use the
+ Google Cloud control panel). Options:
+
+```python
+'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',
+},
+```
+
+Note that `JSON_CREDENTIALS` is optional; if it is not specified, the google libraries
+will try other authentication methods, in the search order described here:
+https://cloud.google.com/docs/authentication/application-default-credentials (e.g. the
+`GOOGLE_APPLICATION_CREDENTIALS` environment variable, attached service account, etc).
+
+
+**django_distill.backends.microsoft_azure_storage**: Publish to a Microsoft
+ Azure Blob Storage container. Requires the Python library
+ `azure-storage-blob` (`$ pip install django-distill[microsoft]`). The storage
+ account must already exist and be set up to host a public static website
+ (use the Microsoft Azure control panel). Options:
+
+```python
+'some-microsoft-storage-account': {
+ 'ENGINE': 'django_distill.backends.microsoft_azure_storage',
+ 'PUBLIC_URL': 'https://[storage-account-name]...windows.net/',
+ 'CONNECTION_STRING': '...',
+},
+```
+
+Note that each Azure storage account supports one static website using the
+magic container `$web` which is where `django-distill` will attempt to
+publish your site.
+
+
+# Tests
+
+There is a minimal test suite, you can run it by cloing this repository,
+installing the required dependancies in `requirements.txt` then execuiting:
+
+```bash
+# ./run-tests.py
+```
+
+
+# Contributing
+
+All properly formatted and sensible pull requests, issues and comments are
+welcome.
+
+
+
+
+%package help
+Summary: Development documents and examples for django-distill
+Provides: python3-django-distill-doc
+%description help
+# django-distill
+
+`django-distill` now has a website. Read more at:
+
+## :link: https://django-distill.com/
+
+`django-distill` is a minimal configuration static site generator and publisher
+for Django. Most Django versions are supported, however up to date versions are
+advised including the Django 3.x releases. `django-distill` as of the 1.7 release
+only supports Python 3. Python 2 support has been dropped. If you require Python 2
+support please pin `django-distill` to version 1.6 in your requirements.txt or
+Pipfile. Python 3.6 or above is advised.
+
+`django-distill` extends existing Django sites with the ability to export
+fully functional static sites. It is suitable for sites such as blogs that have
+a mostly static front end but you still want to use a CMS to manage the
+content.
+
+`django-distill` iterates over URLs in your Django project using easy to write
+iterable functions to yield the parameters for whatever pages you want to save
+as static HTML. These static files can be automatically uploaded to a bucket-style
+remote container such as Amazon S3, Googe Cloud Files, Microsoft Azure Storage,
+or, written to a local directory as a fully working local static version of
+your project. The site generation, or distillation process, can be easily
+integrated into CI/CD workflows to auto-deploy static sites on commit.
+`django-distill` can be defined as an extension to Django to make Django
+projects compatible with "Jamstack"-style site architecture.
+
+`django-distill` plugs directly into the existing Django framework without the
+need to write custom renderers or other more verbose code. You can also integrate
+`django-distill` with existing dynamic sites and just generate static pages for
+a small subsection of pages rather than the entire site.
+
+For static files on CDNs you can use the following 'cache buster' library to
+allow for fast static media updates when pushing changes:
+
+[:link: meeb/django-cachekiller](https://github.com/meeb/django-cachekiller)
+
+There is a complete example site that creates a static blog and uses
+`django-distill` with `django-cachekiller` via continuous deployment on Netlify
+available here:
+
+[:link: meeb/django-distill-example](https://github.com/meeb/django-distill-example)
+
+
+# Installation
+
+Install from pip:
+
+```bash
+$ pip install django-distill
+```
+
+Add `django_distill` to your `INSTALLED_APPS` in your `settings.py`:
+
+```python
+INSTALLED_APPS = [
+ # ... other apps here ...
+ 'django_distill',
+]
+```
+
+That's 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`.
+
+Static media files such as images and style sheets are copied from your static
+media directory defined in `STATIC_ROOT`. This means that you will want to run
+`./manage.py collectstatic` **before** you run `./manage.py distill-local`
+if you have made changes to static media. `django-distill` doesn't chain this
+request by design, however you can enable it with the `--collectstatic`
+argument.
+
+
+# Usage
+
+Assuming you have an existing Django project, edit a `urls.py` to include the
+`distill_path` function which replaces Django's standard `path` function and
+supports the new keyword arguments `distill_func` and `distill_file`.
+
+The `distill_func` argument should be provided with a function or callable
+class that returns an iterable or `None`.
+
+The `distill_file` argument is entirely optional and allows you to override the
+URL that would otherwise be generated from the reverse of the URL regex. This
+allows you to rename URLs like `/example` to any other name like
+`example.html`. As of v0.8 any URIs ending in a slash `/` are automatically
+modified to end in `/index.html`. You can use format string parameters in the
+`distill_file` to customise the file name, arg values from the URL will be
+substituted in, for example `{}` for positional args or `{param_name}` for
+named args.
+
+An example distill setup for a theoretical blogging app would be:
+
+```python
+# 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),
+)
+```
+
+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.
+
+If your path has no URI paramters, such as `/` or `/some-static-url` you do
+not have to specify the `distill_func` parameter if you don't want to. As for
+paths with no parameters the `distill_func` always returns `None`, this is set
+as the default behaviour for `distill_func`s.
+
+You can use the `distill_re_path` function as well, which replaces the default
+`django.urls.re_path` function. Its usage is identical to the above:
+
+```python
+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),
+)
+
+```
+
+If you are using an older version of Django in the 1.x series you can use the
+`distill_url` function instead which replaces the `django.conf.urls.url` or
+`django.urls.url` functions. Its usage is identical to the above:
+
+```python
+from django_distill import distill_url
+
+urlpatterns = (
+ distill_url(r'some/regex'
+ SomeView.as_view(),
+ name='url-view',
+ distill_func=some_func),
+)
+```
+
+### Parameters in file names
+
+You can standard Python string formatting in `distill_file` as well to enable
+you to change the output file path for a file if you wish. Note this does not
+update the URL used by Django so if you use this make sure your `path` pattern
+matches the `distill_file` pattern or your links might not work in Django. An
+example:
+
+```python
+# 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"
+)
+```
+
+### Non-standard 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 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 you can use the
+`distill_status_codes` optional argument to a view. For example:
+
+```python
+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),
+)
+```
+
+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 site.
+
+### Tracking Django's URL function support
+
+`django-distill` will mirror whatever your installed version of Django supports,
+therefore at some point the `distill_url` function will cease working in the future
+when Django 2.x itself depreciates the `django.conf.urls.url` and `django.urls.url`
+functions. You can use `distill_re_path` as a drop-in replacement. It is advisable to
+use `distill_path` or `distill_re_path` if you're building a new site now.
+
+
+# The `distill-local` command
+
+Once you have wrapped the URLs you want to generate statically you can now
+generate a complete functioning static site with:
+
+```bash
+$ ./manage.py distill-local [optional /path/to/export/directory]
+```
+
+Under the hood this simply iterates all URLs registered with `distill_url` and
+generates the pages for them using parts of the Django testing framework to
+spoof requests. Once the site pages have been rendered then files from the
+`STATIC_ROOT` are copied over. Existing files with the same name are replaced in
+the target directory and orphan files are deleted.
+
+`distill-local` supports the following optional arguments:
+
+`--collectstatic`: Automatically run `collectstatic` on your site before
+rendering, this is just a shortcut to save you typing an extra command.
+
+`--quiet`: Disable all output other than asking confirmation questions.
+
+`--force`: Assume 'yes' to all confirmation questions.
+
+`--exclude-staticfiles`: Do not copy any static files at all, only render output from
+Django views.
+
+**Note** If any of your views contain a Python error then rendering will fail
+then the stack trace will be printed to the terminal and the rendering command
+will exit with a status code of 1.
+
+
+# The `distill-publish` command
+
+```bash
+$ ./manage.py distill-publish [optional destination here]
+```
+
+If you have configured at least one publishing destination (see below) you can
+use the `distill-publish` command to publish the site to a remote location.
+
+This will perform a full synchronisation, removing any remote files that are no
+longer present in the generated static site and uploading any new or changed
+files. The site will be built into a temporary directory locally first when
+publishing which is deleted once the site has been published. Each file will be
+checked that it has been published correctly by requesting it via the
+`PUBLIC_URL`.
+
+`distill-publish` supports the following optional arguments:
+
+`--collectstatic`: Automatically run `collectstatic` on your site before
+rendering, this is just a shortcut to save you typing an extra command.
+
+`--quiet`: Disable all output other than asking confirmation questions.
+
+`--force`: Assume 'yes' to all confirmation questions.
+
+`--exclude-staticfiles`: Do not copy any static files at all, only render output from
+Django views.
+
+`--skip-verify`: Do not test if files are correctly uploaded on the server.
+
+`--ignore-remote-content`: Do not fetch the list of remote files. It means that all
+files will be uploaded, and no existing remote file will be deleted. This can be
+useful if you have a lot of files on the remote server, and you know that you want
+to update most of them, and you don't care if old files remain on the server.
+
+`--parallel-publish [number of threads]`: Publish files in parallel on multiple
+threads, this can speed up publishing. Defaults to `1` thread.
+
+**Note** that this means if you use `--force` and `--quiet` that the output
+directory will have all files not part of the site export deleted without any
+confirmation.
+
+**Note** If any of your views contain a Python error then rendering will fail
+then the stack trace will be printed to the terminal and the rendering command
+will exit with a status code of 1.
+
+
+# The `distill-test-publish` command
+
+```bash
+$ ./manage.py distill-test-publish [optional destination here]
+```
+
+This will connect to your publishing target, authenticate to it, upload a
+randomly named file, verify it exists on the `PUBLIC_URL` and then delete it
+again. Use this to check your publishing settings are correct.
+
+`distill-test-publish` has no arguments.
+
+
+# Optional configuration settings
+
+You can set the following optional `settings.py` variables:
+
+**DISTILL_DIR**: string, default directory to export to:
+
+```python
+DISTILL_DIR = '/path/to/export/directory'
+```
+
+**DISTILL_PUBLISH**: dictionary, like Django's `settings.DATABASES`, supports
+`default`:
+
+```python
+DISTILL_PUBLISH = {
+ 'default': {
+ ... options ...
+ },
+ 'some-other-target': {
+ ... options ...
+ },
+}
+```
+
+**DISTILL_SKIP_ADMIN_DIRS**: bool, defaults to `True`
+
+```python
+DISTILL_SKIP_ADMIN_DIRS = True
+```
+
+Set `DISTILL_SKIP_ADMIN_DIRS` to `False` if you want `django-distill` to also copy over
+static files in the `static/admin` directory. Usually, these are not required or
+desired for statically generated sites. The default behaviour is to skip static admin
+files.
+
+
+# Writing single files
+
+As of `django-distill` version `3.0.0` you can use the
+`django_distill.renderer.render_single_file` method to write out a single file
+to disk using `django_distill`. This is useful for writing out single files to disk,
+for example, you have a Django site which has some static files in a directory
+written by `django_distill` but the rest of the site is a normal dynamic Django site.
+You can update a static HTML file every time a model instance is saved. You can
+use single file writing with signals to achieve this. For example:
+
+```python
+# 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
+ )
+```
+
+The syntax for `render_single_file` is similar to Django's `url.reverse`. The full
+usage interface is:
+
+```python
+render_single_file(
+ '/path/to/output/directory',
+ 'view-name-set-in-urls-py',
+ *view_args,
+ **view_kwargs
+)
+```
+
+For example, if you had a blog post URL defined as:
+
+```python
+ # in urls.py
+ distill_path('post/<int:blog_id>_<slug:blog_slug>.html',
+ PostView.as_view(),
+ name='blog-post',
+ distill_func=get_all_blogposts),
+```
+
+Your usage would be:
+
+```python
+render_single_file(
+ '/path/to/output/directory',
+ 'blog-post',
+ blog_id=123,
+ blog_slug='blog-title-slug',
+)
+```
+
+which would write out the contents of `/post/123_blog-title-slug.html` into
+`/path/to/output/directory` as the file
+`/path/to/output/directory/post/123_blog-title-slug.html`. Note any required
+sub-directories (`/path/to/output/directory/post` in this example) will be
+automatically created if they don't already exist. All `django-distill` rules
+apply, such as URLs ending in `/` will be saved as `/index.html` to make sense
+for a physical file on disk.
+
+Also note that `render_single_file` can only be imported and used into an
+initialised Django project.
+
+
+# Publishing targets
+
+You can automatically publish sites to various supported remote targets through
+backends just like how you can use MySQL, SQLite, PostgreSQL etc. with
+Django by changing the backend database engine. Currently the engines supported
+by `django-distill` are:
+
+**django_distill.backends.amazon_s3**: Publish to an Amazon S3 bucket. Requires
+ the Python library `boto3` (`$ pip install django-distill[amazon]`). The bucket
+ must already exist (use the AWS control panel). Options:
+
+```python
+'some-s3-container': {
+ 'ENGINE': 'django_distill.backends.amazon_s3',
+ 'PUBLIC_URL': 'http://.../',
+ 'ACCESS_KEY_ID': '...',
+ 'SECRET_ACCESS_KEY': '...',
+ 'BUCKET': '...',
+},
+```
+
+**django_distill.backends.google_storage**: Publish to a Google Cloud Storage
+ bucket. Requires the Python libraries `google-api-python-client` and
+ `google-cloud-storage`
+ (`$ pip install django-distill[google]`). The bucket
+ must already exist and be set up to host a public static website (use the
+ Google Cloud control panel). Options:
+
+```python
+'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',
+},
+```
+
+Note that `JSON_CREDENTIALS` is optional; if it is not specified, the google libraries
+will try other authentication methods, in the search order described here:
+https://cloud.google.com/docs/authentication/application-default-credentials (e.g. the
+`GOOGLE_APPLICATION_CREDENTIALS` environment variable, attached service account, etc).
+
+
+**django_distill.backends.microsoft_azure_storage**: Publish to a Microsoft
+ Azure Blob Storage container. Requires the Python library
+ `azure-storage-blob` (`$ pip install django-distill[microsoft]`). The storage
+ account must already exist and be set up to host a public static website
+ (use the Microsoft Azure control panel). Options:
+
+```python
+'some-microsoft-storage-account': {
+ 'ENGINE': 'django_distill.backends.microsoft_azure_storage',
+ 'PUBLIC_URL': 'https://[storage-account-name]...windows.net/',
+ 'CONNECTION_STRING': '...',
+},
+```
+
+Note that each Azure storage account supports one static website using the
+magic container `$web` which is where `django-distill` will attempt to
+publish your site.
+
+
+# Tests
+
+There is a minimal test suite, you can run it by cloing this repository,
+installing the required dependancies in `requirements.txt` then execuiting:
+
+```bash
+# ./run-tests.py
+```
+
+
+# Contributing
+
+All properly formatted and sensible pull requests, issues and comments are
+welcome.
+
+
+
+
+%prep
+%autosetup -n django-distill-3.1.3
+
+%build
+%py3_build
+
+%install
+%py3_install
+install -d -m755 %{buildroot}/%{_pkgdocdir}
+if [ -d doc ]; then cp -arf doc %{buildroot}/%{_pkgdocdir}; fi
+if [ -d docs ]; then cp -arf docs %{buildroot}/%{_pkgdocdir}; fi
+if [ -d example ]; then cp -arf example %{buildroot}/%{_pkgdocdir}; fi
+if [ -d examples ]; then cp -arf examples %{buildroot}/%{_pkgdocdir}; fi
+pushd %{buildroot}
+if [ -d usr/lib ]; then
+ find usr/lib -type f -printf "/%h/%f\n" >> filelist.lst
+fi
+if [ -d usr/lib64 ]; then
+ find usr/lib64 -type f -printf "/%h/%f\n" >> filelist.lst
+fi
+if [ -d usr/bin ]; then
+ find usr/bin -type f -printf "/%h/%f\n" >> filelist.lst
+fi
+if [ -d usr/sbin ]; then
+ find usr/sbin -type f -printf "/%h/%f\n" >> filelist.lst
+fi
+touch doclist.lst
+if [ -d usr/share/man ]; then
+ find usr/share/man -type f -printf "/%h/%f.gz\n" >> doclist.lst
+fi
+popd
+mv %{buildroot}/filelist.lst .
+mv %{buildroot}/doclist.lst .
+
+%files -n python3-django-distill -f filelist.lst
+%dir %{python3_sitelib}/*
+
+%files help -f doclist.lst
+%{_docdir}/*
+
+%changelog
+* Wed May 31 2023 Python_Bot <Python_Bot@openeuler.org> - 3.1.3-1
+- Package Spec generated
diff --git a/sources b/sources
new file mode 100644
index 0000000..86d1095
--- /dev/null
+++ b/sources
@@ -0,0 +1 @@
+0a67e5aa2af0df68bc45654d02690215 django-distill-3.1.3.tar.gz