Git Product home page Git Product logo

django-distill's Introduction

django-distill

django-distill now has a website. Read more at:

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:

๐Ÿ”— 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:

๐Ÿ”— meeb/django-distill-example

Installation

Install from pip:

$ pip install django-distill

Add django_distill to your INSTALLED_APPS in your settings.py:

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.

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:

# 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_funcs.

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:

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:

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 use 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:

# 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:

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.

Internationalization

Internationalization is only supported for URLs, page content is unable to be dynamically translated. By default your site will be generated using the LANGUAGE_CODE value in your settings.py. If you also set settings.USE_I18N to True then set other language codes in your settings.DISTILL_LANGUAGES value and register URLs with i18n_patterns(...) then your site will be generated in multiple languges. This assumes your multi-language site works as expected before adding django-distill.

For example if you set settings.LANGUAGE_CODE = 'en' your site will be generated in one language.

If you have something like this in your settings.py instead:

USE_I18N = True

DISTILL_LANGUAGES = [
    'en',
    'fr',
    'de',
]

While also using i18n_patternsin your urls.py like so:

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
    )
)

Then your views will be generaged as /en/some-file.html, /fr/some-file.html and /de/some-file.html. These URLs should work (and be translated) by your site already. django-distill doesn't do any translation magic, it just calls the URLs with the language code prefix.

Note While the default suggested method is to use settings.DISTILL_LANGUAGES to keep things seperate django-distill will also check settings.LANGUAGES for language codes.

Sitemaps

You may need to generate a list of all the URLs registered with django-distill. For example, you have a statically generated blog with a few hundred pages and you want to list all of the URLs easily in a sitemap.xml or other similar list of all URLs. You could wrap your sitemap view in distill_path then replicate all of your URL generation logic by importing your views distill_funcs from your urls.py and generating these all manually, but given this is quite a hassle there's a built-in helper to generate all your URLs that will be distilled for you.

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

Note that distilled_urls() will only return URLs after all of your URLs in urls.py have been loaded with distill_path(...).

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:

$ ./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.

--parallel-render [number of threads]: Render files in parallel on multiple threads, this can speed up rendering. Defaults to 1 thread.

--generate-redirects: Attempt to generate static redirects stored in the django.contrib.redirects app. If you have a redirect from /old/ to /new/ using this flag will create a static HTML <meta http-equiv="refresh" content="..."> style redirect at /old/index.html to /new/.

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

$ ./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.

--parallel-render [number of threads]: Render files in parallel on multiple threads, this can speed up rendering. Defaults to 1 thread.

--generate-redirects: Attempt to generate static redirects stored in the django.contrib.redirects app. If you have a redirect from /old/ to /new/ using this flag will create a static HTML <meta http-equiv="refresh" content="..."> style redirect at /old/index.html to /new/.

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

$ ./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:

DISTILL_DIR = '/path/to/export/directory'

DISTILL_PUBLISH: dictionary, like Django's settings.DATABASES, supports default:

DISTILL_PUBLISH = {
    'default': {
        ... options ...
    },
    'some-other-target': {
        ... options ...
    },
}

DISTILL_SKIP_ADMIN_DIRS: bool, defaults to True

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.

DISTILL_SKIP_STATICFILES_DIRS: list, defaults to []

DISTILL_SKIP_STATICFILES_DIRS = ['some_dir']

Set DISTILL_SKIP_STATICFILES_DIRS to a list of directory names you want django-distill to ignore directories in your defined static/ directory. You can use this to ignore copying directories containing files from apps you're not using that get bundled into your static/ directory by collect-static. For example if you set DISTILL_SKIP_STATICFILES_DIRS to ['some_dir'] the static files directory static/some_dir would be skipped.

DISTILL_LANGUAGES: list, defaults to []

DISTILL_LANGUAGES = [
    'en',
    'fr',
    'de',
]

Set DISTILL_LANGUAGES to a list of language codes to attempt to render URLs with. See the "Internationalization" section for more details.

Developing locally with HTTPS

If you are using a local development environment which has HTTPS support you may need to add SECURE_SSL_REDIRECT = False to your settings.py to prevent a CommandError being raised when a request returns a 301 redirect instead of the expected HTTP/200 response code.

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:

# 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:

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:

    # 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:

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:

'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:

'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:

'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:

# ./run-tests.py

Contributing

All properly formatted and sensible pull requests, issues and comments are welcome.

django-distill's People

Contributors

anentropic avatar debdolph avatar dellsystem avatar ezra-risq avatar franckboyer avatar henhuy avatar jayfk avatar jesusanaya avatar lexifdev avatar lolcatrix avatar lukelambert avatar meeb avatar miguelgr avatar nkantar avatar parkan avatar tback avatar thatch avatar truetug avatar veselosky avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

django-distill's Issues

