{"id":20116524,"url":"https://github.com/bckohan/django-render-static","last_synced_at":"2026-01-30T09:37:58.575Z","repository":{"id":37791272,"uuid":"314977353","full_name":"bckohan/django-render-static","owner":"bckohan","description":"Use Django's template engines to render static files at deployment or package time. Includes transpilers for extending Django's url reversal and enums to JavaScript.","archived":false,"fork":false,"pushed_at":"2025-05-01T20:49:52.000Z","size":1130,"stargazers_count":20,"open_issues_count":5,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-05-05T20:13:22.756Z","etag":null,"topics":["defines","django","enum","enums","javascript","python","reverse","static-files","templates","transpiler","transpilers"],"latest_commit_sha":null,"homepage":"https://django-render-static.rtfd.io","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bckohan.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-11-22T06:49:21.000Z","updated_at":"2025-05-01T20:49:50.000Z","dependencies_parsed_at":"2022-06-23T20:22:56.040Z","dependency_job_id":"15b580d5-04bd-4813-9181-42b8691d8c22","html_url":"https://github.com/bckohan/django-render-static","commit_stats":{"total_commits":211,"total_committers":3,"mean_commits":70.33333333333333,"dds":0.2274881516587678,"last_synced_commit":"c6850a29416cdd27718161367ddf7cd806e07d94"},"previous_names":[],"tags_count":25,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bckohan%2Fdjango-render-static","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bckohan%2Fdjango-render-static/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bckohan%2Fdjango-render-static/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bckohan%2Fdjango-render-static/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bckohan","download_url":"https://codeload.github.com/bckohan/django-render-static/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252703194,"owners_count":21790846,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["defines","django","enum","enums","javascript","python","reverse","static-files","templates","transpiler","transpilers"],"created_at":"2024-11-13T18:41:04.483Z","updated_at":"2026-01-30T09:37:58.516Z","avatar_url":"https://github.com/bckohan.png","language":"Python","readme":"[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)\n[![PyPI version](https://badge.fury.io/py/django-render-static.svg)](https://pypi.python.org/pypi/django-render-static/)\n[![PyPI pyversions](https://img.shields.io/pypi/pyversions/django-render-static.svg)](https://pypi.python.org/pypi/django-render-static/)\n[![PyPI djversions](https://img.shields.io/pypi/djversions/django-render-static.svg)](https://pypi.org/project/django-render-static/)\n[![PyPI status](https://img.shields.io/pypi/status/django-render-static.svg)](https://pypi.python.org/pypi/django-render-static)\n[![Documentation Status](https://readthedocs.org/projects/django-render-static/badge/?version=latest)](http://django-render-static.readthedocs.io/?badge=latest/)\n[![Code Cov](https://codecov.io/gh/bckohan/django-render-static/branch/main/graph/badge.svg?token=0IZOKN2DYL)](https://codecov.io/gh/bckohan/django-render-static)\n[![Test Status](https://github.com/bckohan/django-render-static/actions/workflows/test.yml/badge.svg?branch=main)](https://github.com/bckohan/django-render-static/actions/workflows/test.yml?query=branch:main)\n[![Lint Status](https://github.com/bckohan/django-render-static/actions/workflows/lint.yml/badge.svg?branch=main)](https://github.com/bckohan/django-render-static/actions/workflows/lint.yml?query=branch:main)\n[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)\n[![Published on Django Packages](https://img.shields.io/badge/Published%20on-Django%20Packages-0c3c26)](https://djangopackages.org/packages/p/django-render-static/)\n\n# django-render-static\n\nUse Django's template engines to render static files that are collected\nduring the ``collectstatic`` routine and likely served above Django at runtime.\nFiles rendered by django-render-static are immediately available to participate\nin the normal static file collection pipeline.\n\nFor example, a frequently occurring pattern that violates the\n[DRY principle](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) is the presence of defines,\nor enum like structures in server side Python code that are simply replicated in client side\nJavaScript. Another example might be rebuilding Django URLs from arguments in a\n[Single Page Application](https://en.wikipedia.org/wiki/Single-page_application). Single-sourcing\nthese structures by transpiling client side code from the server side code keeps the stack bone DRY.\n\n**`django-render-static` includes Python to Javascript transpilers for:**\n\n* Django's `reverse` function (`urls_to_js`)\n* PEP 435 style Python enumerations (`enums_to_js`)\n* Plain data define-like structures in Python classes and modules\n    (`defines_to_js`)\n\nTranspilation is extremely flexible and may be customized by using override blocks or extending the provided \ntranspilers.\n\n`django-render-static` also formalizes the concept of a package-time or deployment-time\nstatic file rendering step. It piggybacks off the existing templating engines and configurations\nand should therefore be familiar to Django developers. It supports both standard Django templating\nand Jinja templates and allows contexts to be specified in python, json or YAML.\n\nYou can report bugs and discuss features on the\n[issues page](https://github.com/bckohan/django-render-static/issues).\n\n[Contributions](https://github.com/bckohan/django-render-static/blob/main/CONTRIBUTING.rst) are\nencouraged!\n\n[Full documentation at read the docs.](https://django-render-static.readthedocs.io/en/latest/)\n\n## Installation\n\n1. Clone django-render-static from [GitHub](http://github.com/bckohan/django-render-static) or install a release off [PyPI](http://pypi.python.org/pypi/django-render-static):\n\n```shell\npip install django-render-static\n```\n\n2. Add 'render_static' to your ``INSTALLED_APPS`` :\n\n```python\nINSTALLED_APPS = [\n    'render_static',\n]\n```\n\n3. Add a ``STATIC_TEMPLATES`` configuration directive to your settings file:\n\n```python\n\nSTATIC_TEMPLATES = {\n    'templates' : [\n        ('path/to/template':, {'context' {'variable': 'value'})\n    ]\n}\n```\n\n4. Run ``renderstatic`` preceding every run of ``collectstatic`` :\n\n```shell\n$\u003e manage.py renderstatic\n$\u003e manage.py collectstatic\n```\n\n## Usage\n\n### Transpiling URL reversal\n\nYou'd like to be able to call something like `reverse` on path names from your client JavaScript\ncode the same way you do from Python Django code.\n\nYour settings file might look like:\n\n```python\n    STATIC_TEMPLATES={\n        'ENGINES': [{\n            'BACKEND': 'render_static.backends.StaticDjangoTemplates',\n            'OPTIONS': {\n                'loaders': [\n                    ('render_static.loaders.StaticLocMemLoader', {\n                        'urls.js': '{% urls_to_js %}'\n                    })\n                ]\n            },\n        }],\n        'templates': ['urls.js']\n    }\n```\n\nThen call `renderstatic` before `collectstatic`:\n\n```shell\n$\u003e ./manage.py renderstatic\n$\u003e ./manage.py collectstatic\n```\n\nIf your root urls.py looks like this:\n\n```python\nfrom django.contrib import admin\nfrom django.urls import path\n\nfrom .views import MyView\n\nurlpatterns = [\n    path('admin/', admin.site.urls),\n    path('simple', MyView.as_view(), name='simple'),\n    path('simple/\u003cint:arg1\u003e', MyView.as_view(), name='simple'),\n    path('different/\u003cint:arg1\u003e/\u003cstr:arg2\u003e', MyView.as_view(), name='different'),\n]\n```\n\nSo you can now fetch paths like this, in a way that is roughly API-equivalent\nto Django's `reverse` function:\n\n```javascript\nimport { URLResolver } from '/static/urls.js';\n\nconst urls = new URLResolver();\n\n// /different/143/emma\nurls.reverse('different', {kwargs: {'arg1': 143, 'arg2': 'emma'}});\n\n// reverse also supports query parameters\n// /different/143/emma?intarg=0\u0026listarg=A\u0026listarg=B\u0026listarg=C\nurls.reverse(\n    'different',\n    {\n        kwargs: {arg1: 143, arg2: 'emma'},\n        query: {\n            intarg: 0,\n            listarg: ['A', 'B', 'C']\n        }\n    }\n);\n```\n\n### URLGenerationFailed Exceptions \u0026 Placeholders\n\nIf you encounter a ``URLGenerationFailed`` exception you most likely need to register a placeholder for the argument in question. A placeholder is just a string or object that can be coerced to a string that matches the regular expression for the argument:\n\n```python\nfrom render_static.placeholders import register_variable_placeholder\n\napp_name = 'year_app'\nurlpatterns = [\n    re_path(r'^fetch/(?P\u003cyear\u003e\\d{4})/$', YearView.as_view(), name='fetch_year')\n]\n\nregister_variable_placeholder('year', 2000, app_name=app_name)\n```\n\nUsers should typically use a path instead of re_path and register their own custom converters when needed. Placeholders can be directly registered on the converter (and are then conveniently available to users of your app!):\n\n```python\nfrom django.urls.converters import register_converter\n\nclass YearConverter:\n    regex = '[0-9]{4}'\n    placeholder = 2000  # this attribute is used by `url_to_js` to reverse paths\n\n    def to_python(self, value):\n        return int(value)\n\n    def to_url(self, value):\n        return str(value)\n\n\nregister_converter(YearConverter, 'year')\n\nurlpatterns = [\n    path('fetch/\u003cyear:year\u003e', YearView.as_view(), name='fetch_year')\n]\n```\n\n### Transpiling Enumerations\n\nSay instead of the usual choices tuple you're using PEP 435 style python enumerations as model\nfields using [django-enum](http://pypi.python.org/pypi/django-enum) and\n[enum-properties](http://pypi.python.org/pypi/enum-properties). For example we might define a\nsimple color enumeration like so:\n\n```python\nfrom django.db import models\nfrom django_enum import EnumField, TextChoices\nfrom enum_properties import p, s\n\nclass ExampleModel(models.Model):\n\n    class Color(TextChoices, s('rgb'), s('hex', case_fold=True)):\n\n        # name   value   label       rgb       hex\n        RED   =   'R',   'Red',   (1, 0, 0), 'ff0000'\n        GREEN =   'G',   'Green', (0, 1, 0), '00ff00'\n        BLUE  =   'B',   'Blue',  (0, 0, 1), '0000ff'\n\n    color = EnumField(Color, null=True, default=None)\n```\n\nIf we define an enum.js template that looks like this:\n\n```js+django\n\n    {% enums_to_js enums=\"examples.models.ExampleModel.Color\" %}\n```\n\nIt will contain a javascript class transpilation of the Color enum that looks\nlike this:\n\n```javascript\n\nclass Color {\n\n    static RED = new Color(\"R\", \"RED\", \"Red\", [1, 0, 0], \"ff0000\");\n    static GREEN = new Color(\"G\", \"GREEN\", \"Green\", [0, 1, 0], \"00ff00\");\n    static BLUE = new Color(\"B\", \"BLUE\", \"Blue\", [0, 0, 1], \"0000ff\");\n\n    constructor (value, name, label, rgb, hex) {\n        this.value = value;\n        this.name = name;\n        this.label = label;\n        this.rgb = rgb;\n        this.hex = hex;\n    }\n\n    toString() {\n        return this.value;\n    }\n\n    static get(value) {\n        switch(value) {\n            case \"R\":\n                return Color.RED;\n            case \"G\":\n                return Color.GREEN;\n            case \"B\":\n                return Color.BLUE;\n        }\n        throw new TypeError(`No Color enumeration maps to value ${value}`);\n    }\n\n    static [Symbol.iterator]() {\n        return [Color.RED, Color.GREEN, Color.BLUE][Symbol.iterator]();\n    }\n}\n```\n\nWe can now use our enumeration like so:\n\n```javascript\nColor.BLUE === Color.get('B');\nfor (const color of Color) {\n    console.log(color);\n}\n```\n\n### Transpiling Model Field Choices\n\nYou have an app with a model with a character field that has several valid choices defined in an\nenumeration type way, and you'd like to export those defines to JavaScript. You'd like to include\na template for other's using your app to use to generate a defines.js file. Say your app structure\nlooks like this::\n\n    .\n    └── examples\n        ├── __init__.py\n        ├── apps.py\n        ├── defines.py\n        ├── models.py\n        ├── static_templates\n        │   └── examples\n        │       └── defines.js\n        └── urls.py\n\n\nYour defines/model classes might look like this:\n\n```python\nclass ExampleModel(Defines, models.Model):\n\n    DEFINE1 = 'D1'\n    DEFINE2 = 'D2'\n    DEFINE3 = 'D3'\n    DEFINES = (\n        (DEFINE1, 'Define 1'),\n        (DEFINE2, 'Define 2'),\n        (DEFINE3, 'Define 3')\n    )\n\n    define_field = models.CharField(choices=DEFINES, max_length=2)\n```\n\nAnd your defines.js template might look like this:\n\n```js+django\n{% defines_to_js modules=\"examples.models\" %}\n```\n\nIf someone wanted to use your defines template to generate a JavaScript version of your Python\nclass their settings file might look like this:\n\n```python\nSTATIC_TEMPLATES = {\n    'templates': [\n        'examples/defines.js'\n    ]\n}\n```\n\n\nAnd then of course they would call `renderstatic` before `collectstatic`:\n\n```shell\n$\u003e ./manage.py renderstatic\n$\u003e ./manage.py collectstatic\n```\n\nThis would create the following file::\n\n    .\n    └── examples\n        └── static\n            └── examples\n                └── defines.js\n\nWhich would look like this:\n\n```javascript\nconst defines = {\n    ExampleModel: {\n        DEFINE1: \"D1\",\n        DEFINE2: \"D2\",\n        DEFINE3: \"D3\",\n        DEFINES: [[\"D1\", \"Define 1\"], [\"D2\", \"Define 2\"], [\"D3\", \"Define 3\"]]\n    }\n};\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbckohan%2Fdjango-render-static","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbckohan%2Fdjango-render-static","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbckohan%2Fdjango-render-static/lists"}