- Notifications
You must be signed in to change notification settings - Fork340
Transparently use webpack with django
License
MIT, Unknown licenses found
Licenses found
django-webpack/django-webpack-loader
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
Integrate Webpack bundles in Django templates by using a simple template tag:
{%loadrender_bundlefromwebpack_loader%}<html> <head>{%render_bundle'main''css'%} </head></html>
Behind the scenes, Django Webpack Loader consumes astats file generated bywebpack-bundle-tracker and lets you use the generated bundles in Django.
Achangelog is available.
Generally, Python, Django, and Node LTS releases will be supported until EOL. Checktests/tox.ini for details.
Versions not listed intests/tox.ini may still work, but maintainers will not test them, nor solve issues with them.
Examples below are in Webpack 5.
npm install --save-dev webpack-bundle-trackerpip install django-webpack-loader
For kick-starting a full example project with opinionated development and production settings, you can check thedjango-react-boilerplate. For a more flexible configuration, keep reading.
Before configuringdjango-webpack-loader, let's first configure what's necessary onwebpack-bundle-tracker side. Update your Webpack configuration file (it's usually onwebpack.config.js in the project root). Make sure your file looks like this (adapt to your needs):
constpath=require("path");constwebpack=require("webpack");constBundleTracker=require("webpack-bundle-tracker");module.exports={context:__dirname,entry:"./assets/js/index",output:{path:path.resolve(__dirname,"assets/webpack_bundles/"),publicPath:"auto",// necessary for CDNs/S3/blob storagesfilename:"[name]-[contenthash].js",},plugins:[newBundleTracker({path:__dirname,filename:"webpack-stats.json"}),],};
The configuration above expects theindex.js (the app entrypoint file) to live inside the/assets/js/ directory (this guide going forward will assume that all frontend related files are placed inside the/assets/ directory, with the different kinds of files arranged within its subdirectories).
The generated compiled files will be placed inside the/assets/webpack_bundles/ directory and thestats file with the information regarding the bundles and assets (webpack-stats.json) will be stored in the project root. You may addwebpack-stats.json to your.gitignore.
First of all, addwebpack_loader toINSTALLED_APPS.
INSTALLED_APPS= ( ...'webpack_loader', ...)
Below is the recommended setup for the Django settings file when usingdjango-webpack-loader.
STATICFILES_DIRS= (os.path.join(BASE_DIR,'assets'),)WEBPACK_LOADER= {'DEFAULT': {'BUNDLE_DIR_NAME':'webpack_bundles/','CACHE':notDEBUG,'STATS_FILE':os.path.join(BASE_DIR,'webpack-stats.json'),'POLL_INTERVAL':0.1,'IGNORE': [r'.+\.hot-update.js',r'.+\.map'], }}
Note that you must set the path where you're keeping your static assets and Webpack bundles inSTATICFILES_DIRS.
For that setup, we're using theDEBUG variable provided by Django. Since in a production environment (DEBUG = False) the assets files won't constantly change, we can safely cache the results (CACHE=True) and optimize our flow, asdjango-webpack-loader will read the stats file only once and store the assets paths in memory. IfCACHE=False, we'll always read the stats file to get the assets paths.
TheSTATS_FILE parameter represents the output file produced bywebpack-bundle-tracker. Since in the Webpack configuration file we've named itwebpack-stats.json and stored it on the project root, we must replicate that setting on the backend side.
During development, the stats file will change often, therefore we want to always poll for its updated version (every 0.1s, as defined onPOLL_INTERVAL).
⚠️ In production (DEBUG=False), we'll only fetch the stats file once, soPOLL_INTERVALis ignored.
IGNORE is a list of regular expressions. If a file generated by Webpack matches one of the expressions, the file will not be included in the template.
Using Webpack, you must generate the frontend bundle along with the stats file usingwebpack-bundle-tracker before usingdjango-webpack-loader in Django templates. Note you'll probably want different configurations in development vs. production. The pipeline should look like this:
flowchart TD A("Run webpack") A --> |CSS, JS, imgs, fonts| B(Collect compiled assets) A --> |webpack-stats.json| C(Read on Django templates)In development, we can simply do:
# in one shellnpx webpack --mode=development --watch# in another shellpython manage.py runserver
Checkthe full example for development here.
Aditionally, hot reload is available through a specific config. Checkthis section.
⚠️ For compiling and serving the frontend assets in production, checkthis section.
In order to render the frontend code into the Django templates, we use therender_bundle template tag.
Its behavior is to accept a string with the name of an entrypoint from the stats file (in our case, we're usingmain, which isthe default) and it'll proceed to include all files under that entrypoint. You can read more about the entrypoints concepthere.
⚠️ You can also check an example on how to use multipleentryvalueshere.
Below is the basic usage forrender_bundle within a template:
{%loadrender_bundlefromwebpack_loader%}<html> <head>{%render_bundle'main''css'%} </head></html>
That will render the proper<script> and<link> tags needed in your template.
To run tests whererender_bundle shows up, since we don't havewebpack-bundle-tracker at that point to generate the stats file, the calls to render the bundle will fail. The solution is to use theFakeWebpackLoader in your test settings:
WEBPACK_LOADER['DEFAULT']['LOADER_CLASS']='webpack_loader.loaders.FakeWebpackLoader'
The recommended apporach is to have a production pipeline that generates the frontend bundle along with the stats file during thedeployment phase. We recommend keeping the generated bundles and the stats file outside the version control. In other words, addwebpack-stats.json andassets/webpack_bundles/ to your.gitignore.
Assuming static files is properly configured using Django built-ins or something likedjango-storages, a simple production deployment can use Django's owncollectstatic. Remember the Django settings values ofSTATICFILES_DIRS,BUNDLE_DIR_NAME,STATS_FILE, and Webpack'soutput.path must all be compatible:
// webpack.config.jsmodule.exports={// ...context:__dirname,output:{// Emit bundle files at "assets/webpack_bundles/":path:path.resolve(__dirname,"assets/webpack_bundles/"),publicPath:"auto",// necessary for CDNs/S3/blob storagesfilename:"[name]-[contenthash].js",},plugins:[// Emit 'webpack-stats.json' in project root for Django to find it:newBundleTracker({path:__dirname,filename:"webpack-stats.json"}),],};
# app/settings.pyBASE_DIR= ...# set to project rootSTATICFILES_DIRS= (# make Django collect all "assets/" and "assets/webpack_bundles"# to be served at "my-static-url.com/asset-name.png"# and "my-static-url.com/webpack_bundles/main.js"os.path.join(BASE_DIR,'assets'),)WEBPACK_LOADER= {'DEFAULT': {# Bundle directory, like in "my-static-url.com/webpack_bundles/main.js":'BUNDLE_DIR_NAME':'webpack_bundles/',# Absolute path to where 'webpack-stats.json' is in Django project root:'STATS_FILE':os.path.join(BASE_DIR,'webpack-stats.json'),# ... }}
In your deployment script, you must first run your Webpack build in production-mode, before callingcollectstatic:
NODE_ENV=production webpack --progress --bail --mode=productionpython manage.py collectstatic --noinputThis means we're building the assets and, since we havewebpack-bundle-tracker in our Webpack building pipeline, thewebpack-stats.json stats file is also populated. If you followed the default configuration, thewebpack-stats.json will be at Django's project root (BASE_DIR) and therender_bundle template tag will be able to use it.
However, production usage for this package isfairly flexible, as the entire Django-Webpack integration depends only on thewebpack-stats.json file.
⚠️ Heroku is one platform that automatically runs collectstatic for you, so you need to set theDISABLE_COLLECTSTATIC=1environment var and manually run collectstatic after running Webpack. In Heroku, this is achieved with apost_compilehook. Here'san example.
Hot reload (Hot Module Replacement) is critical for a improving the development workflow. In case you wish to enable for your project, please check outthis example, in particular howwebpack.config.js is configured. The key is to set thepublicPath anddevServer.
In case you wish to useDynamic Imports, please check outthis example, in particular howwebpack.config.js is configured.
Checkwebpack-bundle-trackerREADME for all supported options, such as relative paths, integrity hashes, timestamp logging, etc.
Set those extra settings inside like this:
WEBPACK_LOADER= {'DEFAULT': {# settings go here }
TIMEOUTis the number of seconds webpack_loader should wait for Webpack to finish compiling before raising an exception.0,Noneor leaving the value out of settings disables timeoutsINTEGRITYis a flag enablingSubresource Integrity on rendered<script>and<link>tags. Integrity hash is fetched from the stats ofBundleTrackerPlugin. Theconfiguration optionintegrity: trueis required.CROSSORIGIN: If you use theintegrityattribute in your tags and you load your webpack generated assets from another origin (that is not the samehost:portas the one you load the webpage from), you can configure theCROSSORIGINconfiguration option. The default value is''(empty string), where an emptycrossoriginattribute will be emitted when necessary. Valid values are:''(empty string),'anonymous'(functionally same as the empty string) anduse-credentials. For an explanation, seehttps://shubhamjain.co/2018/09/08/subresource-integrity-crossorigin/. A typical case for this scenario is when you develop locally and your webpack-dev-server runs with hot-reload on a local host/port other than that of django'srunserver.CSP_NONCE: Automatically generate nonces for rendered bundles fromdjango-csp. DefaultFalse. Set this toTrueif you usedjango-cspand and'strict-dynamic'CSP mode.LOADER_CLASSis the fully qualified name of a python class as a string that holds the custom Webpack loader. This is where behavior can be customized as to how the stats file is loaded. Examples include loading the stats file from a database, cache, external URL, etc. For convenience,webpack_loader.loaders.WebpackLoadercan be extended. Theload_assetsmethod is likely where custom behavior will be added. This should return the stats file as an object.
Here's a simple example of loading from an external URL:
importrequestsfromwebpack_loader.loadersimportWebpackLoaderclassExternalWebpackLoader(WebpackLoader):defload_assets(self):url=self.config['STATS_URL']returnrequests.get(url).json()
SKIP_COMMON_CHUNKS(Default:False) is a flag which prevents already generated chunks from being included again in the same page. This should only happen if you use more than one entrypoint per Django template (multiplerender_bundlecalls). By enabling this, you can get the same default behavior of theHtmlWebpackPlugin. The same caveats apply as when usingskip_common_chunksonrender_bundle, see that section below for more details.
render_bundle also takes a second argument which can be a file extension to match. This is useful when you want to render different types for files in separately. For example, to render CSS in head and JS at bottom we can do something like this:
{%loadrender_bundlefromwebpack_loader%}<html> <head>{%render_bundle'main''css'%} </head> <body> ....{%render_bundle'main''js'%} </body></head>
Theis_preload=True option in therender_bundle template tag can be used to addrel="preload" link tags:
{%loadrender_bundlefromwebpack_loader%}<html> <head>{%render_bundle'main''css'is_preload=True%}{%render_bundle'main''js'is_preload=True%}{%render_bundle'main''css'%} </head> <body>{%render_bundle'main''js'%} </body></html>
webpack_static template tag provides facilities to load static assets managed by Webpack in Django templates. It is like Django's built instatic tag but for Webpack assets instead.
In the example below,logo.png can be any static asset shipped with any npm package:
{%loadwebpack_staticfromwebpack_loader%}<!-- render full public path of logo.png--><imgsrc="{% webpack_static 'logo.png' %}"/>
The public path is based onwebpack.config.jsoutput.publicPath.
Please note that this approach will use the original asset file, and not a post-processed one from the Webpack pipeline, in case that file had gone through such flow (e.g.: You've imported an image on the React side and used it there, the file used within the React components will probably have a hash string on its name, etc. This processed file will be different than the one you'll grab withwebpack_static).
You can use the parameterskip_common_chunks=True orskip_common_chunks=False to override the globalSKIP_COMMON_CHUNKS setting for a specific bundle.
In order for this option to work,django-webpack-loader requires therequest object to be in the template context. Therequest object is passed by default via thedjango.template.context_processors.request context processor, so make sure you have that.
If you don't haverequest in the context for some reason (e.g. usingTemplate.render orrender_to_string directly without passing the request), you'll get warnings on the console and the common chunks will remain duplicated.
Thesuffix option can be used to append a string at the end of the file URL. For instance, it can be used if your Webpack configuration emits compressed.gz files.
{%loadrender_bundlefromwebpack_loader%}<html> <head> <metacharset="UTF-8"> <title>Example</title>{%render_bundle'main''css'%} </head> <body>{%render_bundle'main''js'suffix='.gz'%} </body></html>
django-webpack-loader also supports multiple Webpack configurations. Assuming you have different Webpack configs, each with a differentoutput.path, the following configuration defines 2 Webpack stats files in settings and uses theconfig argument in the template tags to influence which stats file to load the bundles from:
WEBPACK_LOADER= {'DEFAULT': {'BUNDLE_DIR_NAME':'bundles/','STATS_FILE':os.path.join(BASE_DIR,'webpack-stats.json'), },'DASHBOARD': {'BUNDLE_DIR_NAME':'dashboard_bundles/','STATS_FILE':os.path.join(BASE_DIR,'webpack-stats-dashboard.json'), }}
{%loadrender_bundlefromwebpack_loader%}<html> <body> ....{%render_bundle'main''js''DEFAULT'%}{%render_bundle'main''js''DASHBOARD'%}<!-- or render all files from a bundle-->{%render_bundle'main'config='DASHBOARD'%}<!-- the following tags do the same thing-->{%render_bundle'main''css''DASHBOARD'%}{%render_bundle'main'extension='css'config='DASHBOARD'%}{%render_bundle'main'config='DASHBOARD'extension='css'%}<!-- add some extra attributes to the tag-->{%render_bundle'main''js''DEFAULT'attrs='async charset="UTF-8"'%} </body></head>
If you need the URL to an asset without the HTML tags, theget_files template tag can be used. A common use case is specifying the URL to a custom CSS file for a Javascript plugin.
get_files works exactly likerender_bundle except it returns a list of matching files and lets you assign the list to a custom template variable.
Each object in the returned list has 2 properties:
name, which is the name of a chunk from the stats file;url, which can be:- The
publicPathif the asset has one; - The
pathto the asset in the static files storage, if the asset doesn't have apublicPath.
For example:
{%loadget_filesfromwebpack_loader%}{%get_files'editor''css'aseditor_css_files%}CKEDITOR.config.contentsCss = '{{ editor_css_files.0.url }}';<!-- or list down name and url for every file--><ul>{%forcss_fileineditor_css_files%} <li>{{ css_file.name }} : {{ css_file.url }}</li>{%endfor%}</ul>
If you need to output your assets in a jinja template, we provide a Jinja2 extension that's compatible withdjango-jinja.
To install the extension, add it to theTEMPLATES configuration in the["OPTIONS"]["extension"] list.
fromdjango_jinja.builtinsimportDEFAULT_EXTENSIONSTEMPLATES= [ {"BACKEND":"django_jinja.backend.Jinja2","OPTIONS": {"extensions":DEFAULT_EXTENSIONS+ ["webpack_loader.contrib.jinja2ext.WebpackExtension", ], } }]
Then in your base jinja template, do:
{{ render_bundle('main') }}Note:get_files in Jinja2 is calledwebpack_get_files.
In order to usedjango-webpack-loader>=1.0.0, you must ensure thatwebpack-bundle-tracker@1.0.0 is being used on the JavaScript side. It's recommended that you always keep at least minor version parity across both packages for full compatibility.
This project includes a Makefile that provides several useful commands for building, installing, and publishing the project. Please feel free to open PRs or create issues!
clean: Removes generated files and directories.build: Cleans the project and builds the distribution packages.test: Run the tests.install: Installs the project's build dependencies. Will initialize a virtual environment if one does not exist.publish: Builds the distribution packages and publishes them to the specified repository.register: Registers the package on the specified repository.
To execute a command, runmake <command> in the project's root directory.
ENV: The name of the virtual environment. (Default:venv)REPOSITORY: The repository to publish the distribution packages to. (Default:pypi)
Django Webpack Loader was originally created byOwais Lone and received contributions from more than 50 developers since its inception, as well as many others who assisted with issues, comments, articles, talks, etc. Thanks for everyone who's been part of Django Webpack Loader community!
This project is currently maintained byVinta Software and is used in products of Vinta's clients. We are always looking for exciting work, so if you need any commercial support, feel free to get in touch:contact@vinta.com.br
About
Transparently use webpack with django
Topics
Resources
License
MIT, Unknown licenses found
Licenses found
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Packages0
Uh oh!
There was an error while loading.Please reload this page.