Prefix option for distill-local

Hi, I tried to use Django-distill to generate a site that I would like to host on Github Pages.
The problem is that Github pages for projects uses URLs that use the format https://<organization>.github.io/<repository> instead of serving from the root, and django-distill generates URLs that would only work if both the pages and the statics are served from the root.

Would it be possible to add a "prefix" option to distill-local.py to automatically fix the URLs to the matching format ?

(For example, if right now the command generates a path like "/tags/index.html" or "/static/css/main.css", with the --prefix=my-project parameter on the command line it would generate "/myproject/tags/index.html" and "/myproject/static/css/main.css" instead.)

how do i use django admin

i need to use django admin
It's possible ?
this distill_path generates error
distill_path('',
admin.site.urls,
name=''),

Why are 'admin' and 'grapelli' excluded from the static files copy?

I am really liking django-distill for a hobby project of mine.
Spent quite a while wondering why, after I had run collectstatic and then run distill-local, the static files for every application except admin were ending up in my target directory.

I now see that admin and grappelli are hard-coded in as exclusions - https://github.com/meeb/django-distill/blob/master/django_distill/renderer.py#L229

How come these are excluded? The comments in the file don't help me understand. If there is no reason, I'll happily try and submit a pull request that stops it happening. If there is a good reason, I'll submit a pull request that mentions it in the documentation!

Could you release a new version with changes on main

