Runtimes

There are two main use cases for when to run renderstatic that determine how it should be included as a dependency.

Package Time

If you are generating static files that produce no deployment specific output as part of a Django app that will be distributed on pypi or a similar package service you should use renderstatic at package time - that is, before you build and publish the package.

Important

When used at package time django-render-static should be included as a development dependency only. There is no need to require the users of your package to install django-render-static because you are distributing the rendered files with your package.

Deployment Time

If you are generating static files that produce output dependent on the deployment specific configuration of a Django site you should use renderstatic as part of your deployment time routine - probably just before collectstatic is run. urls_to_js is an example of a deployment time use case because its output is defined by the deployment’s urls configuration.

Warning

If app directory loaders are used with the default render destinations at deployment time and the deployment user does not have write permission to the virtual environment directories of the installed apps - permission errors will be encountered. This can be fixed by using the dest parameter to force rendering to be written to a directory included in STATICFILES_DIRS

Incurred Dependencies

It may be necessary to incur a deployment time dependency on django-render-static to users of your reusable Django app. For example, if you are distributing a Django app that functions as a single page application (SPA) and makes use of urls_to_js to resolve its AJAX request urls dynamically you will need users of your app to run renderstatic to generate the urls file(s) your app expects. This can be even more complicated when you consider that your users might include multiple copies of your SPA at different url paths.

When you incur a deployment time dependency on your end users - you should take care to think about how it may be used and provide instructions and a reasonable default template configuration. Below is a notional directory layout and template configuration for a reusable app relying on urls_to_js that could be included multiple times in a specific Django deployment.

Lets call our notional app spa. Our application will need to use the namespaces assigned to different inclusions of it by the project. Lets layout our app with the following structure:

.
├── __init__.py
├── apps.py
├── static_templates
│   └── spa
│       └── urls.js
├── templates
│   └── spa
│       └── index.html
├── urls.py
└── views.py

We include a static template for generating our urls.js file but we do not pre-generate and include it in our distribution package. We must instruct our users to generate the file at deployment time. Lets say our template simply looks like this:

{% urls_to_js export=True include=include %}

It expects a context that has an include variable containing a list of namespaces to include. Lets say the project including our app has a ROOT_URLCONF file that looks like this:

from django.urls import include, path

urlpatterns = [
    path('spa1/', include('spa.urls', namespace='spa1')),
    path('spa2/', include('spa.urls', namespace='spa2'))
]

So our app is included twice under two different paths, one with the namespace spa1 and the other with the namespace spa2. We might instruct our users to generate the urls.js file using the following settings:

from pathlib import Path

LOCAL_STATIC_DIR = Path(BASE_DIR) / 'local_static'

STATICFILES_DIRS=[
    ('spa', LOCAL_STATIC_DIR),
]

STATIC_TEMPLATES={
    'templates': [
        ('spa/urls.js', {
            'context': {
                'include': ['spa1', 'spa2']
            },
            'dest': str(LOCAL_STATIC_DIR / 'urls.js')
        })
    ]
}

Here we setup a local static file directory first so our urls.js file will compile to it instead of the default location which would be spa/static/spa in your python environment to avoid any permissions issues (this may be unnecessary depending on the operations environment). We could alternatively render the file to STATIC_ROOT but that would bypass any collectstatic processing that might be necessary. We also add an include list that only includes the namespaces we’ve included the spa app under.

Lets say our spa app’s urls.py file looks like this:

from django.urls import path
from .views import Index, QryView

app_name = 'spa'

urlpatterns = [
    path('', Index.as_view(), name='index'),
    path('qry/', QryView.as_view(), name='qry'),
    path('qry/<int:arg>', QryView.as_view(), name='qry')
]

So we have an index page, and a query view that has an optional integer argument called arg. The context of our IndexView must contain the namespace the app was included under. To do this, our IndexView could easily build its context like this:

from django.views.generic import TemplateView


class Index(TemplateView):

    template_name = 'spa/index.html'

    def get_context_data(self, **kwargs):
        return {
            **super().get_context_data(),
            'namespace': self.request.resolver_match.namespace
        }

Our template file needs to pull in the generated url resolver and instantiate it with this default namespace:

{% load static %}
<html>
    <head>
        <script type="module">
            import { URLResolver } from "{% static 'spa/urls.js' %}";
            const urls = new URLResolver({namespace: '{{namespace}}'});
        </script>
    </head>

    <!-- now we can use urls.reverse('qry') and it will resolve to the correct url -->