We're sorry but this page doesn't work properly without JavaScript enabled. Please enable it to continue.
Feedback

Happy Asset Deployments with Webpack & Django

00:00

Formal Metadata

Title
Happy Asset Deployments with Webpack & Django
Title of Series
Part Number
38
Number of Parts
52
Author
License
CC Attribution - ShareAlike 3.0 Unported:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this
Identifiers
Publisher
Release Date
Language

Content Metadata

Subject Area
Genre
Abstract
Webpack What is it? What does it do? Source transformations Output Why Djangos collectstatic is not up to the job? Must run after deployment Doesn't do all the things Slow Integration on both sides Webpack bundle tracker to output build stats Django Webpack bundle loader to read those files How to render links in templates.
13
Thumbnail
42:32
Figurate numberPoint (geometry)Software developerProduct (business)Identity managementProcess (computing)HypermediaGroup actionCartesian coordinate systemDependent and independent variablesDenial-of-service attackQuicksortRoutingSign (mathematics)Computer fileMathematicsSoftware testingWeightModal logicPhysical systemPosition operatorWordIntegrated development environmentBuildingDecision theoryData structureSpacetimeBridging (networking)Multiplication signWebsiteMedical imagingEndliche ModelltheorieWeb pageVirtual machineVideo gameMetropolitan area networkMereologyService (economics)Control flowDialectMathematical analysisProjective planeRevision controlLimit (category theory)Wrapper (data mining)ACIDSlide ruleVelocityFluid staticsVapor barrierRight angleGeometryLevel (video gaming)Instance (computer science)State of matterSoftwareSound effectSpreadsheetRow (database)Data managementBitFront and back endsWeb 2.0ChainBlogOnline helpCASE <Informatik>CodeAttribute grammarConfidence intervalScaling (geometry)Presentation of a groupArithmetic meanSingle-precision floating-point formatComputer animation
BuildingMedical imagingCodeProcess (computing)QuicksortFactory (trading post)ChainPhysical systemGraph (mathematics)Web pageCartesian coordinate systemComputer fileScripting language1 (number)Endliche ModelltheorieFiber bundleWeb browserFront and back endsMobile appComputer animation
Fiber bundleGoodness of fitComputer fileContent (media)Hash functionMobile appPlug-in (computing)Fluid staticsRevision controlDifferent (Kate Ryan album)Module (mathematics)Category of beingNamespaceMessage passingCartesian coordinate systemConfiguration spaceTemplate (C++)CodeBootingProjective planeFlagFront and back endsDirectory serviceData managementInterface (computing)Point (geometry)Function (mathematics)Binary codeStatisticsQuicksortWebsiteMultiplicationSubject indexingMathematicsBootstrap aggregatingMultiplication signFuzzy logicRegulator geneStaff (military)Set (mathematics)Endliche ModelltheoriePlotterAdditionInsertion lossOrder (biology)File systemFood energyMereologyCASE <Informatik>BuildingTheory of relativityDemoscenePower (physics)TheoryIterationSampling (statistics)DivisorForm (programming)Right angleReduction of orderSheaf (mathematics)GeometrySpiralFlow separationComputer animation
Server (computing)Directory serviceIntegrated development environmentComputer fileSoftware testingMusical ensembleProjective planeQuicksortSoftwareSet (mathematics)Visualization (computer graphics)Product (business)Mobile WebHypermediaAuthorizationEndliche ModelltheorieTraffic reportingOperator (mathematics)MathematicsPie chartFiber bundleLevel (video gaming)Service (economics)Flow separation1 (number)Process (computing)Subject indexingDemosceneInformation securityPhysical lawMultiplication signContent (media)Source codePoint (geometry)Graph (mathematics)AreaPatch (Unix)DataflowView (database)Cellular automatonMobile appVolumenvisualisierungTemplate (C++)BootingHash functionLink (knot theory)BitDifferent (Kate Ryan album)Physical systemStatisticsCartesian coordinate systemMaxima and minimaSoftware developerData managementScripting languageBuildingFluid staticsPunched cardError messagePerspective (visual)Web browserComputer animation
Computer fileIntegrated development environmentFiber bundleTemplate (C++)Process (computing)Virtual machineData Encryption StandardSimilarity (geometry)DatabaseMultiplication signRight angleSoftware developerView (database)StatisticsDependent and independent variablesPoint (geometry)Variable (mathematics)Cartesian coordinate systemCondition numberQuicksortWebsiteRevision controlContent (media)BuildingDisk read-and-write headPhysical systemWeb 2.0Food energyData dictionaryFigurate numberMultiplicationServer (computing)ResultantCASE <Informatik>Sound effectFlow separationMeasurementLiquidHash functionConfiguration spaceBitSet (mathematics)Mobile appConfiguration managementReverse engineeringWeb pageBootingNumbering scheme1 (number)Natural numberProduct (business)Keyboard shortcutDifferent (Kate Ryan album)Lecture/Conference
Point (geometry)Computer animationJSONXML
Transcript: English(auto-generated)
If you're on the Slack team for DjangoCon, I made a channel in case you want to kind of chat afterwards or whenever, and it's called Confident Deploys, so join that if you want to. I'll probably publish my slides there and Twitter as well. Also, tomorrow afternoon, Jack McCoy is talking about React and Django, so this talk is basically
how you might get that set up, but his talk is really like why you might want to use React with your Django talk. So I definitely suggest going there. Before we begin, I have a small secret. Last year, we were beginning to build a new React application at work. We had just hired a first front-end developer.
It was on me to figure out how to make our Django application work with Webpack because he was gung ho about using Webpack, and we knew that it was probably the way to work, the way to move forward with our application. So the ideas that I'm gonna present are not my ideas. The code I'm gonna present is not my code.
I fortunately stumbled across Awais Sloan, who's a developer from India, and his blog and a couple of the packages that I'm gonna show have been super helpful. Without his work, I wouldn't be here talking to you today. So as a brief overview, what are we gonna talk about? We're gonna start with some philosophy,
we'll get into specifics, kind of how to implement an asset deployment workflow around the philosophy, and we'll finally finish with a little bit of philosophy. So what do I mean by confident asset deployments? Sort of work from right to left. Deployments are just changes,
changes you wanna introduce to your application. It could be to production, it could be to staging, it could be to testing, and hopefully, you're putting out new features that get in front of your users. But obviously, we're humans, we're also deploying fixes as well. By assets, I mean JavaScript,
CSS, and images. And in this case, I sort of mean first party images, images that you include with your site, not necessarily images uploaded by your users. Content, what are some attributes of deployments that I think can help us become confident
that they're going to work? Well, the first is that they need to be easy to begin. You should be able to do it from your local machine. Ideally, though this talk is not about it, you should do it maybe from chat ops, from Slack or however you communicate. But if they require you to be logged in to some secret system somewhere,
that's not great because you're less likely to do that. They need to be reversible. What I mean by reversible is I never like to edit resources, specifically my assets. If I'm going to deploy a change, I want to create completely new assets because no matter the scale that you work at,
things will go south at some point and you want to be able to kind of back out of that deployment and get back to a known good state quickly. They need to be fast. A slow process, a slow deployment process necessarily means that it just introduces more time in which things might break. So if you can deploy quickly,
there's just fewer things to go wrong. It has to be automated. Automated deployments can be improved through code and not necessarily any human sort of relearning your process. If we make improvements to our deployment,
we should do so through code and through a PR, something that can be reviewed instead of like a Google spreadsheet that people aren't going to read. It needs to include as few third parties as possible. I don't believe that we should depend on GitHub being up or PyPy or NPM or any third party.
We should be able to deploy whenever we want, however we want. And it needs to be tested. I'm not going to get into how we test or what we test, but if you're delivering software that users depend on, whether they pay for it or however you make money,
you need to be able to test before things get to production that things are gonna work the way you expect. So you should probably test the backend, you should probably test the frontend if it matters to you. Why are we talking about this now? It's 2016, Django is 10 years old, the web is even older.
You'd think that sort of asset deployments are kind of a fixed problem. Well, the tool chains have improved, and specifically on the JavaScript side of things. There are many different tools these days out there for sort of bundling up your applications
and minifying them. What I mean by minify is basically reduce all the white space in your assets so that the requests that you do serve are as small as possible. So these tool chains have gotten a lot better in the last couple of years. Communities move at different speeds.
If anybody is somewhat involved with the JavaScript community and all the sort of sub-project communities, you know that they tend to move very, very quickly. And that's okay. They move much more quickly than Python, they move much more quickly than Django. It's just kind of a younger community.
They're in a very sort of fast growth stage. But as web developers, we wanna use the best tools regardless of what community they come from to accomplish the needs that our website needs. We're ultimately tasked with delivering value to our users, to our stakeholders. So whatever tool a community comes from,
we need to be able to use it. So as backing developers, we need to adapt and sort of build the best tooling and infrastructure that we can to deliver the best sites and experiences for our users. What this means is that we need to build bridges and not wrappers.
What I mean by a wrapper is maybe a high level Django command, management command that beneath the sheets sort of runs third party tools from communities. For instance, collectstatic. You can configure collectstatic, which is a Django management command that ties into the static files application.
That can be, it can be sort of coerced to, when you run it, to eventually post-process your CSS, to minify it, to post-process your JIT, your JavaScript to sort of concatenate all your JavaScript files into a single file and minify it. But I'm gonna take the stance that this is the wrong way to do things.
Because the tool chains have improved, I think we need to work more towards building bridges between your system for building your front-end applications and Django. And so, rest of this talk, I kind of want to demonstrate how we can do that. So, what does this look like from a code perspective?
I've gotten kind of a third of the way through the talk and I haven't mentioned Webpack at all. So, Webpack is a system for modeling all of your modules, specifically on the front-end. So, these days, we can build our front-end with a great, great mini tools and tool chains.
So, this picture, I think, is actually great. This is from webpack.github.io and it's the picture you see when you first start learning about Webpack. So, we can build our front-end, we can build our JavaScript in sort of plain JavaScript, but we can also maybe write it in CoffeeScript, or we can use JSX if we're building React apps.
And it depends, you know, for a particular JavaScript component, it might depend on having some CSS on the page. You might write this CSS in LESS or SAS, or just raw CSS. And those CSS files might require some images on the page as well.
So, all of these dependencies sort of make up our front-end applications, whether we want to or not. So, Webpack's goal is to sort of build the graph of how all these things interrelate and then produce, for us, a few JavaScript files, much fewer, many fewer JavaScript files,
maybe a CSS file, and images. So, through this process, sort of Webpack creates bundles that include all of your code and you can sort of transform it, take it from CoffeeScript or SAS to actual JavaScript and CSS that your browsers understand.
So, how do we get installed with Webpack? Well, we empty install it with dash S. So, dash S is gonna save Webpack in your package.json file, and dash G will sort of put the Webpack binary into your path.
We're also gonna install Webpack bundle tracker, which is a package written by OAS that I mentioned before, and we're gonna see how we use that very quickly. So, Webpack is sort of driven by a webpack.config.js file. You can think of this as your settings.py file for Webpack. You basically write this once and it drives Webpack.
It tells Webpack exactly how to build the bundle, where to find your actual code, and how to bundle it up, any plugins you might need. So, this particular code snippet that I'm showing you is about as basic as it gets. Anything that you'll actually use for your site
is gonna be more complex. The Webpack tends to move quickly. The plugins that people write for it move quickly. So, anything that I really show you is gonna be wrong in about six months. But there's many tutorials out there. There's lots of sort of bootstrapped packages you can learn, you can find, and eventually find a Webpack config file that works for you
but the basics involve a couple of different points, different pieces of data, and so we're focusing kind of on the module.exports. That's kind of a fancy way that JavaScript knows how to essentially like namespace outputs from different files. So, the first important thing
is the entry property of this object. The entry property tells Webpack essentially where to start looking for your code and you can give it a name, in this case app. So, the property is app and it points to a index.js. I'm not gonna show you this file. This file is essentially where you begin importing
all of your actual front-end code. You can import your JavaScript files along with CSS, which looks weird in a JavaScript file, but trust me, you can make it work. The next section, the next property is output. Where should Webpack put all these files
that it's gonna produce? Path.resolve is kind of like os.path in Python and we're basically just, we wanna put it into the bundles directory of the static directory that's alongside this file. And we're gonna give it a name. We're gonna, essentially the,
we wanna give it a specific name. So, the little name token here will be the name of the entry point. So, you could have multiple entry points. In this case, we're gonna produce a file called app-something. That something is the hash. So, any time Webpack rebuilds the bundle, if your code changes or any third-party code changes,
this hash is going to change because the hash is essentially a unique token of all the contents of your file. So, this is how we can start to begin to build basically immutable assets. Every different bundle that we build is gonna have a unique hash on it.
And we can add plugins to Webpack. Basically, plugins that can sort of inject code before going and after the build process. So, the one thing I'm gonna show you here is a bundle tracker. So, bundle tracker just needs to be given a file name. And we're going to write essentially the statistics
of how the build is proceeding to this webpack-stats.json file. I think in the React Django talk later tomorrow, Jack will talk more about the loaders. But fortunately, I don't have enough time. And that's where lots of the magic
that Webpack can do comes in. So, to build the bundle, we just need to run Webpack and pass it the config flag and pass this file as the config flag. What this does is build the bundle. And it also writes out this, because we defined the bundle tracker plugin,
it writes out the webpack-stats.json file. That file is just a small, tiny snippet of JSON. And it basically has the status. During the build, the status is building. But when it's done and it finished successfully,
the status says done. And it also writes out chunks. So, in this example, the only chunk it wrote out was the app, basically the name of our entry point. And so, it has the name of the file. And like I said, it's app-some-hash. And it also has basically the full path
that it wrote the bundle to. So, this encapsulates the entire knowledge of what Webpack did. It's not the bundle itself. I'm not gonna show you the bundle, because the bundle is just a bunch of JavaScript and whatever, and it really depends on your site. But this encapsulates what Webpack did,
the actual assets that it produced. And if you get this far, congratulations, because it can be difficult getting to the point of actually making Webpack do what you want it to. But I promise it's worth it. So, what are we kind of dealing with on the file system?
Where do things live? So, manage.py is a highlight. It's our interface into Django management commands. Next to it is this webpack.config.js file. When we run Webpack and execute a build, it changes webpack-stats.json.
It changes the content of that file, but the name of the file just stays the same. And then next to it, we have our static directory. And our file that contains our JavaScript is index.js, and it builds the bundle into the bundles. I would suggest ignoring the bundles directory.
It's usually good practice with version control systems to not version control build artifacts. And then in the app, slash app directory, that's the rest of your Django project. So, your erls.py, all of your applications
that actually sort of implement your backend logic. So, we actually want to get this, we want to reference this file in our templates because we've just built our assets. We need to reference them in our templates. So, how do we do that?
One way to do that is to use kind of the static files, the static tag. And everybody's probably seen this. This is how, you know, for a very long time, this is how we referenced front end assets. So, behind the scenes, the static files application basically provides a way for you to organize
all your assets and then reference them in templates without hard coding your pass or anything like that. The problem with this is, though, that every time we run a Webpack build with changes, we're gonna get a different hash. So, if we forget to kind of update this HTML document,
we're never gonna reference the new bundle, which is bad because we're gonna get very confused. We thought we'd fix this JavaScript bug, but we didn't change our template. So, users don't actually get linked to this new script. So, errors continue to happen. If only there was a way to fix that. So, another package that OAS wrote
is called Django Webpack Loader. We pip install it the normal way. And then we kind of just have to tweak our settings.py. So, our Django application will use it. So, make sure that in static files, dirs, and that settings.py setting,
we put the static directory in there. We also need to implement the Webpack Loader setting. So, basically, we need to give it the bundle directory name. This is the directory under which Webpack is producing, is writing bundles app. So, in this case, it's just bundles.
Then we also need to tell it the stats file. What file is sort of tracking the bundles that are written into this directory? And in this sort of toy project, it's webpack-stats.json. Finally, we need to input, we need to put Webpack Loader in our installed applications. So, Django's able to reference it in our templates.
So, how do we get the built assets into our templates the same way? Well, now our index.html looks a little bit different. We can load the render bundle tag from Webpack Loader, and then when we need to render the actual script tag,
we don't have to write a script tag ourselves. We use this render bundle tag, and behind the scenes, it will produce the correct script tag for us. If we were able to produce CSS bundles, it would produce the right link to our style sheets. So, what this does is, behind the scenes,
it uses static files, but it also uses that webpack-stats.json file to extract the name of this particular bundle and use it when writing out the link. So, when we're deploying, all we need to do to deploy to production
is run a new webpack build, commit the changes that occur in the stats file, but none of our templates need to change. So, this makes deployments that are essentially one small little JSON file changes, and every template that references them through this render bundle still continues to work exactly as we would expect.
So, what is rendered is kind of the correct, as we would expect. It renders a script tag with the correct source, type, charsets, everything that our browser needs to be able to find that file. So, for the visual people in the audience, we have webpack running independently of Django.
So, when we run a webpack build, it produces this webpack-stats.json file, and it also produces the bundle itself. When Django receives a request and needs to render out a template that references that bundle, it just needs to read the webpack-stats file. When that bundle changes and we produce a new bundle,
the content of the stats file changes, but nothing else in your project changes. You don't have to update any templates yourself. Django, we've configured Django through webpack-loader to just read that file and produce a different link in your template. So, to kind of finish up a little bit of philosophy,
I don't think that we should serve our assets from Django. I recommend using S3. If you're worried about performance, a CDN, if you must, but to me, S3 kind of is a perfect place to put your assets. The permissions are a lot more easy to handle than ingressing into your production network,
and it's also accessible from everywhere. What this means is you can build your bundle through webpack, commit that change, and upload the bundle to S3, and then, as you're testing, if you have your project running under CI, you can actually test,
you can do in-browser tests through the actual bundle that's already produced in an S3 that your users will actually use once you deploy these changes. Why don't we use sort of just pure static files? Why don't we use flex-static? It comes with Django.
Why don't we use that? Well, first, to run it, sort of in your production environment, you have to be able to get to production, and depending on the level of security in your project, that could be difficult. It's slow. Any time you make any change to any of your assets and need to deploy them through collect-static,
it copies everything. So in the meantime, you are potentially serving requests that reference assets that aren't actually ready because collect-static is slow. And finally, if you wanna do anything fancy with your assets and collect-static, you have to include post-processors, which means installing software on your production servers
that are only used, that's only used when you run collect-static. And I like to include sort of the bare minimums of software on my production servers as possible. In general, I think we need to worry about separating concerns. Django does what it does really well, and Webpack and tools like it do
what they do really well. If you sort of use this kind of workflow, the front-end developers on your team are gonna love you because any tutorial that they find about some new JavaScript technology is not gonna be written from the perspective of integrating it in a Django project.
It's gonna be written with Webpack or something like that. DevOps or just operations in general will love you because they don't have to sort of punch a hole in your network so that when you do need to deploy, you would have to get to some production server and run collect-static. Collect-static is not needed anymore in this world,
so operations is able to lock down your servers even more. I think it's great for new developers on your team too. They don't need to learn sort of new, extra Django management commands, and they also don't need access to sensitive systems.
So I hope that this is kind of an interesting workflow. It might not work for your team, but I hope you kind of think about the philosophical points that I'm trying to make about building better workflows for not only backend developers like ourselves, but also the front-end developers on our team.
Thank you. So we have some time for a few questions. Does anyone have a question? That command to actually get the hash or to change that JSON file that holds that hash that Django Webpack uses in the templates
to bind that bundle, can you just pass in any, could you pass in the actual hash? So in case I wanted to deploy or to use a environment variable, instead of changing a JSON file, instead of having that commit in there to do the deployment, I could just pass in a variable,
and that would just kick off my deployment. That's a great question. What I would say is, so the hash is dependent on the contents of your file, and you might not know what that's gonna be until you build the file, but you could use that stats file in other ways.
So you could use that stats file, maybe upload it to a build server or something like that, and it could reference that file, if that makes sense, and sort of introduce an environment variable into your machine or something like that. Yeah, I guess I've got two questions. One, okay, the big one is
we're using something similar. Every time we run a production build, we end up with the old hashed coded files as well as the new one. Do you run some sort of housekeeping to take care of that? No. No, the simple answer is no. I don't really worry about old versions.
Depending on how much you're willing to pay S3, these files, if you minify them and gzip them, they end up being very small. They're hundreds of kilobytes. They're pretty small. In the grand scheme of things, that doesn't cost a lot to host. So no, I typically, I don't ever delete build bundles,
just because I don't want the possibility, the off-chance possibility of somebody referencing that in a page somewhere and it not actually existing. And so like when you're doing development, do you use a separate webpack config so that you're not creating these hashed things? Yes, yeah, I did show that, but there's a way to basically, in development,
build just a app.js file that only lives on your machine. Right, with your settings files. Yeah, yeah, with the webpack config along with Django's config, you can make that so it transparently works the same, but you're not building separate bundles all the time in development.
And one more question. You were saying that this is reversible. Is that reversible because you've got the hashed or because you're using some sort of configuration management system so you always can go back to an earlier build and redeploy? It depends a little bit on your deployment process. But what I meant by reversible is because we never delete or edit existing files,
we just produce new ones. If we produce new ones that aren't correct or something gets gummed up in your deployment process, you can always maybe revert to master and deploy things that exist that you know work. I have a question. So Django's nature is to keep static files
inside the application folders and to have multiple applications. Have you handled this? So the setup I showed here was a very toy, and I probably need to write sort of how we actually do it at work. But what we do is that we have multiple entry files
for the different aspects of our site. And so we have multiple, we ended up having multiple stats files. And I didn't show it, but the Webpack loader settings in settings.py, you can actually build it as a dictionary, kind of like your databases, kind of how you specify databases. And so you can specify sort of each stats file
and have kind of different aspects of your site built as different bundles. We need to prepare for our next speaker, so if you guys have any more questions. I'll be right outside. Thank you.