Because the current release has an import issue with the MiddlewareNotUsed not being defined but is fixed on main.

 File "/Users/tgsoverly/code/core/api/venv/lib/python3.10/site-packages/django_distill/renderer.py", line 404, in render_single_file
    page_uri, file_name, http_response = renderer.render(
  File "/Users/tgsoverly/code/core/api/venv/lib/python3.10/site-packages/django_distill/renderer.py", line 205, in render
    return self.render_file(view_name, status_codes, view_args, view_kwargs)
  File "/Users/tgsoverly/code/core/api/venv/lib/python3.10/site-packages/django_distill/renderer.py", line 181, in render_file
    render = self.render_view(uri, status_codes, args, a)
  File "/Users/tgsoverly/code/core/api/venv/lib/python3.10/site-packages/django_distill/renderer.py", line 272, in render_view
    handler.load_middleware()
  File "/Users/tgsoverly/code/core/api/venv/lib/python3.10/site-packages/django_distill/renderer.py", line 119, in load_middleware
    except MiddlewareNotUsed as exc:
NameError: name 'MiddlewareNotUsed' is not defined

i18n_patterns and django-distill

Hi,

I'm trying to use django-distill on my website that uses the function django.conf.urls.i18n.i18n_patterns to generate language dependent urls.

I got the exception django.core.exceptions.ImproperlyConfigured: Using i18n_patterns in an included URLconf is not allowed. that is raised by the django.conf.urls.include function. This is called in django-distill.renderer.load_urls.

Actually, I don't really understand what is the purpose of the call to the include command in load_urls since the result it returns is not used. As a matter of fact, everything seems to work well if I comment out this line in renderer.py

Is there a more suitable way to make this work ?

Best regards

Missing files in sdist

It appears that the manifest is missing at least one file necessary to build
from the sdist for version 1.7. You're in good company, about 5% of other
projects updated in the last year are also missing files.

+ /tmp/venv/bin/pip3 wheel --no-binary django-distill -w /tmp/ext django-distill==1.7
Looking in indexes: http://10.10.0.139:9191/root/pypi/+simple/
Collecting django-distill==1.7
  Downloading http://10.10.0.139:9191/root/pypi/%2Bf/21a/b421401e178b1/django-distill-1.7.tar.gz (20 kB)
    ERROR: Command errored out with exit status 1:
     command: /tmp/venv/bin/python3 -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/tmp/pip-wheel-467cbaw4/django-distill/setup.py'"'"'; __file__='"'"'/tmp/pip-wheel-467cbaw4/django-distill/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' egg_info --egg-base /tmp/pip-wheel-467cbaw4/django-distill/pip-egg-info
         cwd: /tmp/pip-wheel-467cbaw4/django-distill/
    Complete output (5 lines):
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "/tmp/pip-wheel-467cbaw4/django-distill/setup.py", line 15, in <module>
        with open('requirements.txt', 'rt') as f:
    FileNotFoundError: [Errno 2] No such file or directory: 'requirements.txt'
    ----------------------------------------
ERROR: Command errored out with exit status 1: python setup.py egg_info Check the logs for full command output.

Generate Sitemap

i have set up a django project following the documentation on the django site.

Everything works great in my dev setup.

I use distill to output HTML for the pages in the site so i can upload to an SFTP that only serves static html files.

I've the basic django sitemap framework and it also works great and i can navigate to it in the browser, but when i distill:

i get this error:

CommandError: Failed to render view "/sitemap.xml": sitemap() missing 1 required positional argument: 'sitemaps'

Im pretty new at this stuff, but any help would be much appreciated!

Does minification work with django-distill?

I have essentially copied your example site and started tweaking it (demo | repo),

I have django-htmlmin installed and it works just fine until I try to distill the site, then it is just ignored, and nothing is minified. I've tried different ideas from their docs to make it work for the distilled site as well, but I just cannot seem to figure anything out.

I'm just wondering if it is incompatible with django-distill, or if not, how would I make it work with the generated static site?

After I got this working, I was going to try using django-compressor for CSS, SASS and JS as well, but maybe you already know if this is is or is not compatible with django-distill too?

Thanks!

Keywords not supported in distill_path

I stumbled upon keywords in distill_path function when switching from default django.urls.path to distill_path.
My original path looked something like this:

path(
    "distilled_forest/<int:wind_distance>/<int:forest_usage>",
    view=views.DistilledForestListAPIView.as_view(),
    name="distilled_forest",
)

Simply changing it into:

distill_path(
    "distilled_forest/<int:wind_distance>/<int:forest_usage>",
    view=views.DistilledForestListAPIView.as_view(),
    name="distilled_forest",
    distill_func=models.Setup.get_possible_setups
)

gave me a strange error without stack trace:

Loading site URLs
CommandError: Invalid view arguments

But I could figure it out: distill_path expects first two arguments given as simple args (not kwargs) - See line:

if len(args) < 2:

This works:

distill_path(
    "distilled_forest/<int:wind_distance>/<int:forest_usage>",
    views.DistilledForestListAPIView.as_view(),
    name="distilled_forest",
    distill_func=models.Setup.get_possible_setups
)

Maybe this could be changed to support keyword args - or at least a better Exception with explanation could be thrown?

Single file output support

Distill should support writing out single files to disk with a similar interface to Django's urls.reverse. This would allow signals to save single files to disk on saving a model and other convenient interfaces.

Is it possible to customize rendering or add functionalities to existing rendering by getting pre/ post signals

Hi ,

I find the library easy to use and simple to understand. I have certain requirements and would be great if i can solve it with the django-distill library.
Some information on the usecases.

  • Currently during the rendering process, the settings.STATIC_URL is used during generation process. Is it possible to change during the rendering process, that it points to the settings.DISTILL_DIR/static ?... currently the complete folder structure is generated and it works as long as i am opening the project in the same OS env(Ubuntu) but doesnt work when i change to windows for e.g.,

  • Currently media files used in the web project, are hosted in azure web services. I would like to have a pre signal so that i can also deploy the media files inside the settings.DISTILL_DIR. This will help in using the static files in an environment which is not connected to the internet.
    Thanks in advance

BR
SK

'WSGIRequest' object has no attribute 'session'

Running python manage.py distill-local site-export produces the response CommandError: Failed to render view "/": 'WSGIRequest' object has no attribute 'session'
URLs.py config:

from django.urls import path
from django.contrib.auth.views import LoginView, LogoutView

from . import views

from django_distill import distill_path

def get_index():
    return None
def get_ageVerification():
    return None
def get_register():
    return None
def get_login():
    return None
def get_profile():
    return None
def get_message():
    return None
def get_messages():
    return None
def get_faq():
    return None
def get_legal():
    return None


urlpatterns = [
    distill_path('', views.index, name='index', distill_func=get_index, distill_file="index.html"),
    distill_path('ageVerification', views.ageVerification, name='ageVerification', distill_func=get_ageVerification),
    distill_path('register', views.register, name="register", distill_func=get_register),
    distill_path('login', LoginView.as_view(template_name='user/login.html'), name='login', distill_func=get_login),
    path('logout', LogoutView.as_view(), name='logout'),
    distill_path('accounts/profile/', views.profile, name="profileUser", distill_func=get_profile),
    distill_path('message/<str:username>', views.message, name="message", distill_func=get_message),
    distill_path('messages', views.messages, name="messages", distill_func=get_messages),
    distill_path('faq', views.faq, name="faq", distill_func=get_faq),
    distill_path('legal', views.legal, name="legal", distill_func=get_legal)
]

ModuleNotFoundError: No module named 'future'

Traceback (most recent call last):
  File "manage.py", line 23, in <module>
    execute_from_command_line(sys.argv)
  File "/home/travis/virtualenv/python3.6.3/lib/python3.6/site-packages/django/core/management/__init__.py", line 371, in execute_from_command_line
    utility.execute()
  File "/home/travis/virtualenv/python3.6.3/lib/python3.6/site-packages/django/core/management/__init__.py", line 365, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/home/travis/virtualenv/python3.6.3/lib/python3.6/site-packages/django/core/management/__init__.py", line 216, in fetch_command
    klass = load_command_class(app_name, subcommand)
  File "/home/travis/virtualenv/python3.6.3/lib/python3.6/site-packages/django/core/management/__init__.py", line 36, in load_command_class
    module = import_module('%s.management.commands.%s' % (app_name, name))
  File "/home/travis/virtualenv/python3.6.3/lib/python3.6/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 994, in _gcd_import
  File "<frozen importlib._bootstrap>", line 971, in _find_and_load
  File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 678, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "/home/travis/virtualenv/python3.6.3/lib/python3.6/site-packages/django_distill/management/commands/distill-local.py", line 14, in <module>
    from django_distill.renderer import (run_collectstatic, render_to_dir)
  File "/home/travis/virtualenv/python3.6.3/lib/python3.6/site-packages/django_distill/renderer.py", line 11, in <module>
    from future.utils import raise_from
ModuleNotFoundError: No module named 'future'

Google Backend Remote path on windows

Hi, I'm using django-distill with the Google storage backend on windows. When uploading a file the remote path for the file becomes path\\to\\index.html. However Google storage interprets that literally and does not convert the windows path format. So the URL for the uploaded file becomes https://storage.googleapis.com/bucket/path%5Cto%5Cindex.html instead of the https://storage.googleapis.com/bucket/path/to/index.html that you would expect.

Would it be possible to convert the windows-style path format to the right url format before initiating the upload?

the files out without extension

from django.urls import path
from . import views
from blog.models import Post, Category
from django_distill import distill_path
from django.template.defaultfilters import slugify

def get_index():
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 {'pk': post.id, 'slug': slugify(post.title)}

urlpatterns = [
distill_path('post/int:pk/slug:slug',
views.PostDetailView.as_view(),
name='post',
distill_func=get_all_blogposts,
),

distill_path('',
             views.home,
             name='home',
             # 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'),

# path('', views.home, name="home"),
    
path('author/', views.author, name="author"),
# path('post/<int:pk>/<slug:slug>/', views.PostDetailView.as_view(), name="post"),
]

NoReverseMatch exception introduced in version 2.4

After upgrading django-distill from version 1.9 to the latest 3.0.2 admin views in my app started to crash with the following error

django.urls.exceptions.NoReverseMatch: Reverse for 'app_list' with keyword arguments '{'app_label': 'web'}' not found. 1 pattern(s) tried: ['__website_manager/(?P<app_label>auth)/$']

With a trial and error I found that versions prior to 2.3 are working normally, version 2.3 crashes the whole app on startup with:

File "/Users/ivan/.local/share/virtualenvs/adesk-website-mLhlACXX/lib/python3.8/site-packages/django_distill/renderer.py", line 40, in <module>
    nspath = ':'.join(namespaces)
TypeError: sequence item 1: expected str instance, NoneType found

and versions starting from 2.4 produce errors mentioned above.

Usage of "include" in distill_url?

Hey @meeb I've been using django_distill in a project and we want to include another package that uses django_distill using include(). But I'm getting failed to do that because instead of passing a view_func example MyVIew.as_view(), I'm passing a tuple i.e. include(package_name.urls). Can you please help me out? Is there any way I can get that possible using include() ?

static files are not being generated.

I am using latest version of django-distill. Whenever I use command: python .\manage.py distill-local --force --collectstatic, I can't see generated static folder.
my static root settings. STATIC_ROOT = BASE_DIR/"static".
What I want is the static folder to appear in folder setted in DISTILL_DIR.

Feature: Propagate view_name to distill_func

I want to make my distill_func to be aware of given view_name.
Use case:

  • Generate multiple distill_paths in a for loop:
views = [("name", View), ("name2", View2)]
urlpatterns += [
    distill_path(
        f"{name}_url/",
        view.as_view(),
        name=name,
        distill_func=get_params
    )
    for name, view in views
]
  • get different params depending on view name:
def get_params(view_name):
     if view_name == "name":
         return 1
     else:
         return 2

This is very usefull for me. Maybe for others too?
Will provide PR if you want to merge...

Feature reuest: Option to ignore render errors

For me it would be nice to add option to ignore non-200 errors on rendering pages.
I have a view which is filled with many parameters, but not all pages return status 200 (but 204 instead).
For me it would be nice to add option ignore_errors (default False) to distill_path.
Will also provide PR if this is interesting for others too.

HttpResponse content not rendered in distill-local

When using distill-local I became following error:
django.template.response.ContentNotRenderedError: The response content must be rendered before it can be accessed.
in line:

content = http_response.content

because http_responseis not rendered yet.
After adding http_response.render() before that line, everything seems to work and distilled files are generated...
Is this fix correct?
I will post simple PR...

Getting a 301 error when running distill on server

Hi,

I am getting a strange 301 error which is not happening during local development, but only when running on an app server. The same distill action on a local server runs without any error.

Unfortunately, I cannot do a remote debug on the server. Some information on the error

image

image

image

I can add more debug information, but I don't know which would be of use here.

BR

SK

Keyword arguments are not utilized properly in distill_path

It looks like the following urlpatterns fails when the site is being generated:

urlpatterns = [
    distill_path(route='/', view=TemplateView.as_view(template_name='test.html'), name='index'),
]

It seems that runserver works fine though. I think the issue might be here: https://github.com/meeb/django-distill/blob/master/django_distill/renderer.py#L184, notice that k is not being used anywhere during the rendering, therefore neither the route nor the view gets forwarded properly in this case. It works fine when using positional arguments for the route and view instead of the named arguments. Nonetheless, in some cases using named arguments is unavoidable (e.g. if these arguments are coming from an unpacked dictionary).

Remove the Rackspace Cloud Files backend

The Rackspace Cloud Files backend currently depends on Pyrax, which is Python2 only and depreciated. While there is an apparent replacement in the form of rackspacesdk all documentation is still for Pyrax and has not been updated. It's quite likely no-one uses this backend anyway any more given the popularity of S3 and Google Cloud Storage.

General plan at the moment is to remove the Rackspace Cloud Files backend and if anyone complains re-implement it with the openstacksdk, likely at quite some time expense given the sparse documentation and awkward testing.

ร‚ character being generated by distill-local

Hello, I am using django-distill to build a site locally and when I generate the site, every time I use the builtin humanize "naturaltime" filter an "ร‚" character is inserted in between the number and the time (e.g. "19ร‚ hours ago"). This is not occurring on either my development server, or the production apache server I am using - only during the local generation of django-distill.

Any ideas where I might be able to go to fix this? Do I need to specify utf8 encoding somewhere for django-distill to use?
Thanks so much for the help!

How can I render the static generated html files without GET Request to CMS

Hello.
I'm trying to create a Django blog site using a headless CMS.
Today I found django-distill and succeeded in generating static html files.

python ./manage.py distill-local

I don't want to send a GET request every time I render, so I'm thinking of using django-distill.
How can I display the statically generated site when I run python manage.py runserver?
Is the static site delivered after deployment in the first place?
Sorry for the rudimentary question.

I'll paste the source code that I think is relevant below.

#project/settings.py

...

STATIC_ROOT = Path(BASE_DIR, 'static')
DISTILL_DIR = Path(BASE_DIR, 'dist')
#project/urls.py
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('blog.urls'))
]
#blog.urls.py

