Package: django-render-static
django-render-staticUse Django’s template engines to render static files that are collected
during thecollectstaticroutine and likely served above Django at runtime.
Files rendered by django-render-static are immediately available to participate
in the normal static file collection pipeline.For example, a frequently occurring pattern that violates theDRY principleis the presence of defines, or enum like structures in server side Python code that are simply replicated in client
side JavaScript. Another example might be rebuilding Django URLs from arguments in aSingle Page Application.
Single-sourcing these structures by transpiling client side code from the server side code keeps the stack bone DRY.django-render-staticincludes Python to Javascript transpilers for:Django’sreversefunction (urls_to_js)PEP 435 style Python enumerations (enums_to_js)Plain data define-like structures in Python classes and modules
(defines_to_js)Transpilation is extremely flexible and may be customized by using override blocks or extending the provided
transpilers.django-render-staticalso formalizes the concept of a package-time or deployment-time
static file rendering step. It piggybacks off the existing templating engines and configurations
and should therefore be familiar to Django developers. It supports both standard Django templating
and Jinja templates and allows contexts to be specified in python, json or YAML.You can report bugs and discuss features on theissues page.Contributionsare
encouraged!Full documentation at read the docs.InstallationClone django-render-static fromGitHubor install a release offPyPI:pipinstalldjango-render-staticAdd ‘render_static’ to yourINSTALLED_APPS:INSTALLED_APPS=['render_static',]Add aSTATIC_TEMPLATESconfiguration directive to your settings file:STATIC_TEMPLATES={'templates':[('path/to/template':,{'context'{'variable':'value'})]}Runrenderstaticpreceding every run ofcollectstatic:$>manage.pyrenderstatic$>manage.pycollectstaticUsageTranspiling Model Field ChoicesYou have an app with a model with a character field that has several valid choices defined in an
enumeration type way, and you’d like to export those defines to JavaScript. You’d like to include
a template for other’s using your app to use to generate a defines.js file. Say your app structure
looks like this:.
└── examples
    ├── __init__.py
    ├── apps.py
    ├── defines.py
    ├── models.py
    ├── static_templates
    │   └── examples
    │       └── defines.js
    └── urls.pyYour defines/model classes might look like this:classExampleModel(Defines,models.Model):DEFINE1='D1'DEFINE2='D2'DEFINE3='D3'DEFINES=((DEFINE1,'Define 1'),(DEFINE2,'Define 2'),(DEFINE3,'Define 3'))define_field=models.CharField(choices=DEFINES,max_length=2)And your defines.js template might look like this:{%defines_to_jsmodules="examples.models"%}If someone wanted to use your defines template to generate a JavaScript version of your Python
class their settings file might look like this:STATIC_TEMPLATES={'templates':['examples/defines.js']}And then of course they would callrenderstaticbeforecollectstatic:$>./manage.pyrenderstatic$>./manage.pycollectstaticThis would create the following file:.
└── examples
    └── static
        └── examples
            └── defines.jsWhich would look like this:constdefines={ExampleModel:{DEFINE1:"D1",DEFINE2:"D2",DEFINE3:"D3",DEFINES:[["D1","Define 1"],["D2","Define 2"],["D3","Define 3"]]}};Transpiling EnumerationsSay instead of the usual choices tuple you’re using PEP 435 style python
enumerations as model fields usingdjango-enumandenum-properties. For example
we might define a simple color enumeration like so:fromdjango.dbimportmodelsfromdjango_enumimportEnumField,TextChoicesfromenum_propertiesimportp,sclassExampleModel(models.Model):classColor(TextChoices,s('rgb'),s('hex',case_fold=True)):# name   value   label       rgb       hexRED='R','Red',(1,0,0),'ff0000'GREEN='G','Green',(0,1,0),'00ff00'BLUE='B','Blue',(0,0,1),'0000ff'color=EnumField(Color,null=True,default=None)If we define an enum.js template that looks like this:{%enums_to_jsenums="examples.models.ExampleModel.Color"%}It will contain a javascript class transpilation of the Color enum that looks
like this:classColor{staticRED=newColor("R","RED","Red",[1,0,0],"ff0000");staticGREEN=newColor("G","GREEN","Green",[0,1,0],"00ff00");staticBLUE=newColor("B","BLUE","Blue",[0,0,1],"0000ff");constructor(value,name,label,rgb,hex){this.value=value;this.name=name;this.label=label;this.rgb=rgb;this.hex=hex;}toString(){returnthis.value;}staticget(value){switch(value){case"R":returnColor.RED;case"G":returnColor.GREEN;case"B":returnColor.BLUE;}thrownewTypeError(`No Color enumeration maps to value${value}`);}static[Symbol.iterator](){return[Color.RED,Color.GREEN,Color.BLUE][Symbol.iterator]();}}We can now use our enumeration like so:Color.BLUE===Color.get('B');for(constcolorofColor){console.log(color);}Transpiling URL reversalYou’d like to be able to call something likereverseon path names from your client JavaScript
code the same way you do from Python Django code.Your settings file might look like:STATIC_TEMPLATES={'ENGINES':[{'BACKEND':'render_static.backends.StaticDjangoTemplates','OPTIONS':{'loaders':[('render_static.loaders.StaticLocMemLoader',{'urls.js':'{% urls_to_js %}'})]},}],'templates':['urls.js']}Then callrenderstaticbeforecollectstatic:$> ./manage.py renderstatic
$> ./manage.py collectstaticIf your root urls.py looks like this:fromdjango.contribimportadminfromdjango.urlsimportpathfrom.viewsimportMyViewurlpatterns=[path('admin/',admin.site.urls),path('simple',MyView.as_view(),name='simple'),path('simple/<int:arg1>',MyView.as_view(),name='simple'),path('different/<int:arg1>/<str:arg2>',MyView.as_view(),name='different'),]So you can now fetch paths like this, in a way that is roughly API-equivalent
to Django’sreversefunction:import{URLResolver}from'/static/urls.js';consturls=newURLResolver();// /different/143/emmaurls.reverse('different',{kwargs:{'arg1':143,'arg2':'emma'}});// reverse also supports query parameters// /different/143/emma?intarg=0&listarg=A&listarg=B&listarg=Curls.reverse('different',{kwargs:{arg1:143,arg2:'emma'},query:{intarg:0,listarg:['A','B','C']}});URLGenerationFailed Exceptions & PlaceholdersIf you encounter aURLGenerationFailedexception 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:fromrender_static.placeholdersimportregister_variable_placeholderapp_name='year_app'urlpatterns=[re_path(r'^fetch/(?P<year>\d{4})/$',YearView.as_view(),name='fetch_year')]register_variable_placeholder('year',2000,app_name=app_name)Users 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!):fromdjango.urls.convertersimportregister_converterclassYearConverter:regex='[0-9]{4}'placeholder=2000# this attribute is used by `url_to_js` to reverse pathsdefto_python(self,value):returnint(value)defto_url(self,value):returnstr(value)register_converter(YearConverter,'year')urlpatterns=[path('fetch/<year:year>',YearView.as_view(),name='fetch_year')]