from django_distill import distill_path, distill_url
from . import views
from django.conf import settings
import requests

app_name = 'blog'


def get_index():
    return None


def get_posts():
    end_point = '/post'
    url = getattr(settings, "BASE_URL", None)
    api_key = getattr(settings, "API_KEY", None)
    headers = {'API-KEY': api_key}
    res = requests.request('GET', url=url + end_point, headers=headers)
    for data in res.json()['contents']:
        yield data['id']


urlpatterns = [
    distill_path('',
                 views.post_list,
                 name='index',
                 distill_func=get_index,
                 distill_file='index.html'),

    distill_path('post/<slug:slug>.html',
                 views.post_detail,
                 name='post_detail',
                 distill_func=get_posts),
]
# blog/views.py

from django.shortcuts import render
import requests
from django.conf import settings
from django.http import Http404


def post_list(request):
    end_point = '/post'
    url = getattr(settings, "BASE_URL", None)
    api_key = getattr(settings, "API_KEY", None)
    headers = {'API-KEY': api_key}
    res = requests.request('GET', url=url + end_point, headers=headers)
    context = {
        'post_list': res.json()['contents']
    }

    return render(request, 'blog/index.html', context)


def post_detail(request, slug):
    end_point = f'/post/{slug}'
    url = getattr(settings, "BASE_URL", None)
    api_key = getattr(settings, "API_KEY", None)
    headers = {'API-KEY': api_key}
    res = requests.request('GET', url=url + end_point, headers=headers)
    if res.status_code != 200:
        raise Http404

    context = {
        'post': res.json()
    }

    return render(request, 'blog/post_detail.html', context)

worki with multiple models

Hi, I try to understand how can I generate my project in static files. It has multiple class objects in views and to be honest I don't know how pass them to distill_func . For example my index looks like this:

def index(request, *args, **kwargs):
    multilanguage = Multilanguage.objects.exclude(multilanguage_marker=False)
    site_seo_tools = SiteSeoTools.objects.first()
    carousel = Carousel.objects.all()
    homepage = Homepage.objects.first()
    box = Box.objects.exclude(display=False)
    subpage_sorted = Subpage.objects.exclude(is_active=False).order_by('display_order')
    context = {
        'carousel':carousel, 
        'multilanguage':multilanguage,
        'site_seo_tools':site_seo_tools, 
        'homepage': homepage,
        'box': box,
        'subpage_sorted': subpage_sorted,
        'recaptcha':settings.GOOGLE_RECAPTCHA
    }
    return render(request, 'home.html', context)

How can I do that ?

Django CMS

Hi, I am trying to install django-distil in a project with Django CMS. It's compatible ?

My problem is in the urls.py, in these lines:

urlpatterns = [
        distill_path('',
                 details,
                 name='blog-index',
                 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

I get the following error when running distil-local:

CommandError: Failed to render view "/": details() missing 1 required positional argument: 'slug'

if I pass slug = None on details() I get the following error:

TypeError: details() missing 1 required positional argument: 'request'

I don't know much about it, but I think the problem is class-based views and function-based views.

This is the code for the views.py of Django CMS:

from django.conf import settings
from django.contrib.auth import REDIRECT_FIELD_NAME, login as auth_login
from django.contrib.auth.views import redirect_to_login
from django.http import HttpResponse, HttpResponseRedirect
from django.urls import reverse
from django.utils.cache import patch_cache_control
from django.utils.http import is_safe_url, urlquote
from django.utils.timezone import now
from django.utils.translation import get_language_from_request
from django.views.decorators.http import require_POST

from cms.cache.page import get_page_cache
from cms.exceptions import LanguageError
from cms.forms.login import CMSToolbarLoginForm
from cms.models.pagemodel import TreeNode
from cms.page_rendering import _handle_no_page, render_page, render_object_structure, _render_welcome_page
from cms.toolbar.utils import get_toolbar_from_request
from cms.utils import get_current_site
from cms.utils.conf import get_cms_setting
from cms.utils.i18n import (get_fallback_languages, get_public_languages,
                            get_redirect_on_fallback, get_language_list,
                            get_default_language_for_site,
                            is_language_prefix_patterns_used)
from cms.utils.page import get_page_from_request
from cms.utils.page_permissions import user_can_change_page


def _clean_redirect_url(redirect_url, language):
    if (redirect_url and is_language_prefix_patterns_used() and redirect_url[0] == "/"
            and not redirect_url.startswith('/%s/' % language)):
        # add language prefix to url
        redirect_url = "/%s/%s" % (language, redirect_url.lstrip("/"))
    return redirect_url


def details(request, slug):
    """
    The main view of the Django-CMS! Takes a request and a slug, renders the
    page.
    """
    response_timestamp = now()
    if get_cms_setting("PAGE_CACHE") and (
        not hasattr(request, 'toolbar') or (
            not request.toolbar.edit_mode_active and
            not request.toolbar.show_toolbar and
            not request.user.is_authenticated
        )
    ):
        cache_content = get_page_cache(request)
        if cache_content is not None:
            content, headers, expires_datetime = cache_content
            response = HttpResponse(content)
            response.xframe_options_exempt = True
            response._headers = headers
            # Recalculate the max-age header for this cached response
            max_age = int(
                (expires_datetime - response_timestamp).total_seconds() + 0.5)
            patch_cache_control(response, max_age=max_age)
            return response

    # Get a Page model object from the request
    site = get_current_site()
    page = get_page_from_request(request, use_path=slug)
    toolbar = get_toolbar_from_request(request)
    tree_nodes = TreeNode.objects.get_for_site(site)

    if not page and not slug and not tree_nodes.exists():
        # render the welcome page if the requested path is root "/"
        # and there's no pages
        return _render_welcome_page(request)

    if not page:
        # raise 404
        _handle_no_page(request)

    request.current_page = page

    if hasattr(request, 'user') and request.user.is_staff:
        user_languages = get_language_list(site_id=site.pk)
    else:
        user_languages = get_public_languages(site_id=site.pk)

    request_language = get_language_from_request(request, check_path=True)

    if not page.is_home and request_language not in user_languages:
        # The homepage is treated differently because
        # when a request goes to the root of the site (/)
        # without a language, Django will redirect to the user's
        # browser language which might not be a valid cms language,
        # this means we need to correctly redirect that request.
        return _handle_no_page(request)

    # get_published_languages will return all languages in draft mode
    # and published only in live mode.
    # These languages are then filtered out by the user allowed languages
    available_languages = [
        language for language in user_languages
        if language in list(page.get_published_languages())
    ]

    own_urls = [
        request.build_absolute_uri(request.path),
        '/%s' % request.path,
        request.path,
    ]

    try:
        redirect_on_fallback = get_redirect_on_fallback(request_language, site_id=site.pk)
    except LanguageError:
        redirect_on_fallback = False

    if request_language not in user_languages:
        # Language is not allowed
        # Use the default site language
        default_language = get_default_language_for_site(site.pk)
        fallbacks = get_fallback_languages(default_language, site_id=site.pk)
        fallbacks = [default_language] + fallbacks
    else:
        fallbacks = get_fallback_languages(request_language, site_id=site.pk)

    # Only fallback to languages the user is allowed to see
    fallback_languages = [
        language for language in fallbacks
        if language != request_language and language in available_languages
    ]
    language_is_unavailable = request_language not in available_languages

    if language_is_unavailable and not fallback_languages:
        # There is no page with the requested language
        # and there's no configured fallbacks
        return _handle_no_page(request)
    elif language_is_unavailable and (redirect_on_fallback or page.is_home):
        # There is no page with the requested language and
        # the user has explicitly requested to redirect on fallbacks,
        # so redirect to the first configured / available fallback language
        fallback = fallback_languages[0]
        redirect_url = page.get_absolute_url(fallback, fallback=False)
    else:
        page_path = page.get_absolute_url(request_language)
        page_slug = page.get_path(request_language) or page.get_slug(request_language)

        if slug and slug != page_slug and request.path[:len(page_path)] != page_path:
            # The current language does not match its slug.
            # Redirect to the current language.
            return HttpResponseRedirect(page_path)
        # Check if the page has a redirect url defined for this language.
        redirect_url = page.get_redirect(request_language, fallback=False) or ''
        redirect_url = _clean_redirect_url(redirect_url, request_language)

    if redirect_url:
        if request.user.is_staff and toolbar.edit_mode_active:
            toolbar.redirect_url = redirect_url
        elif redirect_url not in own_urls:
            # prevent redirect to self
            return HttpResponseRedirect(redirect_url)

    # permission checks
    if page.login_required and not request.user.is_authenticated:
        return redirect_to_login(urlquote(request.get_full_path()), settings.LOGIN_URL)

    if hasattr(request, 'toolbar'):
        request.toolbar.set_object(page)

    structure_requested = get_cms_setting('CMS_TOOLBAR_URL__BUILD') in request.GET

    if user_can_change_page(request.user, page) and structure_requested:
        return render_object_structure(request, page)
    return render_page(request, page, current_language=request_language, slug=slug)


@require_POST
def login(request):
    redirect_to = request.GET.get(REDIRECT_FIELD_NAME)

    if not is_safe_url(url=redirect_to, allowed_hosts=request.get_host()):
        redirect_to = reverse("pages-root")

    if request.user.is_authenticated:
        return HttpResponseRedirect(redirect_to)

    form = CMSToolbarLoginForm(request=request, data=request.POST)

    if form.is_valid():
        auth_login(request, form.user_cache)
    else:
        redirect_to += u'?cms_toolbar_login_error=1'
    return HttpResponseRedirect(redirect_to)

I hope you can help me, thank you very much.

Change Detection?

I think this may be a feature request, but does django-distill support generating static pages only for database records that have changed since the last generation, or does it re-generate all static pages every time the distill-local command is run?

Configuring output of % static in django ?

enter image description here

I'm experimenting with django-distill (https://github.com/mgrp/django-distill) to generate a static site from a django project. i'm using django 1.10.8 . My main template contains:

<!-- Bootstrap Core CSS -->
{% load staticfiles %}
<link href="{% static "css/bootstrap.min.css" %}" rel="stylesheet" />

<!-- Custom CSS -->
<link href="{% static "css/landing-page.css" %}" rel="stylesheet" />
<link rel="stylesheet" type="text/css" href="//cdn.jsdelivr.net/jquery.slick/1.5.0/slick.css"/>

The paths to the css and js files are correct when I run the project locally on my windows system using a dev server. However when I look at the source html I see:

enter image description here

on my windows system this works with a running sever, i.e.

 http://127.0.0.1:8000/static/css/bootstrap.min.css

but it breaks with static files and I have to change these to

  <link href="./static/css/bootstrap.min.css" rel="stylesheet" />

Is there any way to set % static to render to

./static/ instead of /static/

Switch to using boto3

The Amazon S3 backend needs to be updated to use the boto3 library rather than the now legacy boto library.

Broken link on site

Went to the installation page - https://django-distill.com/install . At the bottom of the page is the link to the intergration
image

Clicked on the link and it went to the following incorrect address; https://django-distill.com/integration.html. This address should be https://django-distill.com/integration

How to protect .git directory with distill-local

I'm trying to turn a django site into a static site using https://github.com/mgrp/django-distill . I am outputting the static files into

/e/ENVS/STATIC/static1

on my win10 local system. As I make changes I want to overwrite all the non hidden files including the .git directory, the commit and push the changes to my github repo for deployment. Unfortunately the distill project overwrites the entire directory , deleting the .git/ files. I've tried to protect the git files with

$ chmod -R 707 .git/ 

using git-bash, but the output looks like:

drwxr-xr-x 1 me 197121     0 Oct 21 14:35 ./
drwxr-xr-x 1 me 197121     0 Oct 20 17:11 ../
drwxr-xr-x 1 me 197121     0 Oct 21 14:35 .git/
drwxr-xr-x 1 me 197121     0 Oct 21 14:36 .idea/
-rw-r--r-- 1 me 197121  9963 Oct 21 15:02 agreement.html
-rw-r--r-- 1 me 197121    17 Oct 21 15:02 contact.html
-rw-r--r-- 1 me 197121 14027 Oct 21 15:02 documents.html
-rw-r--r-- 1 me 197121 17048 Oct 21 15:02 form.html
-rw-r--r-- 1 me 197121 11060 Oct 21 15:02 index.html
-rw-r--r-- 1 me 197121  4921 Oct 21 15:02 slideshow.html
drwxr-xr-x 1 me 197121     0 Oct 21 15:02 static/

Do you have any advice on how to protect the .git/ directory or copy the new files without deleting the .git directory. This would really simplify the workflow, by allowing you to quickly stage and push to my github repo which is linked to (in my case) a netlify account.

Triggering page generation manually?

Hi. Is there a way to trigger the generation of static pages manually? Eg. Trigger it to render on receiver save?

I've got 50000 mostly static pages that only need to be refreshed once a day. What's the best way to handle that? It would take too long to prerender 50K pages everytime I deploy.

Thanks

Make `distill_func` optional

I find that I very often create a function that does nothing:

def return_none() -> None:
    return None

just so I can provide it to distill_func:

urlpatterns = [
    distill_path('', TemplateView.as_view(template_name='home.html'), name='home',
                 distill_func=return_none),
    # ... (the same repeating many times)
]

It seems that using a return_none-like function by default (and defaulting to it when it is not provided for distill_path) would be a more elegant approach in these cases.

Django 2.0 replaced django.core.urlresolvers with django.urls

setup.py & requirements.txtdo not pin the version of Django, and Django 2.0 is out, and Django distill is broken.

  File "/home/travis/virtualenv/python3.6.3/lib/python3.6/site-packages/django_distill/management/commands/distill-local.py", line 14, in <module>
    from django_distill.renderer import (run_collectstatic, render_to_dir)
  File "/home/travis/virtualenv/python3.6.3/lib/python3.6/site-packages/django_distill/renderer.py", line 17, in <module>
    from django.core.urlresolvers import reverse
ModuleNotFoundError: No module named 'django.core.urlresolvers'

Refer to "django.core.urlresolvers" entry on their deprecation doc.

Unable to install via pip

The packages setup.py imports __version__ from django_distill/__init__.py which imports url from django.conf.urls.

This is problematic if Django is not installed, e.g. when using pip to install all the packages in a requirements.txt and pip decides to install django-distill before it is installing Django.

Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "/tmp/pip-build-pr2k64s3/django-distill/setup.py", line 6, in <module>
        from django_distill import __version__ as version
      File "/tmp/pip-build-pr2k64s3/django-distill/django_distill/__init__.py", line 4, in <module>
        from django_distill.distill import distill_url
      File "/tmp/pip-build-pr2k64s3/django-distill/django_distill/distill.py", line 4, in <module>
        from django.conf.urls import url
    ModuleNotFoundError: No module named 'django'

Make copying of existing staticfiles and media optional

In my case, I want to serve distilled files as static files via Whitenoise.
Therefore I store disitilled files in my STATIC_ROOT.
Unfortunately, django-distill copies all existing staticfiles and media files into subfolder static in DISTILL_DIR when running distill-local - and thus all files are copied unnecessarily.

I created a PR in which copying of staticfiles and media can be suppressed by giving an extra option --exclude-staticfiles. Maybe this is also helpful for others?
See my branch https://github.com/henhuy/django-distill/tree/optional_static_and_media

Add an Azure blob store backend

Add a backend for Azure blob storage. This should be relatively easy and drop in next to the existing Google Cloud Files and S3 backends.

Namespaces not taken into account

I wanted to distill views from an app (via python manage.py distill-local), but I think distill does not take namespaces into account...
I tried with following setup:

# base urls.py
urlpatterns = [
    ...
    path("", include("djagora.map.urls", namespace="map")),
]

# map/urls.py
app_name = "map"

urlpatterns = [
    ...
    distill_path(
        "some_view/",
        view=views.SomeView.as_view(),
        name="some",
    ),
]

But this lead to an error when generating uri:

uri = self.generate_uri(view_name, param_set)

as view_name only contains "some", but correct reverse name would be "map:some".
After setting view_name = "map:some" within debugging mode, script goes on without error (giving me some errors later, but this does not belong here).
Can you confirm this error, or am I missing something?

Django i18n support

Does django-distil generate i18n files. For example project.com/fr,project.com/de

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.