Lightning Talks Day 3
Published November 8, 2018
This video features Dan Taylor at DjangoCon US 2019 in San Diego, California, USA.
DjangoCon 2019 - Creating a containerized Django + React + PostgreSQL development environment by Dan Taylor
There are many reasons to develop with containers, but getting your developer tools to work with containers can be a big challenge. In this talk we'll show how you can easily set up a fully featured containerized Django development environment using Visual Studio Code remote development extensions.
This talk was presented at: https://2019.djangocon.us/talks/creating-a-containerized-django-react/
LINKS:
Follow Dan Taylor 👇
On Twitter: https://twitter.com/qubitron
Follow DjangCon US 👇
https://twitter.com/djangocon
Follow DEFNA 👇
https://twitter.com/defnado
https://www.defna.org/
Intro music: "This Is How We Quirk It" by Avocado Junkie.
Video production by Confreaks TV.
Captions by White Coat Captioning.
Dan Taylor shows how to build a containerized development environment for a Django REST Framework API, React frontend, and PostgreSQL database using Docker and Visual Studio Code. He demonstrates attaching VS Code to an existing container, creating a reproducible multi-service development container with Docker Compose, and retaining normal editing, debugging, testing, formatting, Git, and port-forwarding workflows. He then builds the Django API, connects it to PostgreSQL, adds a React frontend that consumes the API, and explains how to create a production image with multi-stage Docker builds, Nginx, uWSGI, environment files, and collected static files. The central argument is that running development tools inside the container removes host-environment mismatches while keeping a full-featured local workflow, and that development and production images should be configured differently.
Summarised automatically from the transcript.
Automatically transcribed, so expect mistakes in names and technical terms.
Speaker 1: Good afternoon everyone. Thank you for coming to my talk today. So I'm a program manager on our Python Developer Tools at Microsoft. And today's talk is about how you set up a development environment that's running uh inside of a container. And for this talk we'll be building a sample application that's that uses a somewhat common configuration where you have Django REST Framework API with a React front end and a Postgres database. And to build this application, we're going to use just two dependencies We'll be using Docker and Visual Studio Code. Now, if you don't use Visual Studio Code, you'll still get a lot out of the talk about all the other pieces here. You'll learn a lot about containers and React and things like that. But the neat part because we're using containers, you just need these two dependencies if you want to follow along.
Speaker 1: Now please don't try and follow along in this room with me that you can follow along with the video afterwards. I'm gonna be live coding here and I don't need uh we don't need gigabytes of Docker downloads to be happening during the talk. Um so appreciate that. So what why you know uh why are you all here at the t the talk today? Many of you have uh adopted or are thinking about adopting containers uh in your development or production environment. And one of the key benefits of using containers that we hear often is that if it works locally, it should work in production because what a container uh allows you to do is um run kind of like a mini operating system with all of your applications dependencies um in sort of the uh containerized environment. It's kind of like a lightweight VM running on your machine.
Speaker 1: And that's typically defined with a Docker file and you have some tools that will build this container from a Docker file, put your app uh inside of that and then run it. And then when you run that app it does some things. Right? And then if you deploy that container to production, hopefully it will do the same things because all of the environmental factors around your app have been put inside of the container. Now um and it w and an additional benefit that comes with containers is you can use containers to acquire development dependencies. So Uh if you need a database, a Redis Cache, or you know, a Celery worker or a bunch of other microservices that your application depends upon, uh you can throw a bunch of those uh into a Docker Compose file and you can use uh Docker to build a set of containers uh that you can have locally. So it makes it really easy to to create a lot of the complex development environments that we have to work with today.
Speaker 1: Now one of the one of the problems that gets introduced when using containers is that boundary, that container boundary, it's a different operating system. It causes many issues for tools. It's really hard to actually productively edit, debug, test, do all the things that you ni normally want to uh do when you're working on code. So for example, you you have to uh typically you might need to rebuild the container every time you make a code change. Autocomplete might not work if you don't have the Python packages installed on your uh host operating system, which I'll call the local machine. Um and then you know your debugger might not be set up to run the app in the container. And for example, your testing framework might not be in the container because these are things that your app doesn't need during production to run. So typically what you would do is you'd create a different container definition
Speaker 1: for local development. And the thing that I'm going to be talking about today is actually developing fully inside of the container where some portion of your IDE is actually running uh inside the container environment where um you'll have like your editor UI talking to some backend running in the container and that back end is working directly with the code, the app, and the development dependencies. So that allows you to work a lot a lot faster, more productive without sacrificing all the tools that you're used to when you're developing. So uh to make this concrete, I'm just gonna start off with a very low-tech example. Uh I'm gonna create A new uh containerized dev box just from scratch here by running some Docker commands. Um so with Docker you can You can pull down a container just by typing Docker run
Speaker 1: and we'll say I want to run a bash prompt so I'll type dash IT, so run interactive, and we'll give this new container a name, Python DevBox. And uh we'll base it off of the standard Python image that's available on Docker Hub. So you can use any image here if you wanted a different version of Python, you could say Python 3. 6. Uh and then when that container starts up, we just want to run bash. So there we go. I've got uh you know I've got a development container. I can I'm running on a Windows machine, but I've got a bash prompt here. Um so let's actually install some tools. Uh just for fun, let's uh so we can run apt get. And then uh apt get install Vim. So we'll put some Vim in here. Uh we'll we'll we're having some serious fun.
Speaker 1: So it's going to pull the packages down and then we'll install Vim and then we'll let's write a simple piece of code. We'll go and create a new folder for our app. And then we'll Vim hello. py and then press I to insert some code here. We'll say hello DjangoCon. And then uh we'll print that message and then escape colon w enter colon q. Yeah, love vim. Okay, so um we can We can run that with Python's installed in this container. We can get a little nice little Python REPL if we wanted to. Uh Hold on now.
Speaker 1: I shouldn't have done that. I don't know what I typed. Ah. Good thing I've got a backup. This is like the one part of my talk that never messed up when I practiced. All right. We'll just Restart that. Hello DjangoCon. And then print that message. And um we can actually even debug this running inside of the container. We can use pdb, which is the command line debugger built into Python. So I'll run python-mpdb, so that will start the pdb module, and we'll debug the file hello. py So there we're stopped on our line of code, hello DjangoCon. We can step over, we can inspect that variable, and we can step over and
Speaker 1: we can see it prints out the message. All right. So that's our nice little dev container. However, there's some problems with that, right? So we have to remember all these Docker commands. I'm not really the much of a command line per person , so we're limited only to command line developer tools And um in this case, with this simple primitive setup, the code will disappear as soon as the container stops because containers are kind of immutable. They get created one way. And then right with with just how I've configured this, if I got a website running in the container, the ports aren't exposed. So those are all additional things that I would need to do to make this better. So to solve a lot of those problems, I'm going to show you how we can use Visual Studio Code to get a full feature development environment without sacrificing all the things that we're used to. So if you're not familiar with with Visual Studio Code, it's a free cross-platform and open source editor for Microsoft.
Speaker 1: And it's fast and lightweight, and it has support for multiple languages that are enabled through a rich extension ecosystem. And it has uh built-in support for JavaScript and TypeScript, and you get the standard features that you would expect from an editor with IntelliSense debugging and source control. And then for Python, uh there's a Python extension that's developed by my team that adds the Python support uh for those same features like IntelliSense, debugging, linting, testing, refactoring. And this is the the most popular extension for the VS Code marketplace. So there's a lot of Python developers that that are using this tool. And just so you know, it's developed in the open on GitHub and we we put out a new release every month if you if you follow our blog. You can stay up to date with all the new stuff that's coming out. So the the key reason I'm talking about Visual Studio Code,
Speaker 1: it has some unique benefits for this particular scenario. Um there's a unique it has a unique architecture that actually enables a fully featured just like local development experience. So Visual Studio Code is built uh uh on top of electron and it's written in JavaScript and HTML. So it it's always had this inherent client-server model Where the back end all the guts of VS Code ran it as as a server and the UI ran as a separate process. What makes it nice and fast So for uh we we announced remote development um uh for for VS Code at PyCon back in May. And this in this case what we do is we take that backend, we run it in a different machine. So it can run on a remote SSH VM. And in this case, it's running inside of the container.
Speaker 1: So you have all of the UI just kind of transparently works. And we'll show what that means in a second. So one of the benefits of this approach is that you can quickly create or switch between development environments. You just clone something, open it uh with VS Code, and then uh you could just start working because all of your dependencies are encoded in sort of that Docker container environment. So others can really quickly replicate your environment on a different machine. And then other tools do have similar features. I showed you what Vim looked like, and then there's uh PyTerm also has remote development support. But for this talk, I'm going to be walking through VS Code, like I said. So that's some background. So for the rest of the talk, what I'm going to walk through is attaching to an existing container and then creating a new development container.
Speaker 1: and then adding a React front end to the application and then finally uh building the production version. All right, so we just created uh this command line development environment uh front um From the command prompt. Let's actually attach to this this dev box that we created with VS Code. So um I'm gonna open uh Visual Studio Code here. So the first thing we need to do is install some extensions. Alright, so if I go to my extensions tab uh here, uh the first extension that we'll need is we'll need Docker, which I've already got uh that installed. And this allows us to create and manage Docker containers. And then for remote development, we want to install the remote containers extension.
Speaker 1: So this allows remote development to work with the Docker container scenario that I was talking about. So once we have those installed, there'll be a Docker icon on this left activity bar here And that allows us to see all of the containers that we have on our machine, the images that we have built locally, and any registries that container registries that we're connected to. So I can see that there's the two containers, my primary and backup one. I can just right-click on this container and say attach Visual Studio Code. So this will start a new instance of Visual Studio Code, a new UI, and it will install the remote server into that container that I that I built. And if we look at the details here, um
Speaker 1: It's showing us all of the uh Docker commands that it's running. It's copying um the server into the container. It's gonna I don't know all the things it's doing, but It's doing a lot of stuff. Um, but the point is that this is all stuff that, you know, I didn't really have to think about setup and manage. And then once it's finished. Um now this instance of Visual Studio Code is fully running inside of this container. So uh anytime I do anything I would normally do with Visual Studio Code, like open a terminal, now I get a bash prompt because that's that's running in that same uh container. Now I don't have any files open because I actually need to open a folder. And I also need to install the Python extension so that I can start working with the Python code in this app. So
Speaker 1: uh I'll go back to my extensions and I'll search for the Python extension. Um this time it's saying um install uh it's asking if I want to install the extension into the container. So when I when I click this install button, um it will install the Python extension um, like I said, into the container environment. And then once that's finished, I'll actually open up. And now I need to reload VS Code, but I'm gonna uh open a folder on the uh on the container using this open folder button. So there's the app folder where I wrote that simple hello. py script. I'll I'll click that and I'll open. And so reload will then VS Code will then refresh and open that application. uh that folder. So when when it opens I'll be able to see uh my files
Speaker 1: um here and I can actually start uh start coding. So just give it uh uh a second here. So it's a little bit faster the second time because it I it doesn't have to do as much. So there's that file file I wrote from the command line. I can open it. I can select my Python interpreter. I can um I can run this piece of code by going right-click uh run the Python file in terminal. Um I can debug, so I can set a breakpoint, I can click there. and um I can go debug, start debugging. I'll select a Python file. Um and you know I can inspect variables. And everything just kind of works as I would expect, right?
Speaker 1: Um, but it's all running in that uh that container environment. So uh I've got a bunch of slides here that are more for reading offline, so I'll go through them fairly quickly. Um but just a quick recap, uh we installed uh the Docker and Remote Containers extension. And then we right-clicked from the Docker tab and then attached to the container that we had. We installed the Python extension into the container and then we opened a folder and then did some debugging. So we can run the Python file or or debug the code. Okay, so there's still a few problems with this. I attached to an existing container. But again, I haven't solved the problem where my code will disappear or or that I haven't set up port forwarding so I can access websites.
Speaker 1: So the next step we're going to take is we're actually going to use Visual Studio Code. to create a new development container and and configure it with you know more features that we need to be productive. So let me just go back here. So to do this, I'll uh I'll close out this container and I'll make a new container uh folder. And what I like to do, uh I like to do code dot from the command line to open this folder up. Um it just runs VS Code in the current location. So now I'm in this new container folder and uh to create a new development container I'm going to run a command in VS Code. So to run a command you you use control shift p or command shift-p if you're on a Mac.
Speaker 1: And then then like all of the com all the extensions add various commands to this menu. So this is where a lot of functionality shows up. So I can search here and say add development container configuration files. And that will let me pick from a set of predefined containers that that the VS Code team has built. So if I search for Python, I have a few different versions of Python that I can start with. I could use Anaconda or Miniconda. And then for this talk, obviously I want to use the one that has a Python uh image and a Postgres database. So I'll select that. And what that's going to do is going to add a bunch of files to my workspace. Now this is going to take a minute, so I'm going to click the reopen in container. As soon as those files exist, VS Code says, ah, this is a development container.
Speaker 1: Do you want to open this in the container environment? So I'll click that reopen and container button there and while that's opening I'll just open a new instance of VS Code just to walk through some of the files that we created. So this devcontainer. json, this defines, this tells VS Code what it's supposed to do with this development container. It tells it, you know. It's it it tells it um uh uh which file it should use to build the container. In this case it's gonna use a Docker Compose file because it's gonna build multiple services. And then it says which service do you want VS Code to attach to? VS Code will only attach to one of the containers that I'm running. And then, you know, where do you want them out the workspace? And then you can include additional settings that only apply to this development container.
Speaker 1: So for example, it sets where the Python path is. Do I want to turn linting on? And then most importantly, what extensions do I want to install into the container when it starts up? And then And then so if I look at the Docker Compose file, and then this is just the pre-built image that that uh the VS Code gives you. It has an application service. Here, so this defines the different services that Docker Compose will build. Um, and then so it's my app is defined by this Docker file right here. So that's the image that's going to be built when my Docker file runs. And then that connects to a database, which is going to be available on with the name DB on my local network inside of this container.
Speaker 1: And that's just built from a standard Postgres image, and I've set up a uh a simple password that I can connect to the database with. So this container just finished opening. Well I don't know when it finished opening, but it finished opening. And uh so now I can start working with it. Um I've already got uh Python installed. Let me hide some of these So I can get more space back. Okay, that's fine. Um so now I can um Now let's start scaffolding our Django application. So first I'm going to create a requirements. txt file. That's going to include all the Python dependencies I want. And I'll put uh Django
Speaker 1: in that And then I will install um those requirements for my command line. Um so that will install Django and you notice that was super fast. I'm pretty sure that the that these containers have been mapped to volume mount my uh my Python package cache so that that doesn't need to be recreated for every container I open. So now that we've got Django installed, we can run Django Admin start project. And we'll create a new site in this current folder. And then that will give me my my Django application Um and then I can I can uh run that. But in this case, what I want to do is actually when I'm running this, because it's running inside of the container and I want to talk to it from outside of the container, I need to bind
Speaker 1: to 0. 0. 0. 0 so that the site is available to external uh incoming requests. So I'm going to run that on port 8000. I'll get to the migrations in a minute. And when I run um so this won't actually browse yet, but I need to, because I need to forward a port. So I'll run I did control shift P again and I'll say uh forward port to the container and it will it detected that port 8000 is being listened on so I'll select port 8000 and then I can open the browser and Um my new Django application will come up. So we're off and running.
Speaker 1: Okay. So next I want to configure uh this to actually talk to my Postgres database that I have. Um if I actually go uh back to this container here, my Postgres image is running. there. And just a quick tip, you can actually attach a shell to this Postgres container so that if you want to run PSQL commands, you can just type pSQL-U Postgres. And then this will allow you to uh describe the tables and um you know run your SQL queries. Um so now to connect to my Postgres backend, I just need to change some of my settings here. Let's collapse that. We'll use uh the Postgres QL engine. Um and to use that we actually need to install
Speaker 1: um the Postgres driver, put that in a requirements. txt. And then the database name to use is just Postgres, that's the default. And then user. is also default Postgres. And then the host is db. Remember that was in our Docker Compose file as as the name of the of the image. or the the name on the network that our database will be available on. And then finally our super secret password And if someone manages to hack into my machine, they're welcome to my sample data. Okay. Okay.
Speaker 1: So now when we we run this, so I'll just do a quick, so typically if this database didn't already exist, you'd have to do a managed PY migrate. But the migrations have already been applied, my data is created, and now I can run server again. However, this time I'm going to set up the debugger. So to set up the debugger with Django, we want it to always run that manage PY migrate or manage PY run server command when we start. So I'll go to the debug tab here and I'll click add configuration. So this will create a debug configuration that I can use over and over again. I'll start with this Django configuration. And then I will select So this is just tells it how to launch Django, and I'll just add uh an argument on here that specifies
Speaker 1: that that local port. So now when I start debugging, it'll automatically uh debug my Django app and my development server. Should be running. So we'll go localhost there. And if we go to our admin panel, I previously ran the uh create super user command um and there we go. This is all set up with a Postgres database. We have our backend. Now uh I want to add some APIs to this. So you notice there's some models already built in with Django, users and groups. I'd like to expose those as uh APIs
Speaker 1: that I can access as a as a REST API. I'll call those from my node backend later. So I'm going to switch to different container that I've already added some code to um so I'll close this out And if you're switching between multiple containers, you may want to come in here and just stop your old ones just because they'll be using the ports that you want. So I'll just stop those. And then uh we'll go to dev container backend, open this with VS Code.
Speaker 1: And then I'll select reopen in container. And while that's opening, I'll just make sure it's actually opening and then I'll go back to some slides. I've hit uh enough failure modes on this presentation that I know where it is likely to fail. Okay, so just a quick recap of where we are at so far. So we created a new dev container using VS Code. Um and We did that by running the Add Development Container Configuration Files command. We selected a pre-existing Python 3 and Postgres container. And then we used the Django admin command to start a new site. And uh we
Speaker 1: ran it on the local IP address and then set up port forwarding. Um and I didn't cover this, but One thing that I did add to the Docker Compose file is to set up that port forwarding automatically just by adding this list of ports here so that every time I open the container those ports are always exposed. And all right. Um come back here. Um I forgot to cover Git. Um But uh I'll get to that later. So um I've opened this um this new dev container here and uh So
Speaker 1: uh to add that API code, what I've what I've just done is um I've followed the Django Rest framework tutorial that's that's available at this link, um, and I added the Django Rest framework package. And uh I'll just walk you through what that that code looks like. So um I also did uh Django Admins start app. Back end to create the back end. Um so basically what what code uh we added here, um first We added some serializers that take those users and groups and tell Django Rest framework how to serialize those into JSON objects. And then we added some views that expose those users and groups as a set of REST APIs.
Speaker 1: So this defines multiple endpoints for all your different HTTP verbs, your get, update. post, put, those things. And then finally we uh added those views as um uh we've mounted those views at a URL path of API and and we've defined this router and registered it. So again, that's following the standard uh REST framework tutorial stuff. So um once we've done that, let's go ahead and uh run the app again and we'll just see uh what that looks like now and so now if I go uh API Oops. Now we get the Django Rest Frameworks browsable API.
Speaker 1: We can go in here and we can click around. We can look at the existing user users that we have. We can even create users and things like that. Um so if I wanted to create a new user, I'll just name him Jeff for whatever reason. That's just what I'm feeling right now. And we'll uh click post and then now that user is added to the user list. Okay, so we have an API. Um and again, that's the code we went through. I'm just skipping. through it for now. And so let's just do uh while we're here, let's just play around with a few more features of US code. So first I'll configure tests. So if we want to configure tests again, I can run another command. In my backend, I just have a few tests I've written using unit test.
Speaker 1: So CtrlShift P or Command Shift P, we'll run the configure test command. And uh we so we support unit tests, py test, and nose. And unfortunately we don't support Django unit tests, but it's uh it's on our backlog. Um so I'll select unit tests, I'll tell it where those tests are, they're in the back-end folder, and they're named tests star. py. So once I do that, I I can get uh the testing features of VS Code running here. Um I can run tests or debug tests from here in code, and I can also use um a test explorer to to view and and run all of my tests. So I'm getting all this, all the, like I said, all of the kind of features that that you have with VS Code, they just inherently work here. So now um let 's just uh just for fun, let's
Speaker 1: run the Django Rest framework uh code through Black to format it just to see what Black thinks of this code. code. So we'll run the format document command. You can also right-click and do format document. It'll ask me what format I want to use. I'll select black. And that will install black into the the container. And uh now I actually have to run the format document command again now that I've installed it. So Black just added some spaces and it changed the the single quotes to double quotes. Okay, that's interesting. And then like I said, I've got my source control here so I can see those changes. And um And I can actually commit these and and and push them. Uh when VS Code set up the container, it actually copied my git config file from my my desktop.
Speaker 1: So anything that I have On my local machine, I can implicitly push to an access from the Git with inside the container. That's another nice thing that I didn't have to set up. All right. So let's go ahead and quickly add the React front end here. So let's close out all these files. So uh inside of my Docker file, um I have actually added to this one I've added uh just some apt get commands that install node. I'm pretty sure I got these from the node website. Um just pasted those in here and then so now this container actually um has node installed in it so I can use that to build uh the React
Speaker 1: front end. To do that, I'm going to use a tool called Create React App. So Create React App is is, you know, this isn't your father's React anymore. So I used to work with React a long time ago before we had Create React App and it was just a mess, but Create React App makes things a lot easier because it packages all of the things that React needs into uh a a a a very small package. So we'll just run the npx uh create react app command to build a new uh front end folder. Um Now again, uh this actually takes um I think like 10 minutes, so I'm just going to cancel out of that and switch to a different container where I've already set this up.
Speaker 1: This is a get up and go get coffee moment. So let me close that and then we'll switch over to dev container frontend. And then do the same uh code dot. And then we'll just go ahead and uh reopen that uh in the container. And uh while that's opening, this will take about 30 seconds or so. I'll just start to look at some of the files that we have. So after Create React App ran, um it uh it created this front-end folder. And that basically has my input source for React. So React is a ah see I gotta stop those old containers.
Speaker 1: All right, we'll try that again. I think I've confused myself. All right. So also a neat tip, your recent files lets you pick between whether you want to open something inside of the container or if you just want to open the folder normally. So I think So this one I'll select, I'll reopen this in the dev container. Okay, so that's building. So while that's building, uh this one. So basically the files in the source folder, those are all compiled into
Speaker 1: basically from the React syntax into this build folder that we have up here So um this is like the uh this is JSX which is a mix of uh JavaScript and HTML. Uh it's a it's a React concept. It's very divisive, a lot of people don't like it, but I personally find it really uh fun to work with. Um but basically this is uh a new function that just returns uh a piece of HTML code. I've got my CSS here, I've got some tests and other things. So like I said, all this stuff gets compiled um into you know uh mangled JavaScript and things like that. Um and how this works is that there's an index. html,
Speaker 1: which you can't really read here, but basically this index. html is a static index. html that includes all the JavaScript that got built by React. And so that's the the page the page that we need to return for our users to be able to view the site. And then I think okay, so this has started. The first thing I'm going to do is start the uh node development server with npm start. And so this is going to give me a development server. that I can use where I can uh edit my code on the fly and then the changes will be immediately reflected in the browser. And but it won't this won't actually build those files that I was talking about. This is just for for local development.
Speaker 1: Um and this uh also takes a minute uh to get kicked off. So while that's running, I'll just do a quick recap. So we added the node uh to the image by adding these lines to the Docker file. We ran the create uh React app command which which generated uh that folder structure over there. And our server is almost ready. I'll get my browser going. So this server is going to start on port 3000. And in this case, this right now this isn't connected at all to my Django application. This is just a node development server that's going to serve up some HTML for me. So um
Speaker 1: and the reason that I want that is that I can come in to my source folder, my app. js, and uh I can say I'll just change some of this HTML code that says, you know, hello DjangoCon. I'm not very creative today. And if that's all working correctly, it should um It's it's recompiling and then the updates show up in my browser. Okay. So I'm gonna switch uh this code over uh to Another branch where I've written a little bit of code to call my backend API, the user's uh API. Okay. Just throw away my changes before I switch branches.
Speaker 1: So I'll switch over to that branch, which has added a little bit of uh code to this API call, hopefully. Okay. So walking through this code a little bit, so it's we just added a few things here. So uh the way React works, it's a unidirectional rendering thing. You have some state and uh you have a render call that will just generate your HTML based on what that state is. So for what we're going to do here, we're going to define a users variable. So we're going to get the users from our API. We're going to store it in this users variable. So this initializes users and a set users call. By the way, this is I'm using the new React hooks that were recently released, so this doesn't look like React has in the past. It's actually a little bit cleaner.
Speaker 1: So um uh then I'll have this use effect call which which will get called when the page loads and that will fire off an API call to the back end. to get my API users that will then return uh JSON and with that JSON I'll uh execute the set users call which I've defined up here. So that will update the state of my app. And then when that state gets updated, this render call will happen. And uh It will use the user's state here to generate a list of paragraphs which will show the users and the user email. All right, so that so this is these curly braces allow you to insert a little bit of uh JavaScript code, and then that JavaScript code can generate HTML. It's kind of confusing, but I I like it.
Speaker 1: So with that update, this time let's go ahead and uh uh do npm run build. Ah, this is my favorite bug that I've come across in prepping for this talk. To fix this bug, I just go cd dot dot. And then CD back into the folder. And then it works. Cool. So that's that's gonna build with um that's gonna build uh the uh HTML and JavaScript that I have uh up in this build folder here.
Speaker 1: And it should uh finish in a second. Oh, and so while that's building, um I actually need to add a view an a uh uh a URL, an endpoint that will return the index. html. So So now Django is going to serve up my React app. I'm not going to use the node development server. So to make that work, what I do is I have I define a new uh index view, and that basically just uh returns the the index. html um from my front-end folder. And uh in my settings, The other important thing that I have to do in my settings is add this front-end folder to my static files list. So there I define the React app
Speaker 1: dir and then I add the build static folder to the static files lifts that Django is able to find. And the reason I do that is because um this front-end folder has no Python code in it. There's no Django application or anything associated with that. So we just need to tell our back end to return it. So now that we've got that, we can again, we can run our back end and we should see a new updated app. And um apologize for the text wrapping. Okay, so there we go. We've our React app, it gets the API call that's showing us the list of users that we have uh in our database.
Speaker 1: So that's kind of the basic application. So uh again for the dev uh server we needed to add port 3000 to the mapping mapping. And then we were able to edit code and reload. And then another thing that uh that we've that I've done here is uh in the package. json file, I've uh I added this proxy command. So that when I'm using when I am using the development server, if there's any API calls that it doesn't recognize, it'll just forward those onto the back end, the Django backend API. So that's just something you just need to do for local development to make this setup work. Okay. And then that's the code. This is all for reading offline.
Speaker 1: If you wanted to follow through. Okay. So now let's build a production version of this application. So as I've said before, this is all um For purposes of making my life easier as a developer, I can edit code and do all those things. But when I publish to production, I want to remove all of these things that I have from the container and build something that's production ready. In particular, when I'm running manage PY run server, uh this web server won't handle production workload. If you throw a load test at this, it might actually just like hang. Um and you know, things like that. So to So for now I actually want to exit the development container and to do that I'm going to run another command called reopen locally. So when I run reopen locally, that just takes me outside of the the dev
Speaker 1: container and uh actually I'll hit hit close here. So before I do this, I'm going to stop my my container so that the ports uh don't get used up. Okay. And uh so I'm going to switch to another branch where I've added a new set of uh Docker files and Docker Compose file. So I'll switch to the uh production branch here. And so here I've got uh these files, the Docker Compose and Docker file that I'm going to use to build my production image Now there's a few small differences here. So in this case, I still have a local database I can use for testing,
Speaker 1: but in this case I've I've tagged the image with the name of the container registry that I want to push it to when I'm done And then also I have it pulling environment variables from a. env file. And so this allows me to put passwords and connection strings into a file that is in my gitignore and will not get checked in. And so I'm not putting any of that information into code. And I have this repo comes with a sample. m sample where it's got, you know, here's the template to fill out Um and I can use this, I can just say CP. env uh sample to. env. Um and then I'll start uh I'll start to build this I can actually right click and just say compose up
Speaker 1: and that will build that Docker compose file and it will um uh it will run the the containers in it. So while this is building, I'll uh keep explaining uh some of the different things that I've put into these Docker files. So in the in this case for the Docker file, um before I just put everything into one image, I installed Node into the actual container. In this case, I'm actually going to use Node just to build the front end, and then I'm going to take Node out of the container. And I'm going to do that using a concept called multi-stage builds. So whenever you see multiple from uh declarations in a Docker file, it's using a multi-stage build. So this first stage it takes a standard node container, then it runs this weird-looking command to apply all the variables in my. n file.
Speaker 1: And it runs the build. And then I create a new uh Python container uh from uh from this pre-built image on Docker Hub. In this case I'm using an Nginx web server for serving the static files and it uses uh UWISGI. uh uh server um for hosting a Django application. There's some configuration files, but the key thing here is um that it actually copies the output of the node build from that uh front end from the first stage and puts it into the second container. So I've got the output of the node build, but then I don't end up with the node runtime or any of that stuff in the production application. And then uh the last step that I do is I run uh Python managed py collect static. So this will take all of the static files and put them into a static files folder uh in my uh
Speaker 1: application. And this is the stat this is the folder that my Nginx web server will serve. And again, I apply the. en file when I'm running that command so that it has the uh the various variables, and I'll explain uh why in a minute. Okay, so this is probably finished building by now. Alright. So if I go to my local host uh 8000. There's the uh production version of the container um that I have so that it's good that that works. Um and then again this is hosted using an Nginx web server. So if I look at the Nginx configuration, I've got uh It says okay, host all static files. Basically put slash static
Speaker 1: to serve static files from the static files folder. This is the output of the build. and then host my Django application um using WISGI at the root on port 8000. Now um I actually have My production secrets in a different. n file that I have saved here and I'm not going to show you this file, because that's the point. Um and then that one actually defines if you look at the sample file here. Um It actually defines a place to upload static files to using the Django storages library because for production typically I want my files to be available as a my static files to be available on a CDN somewhere. So I'll start this building again. So now that I've changed my. m file, I have to rebuild everything.
Speaker 1: So what these environment variables will do is while this is building, It'll actually, when it runs this collect static command, it will actually take all the static files and upload them to a Nash Storage account that I have, and that's connected to a CDN. And when that's finished, I'll just reload the container and to show you. you what that means. So we're almost done here. So right now when I'm loading these files, all the static files come from my localhost. So after this is finished building, and I also remove the. m file from the container afterwards because it has my passwords. So now that this is finished building, when I reload this, all of the uh static files are now served from Azure Edge.net. And so that will make uh those files uh available distributed all over the world so that uh it's nice and fast for people who
Speaker 1: browse my website. Okay, so um just quick recap. We reopened the folder locally, we added uh some production Docker files, and we stored our secrets in a. m file That's an example of what that. n file looks like with my passwords and key removed. And I walked you through what those production Docker files and there's the configuration for the USGI server and it that's what picks up the WISGI app for Django. And then I also have, once we have this container built, you can easily just deploy it up to Azure just by using the Azure App Service extension that we have. But that's a bonus, I don't have time to walk through that. today. Okay, so that was uh a tour of attaching to an existing create container, creating a new development container, adding a React front end, and then building it for production.
Speaker 1: The slides and code are available at the uh aka ms link up there that's http colon slash slash aka. ms um follow our python blog for any updates and follow us on twitter thank you so much
Speaker 2: All right. Thank you, Dan.
Containers package an application and its dependencies so the same environment can run locally and in production, and Docker Compose can provide supporting services such as PostgreSQL, Redis, or Celery. The tradeoff is that the container boundary can make editing, debugging, testing, and autocomplete harder unless development tools also run inside the container.
Discussed at 1:00Install the Docker and Remote Containers extensions, select a running container from the Docker view, and choose “Attach Visual Studio Code.” VS Code installs its remote server and extensions into the container, allowing the editor, terminal, Python tooling, and debugger to work against the containerized code.
Discussed at 10:15Use VS Code’s “Add Development Container Configuration Files” command and choose the predefined Python-and-Postgres configuration. It creates a devcontainer configuration and Docker Compose setup that builds an application service, starts PostgreSQL as a separate service, and tells VS Code which container and workspace to use.
Discussed at 14:51Run Django on `0.0.0.0:8000` rather than only on localhost, then use VS Code’s port-forwarding command to expose port 8000. The forwarded port lets the Django site open in a browser outside the container.
Discussed at 18:49Configure Django to use the PostgreSQL engine and install the PostgreSQL driver, then use the Compose service name `db` as the database host. The database name, user, and default password in the example are all PostgreSQL defaults defined by the Compose configuration.
Discussed at 19:34Add Django REST Framework, create serializers for the models, define views for the API endpoints, and register those views with a router under an `/api` URL. This produces Django REST Framework’s browsable API, where the users and groups endpoints can be viewed and modified.
Discussed at 25:01Install Node in the development image and use Create React App to generate the frontend. React’s development server can provide live reload on port 3000, while a React component can fetch JSON from the Django API, store it in state, and render the users in the page.
Discussed at 28:55Build the React frontend with `npm run build`, add the generated frontend directory to Django’s static-file configuration, and create a Django view that returns the built `index.html`. In the production setup, a multi-stage Docker build uses Node only to compile the frontend, then copies the output into a separate image served by Nginx, with Django hosted through uWSGI.
Discussed at 37:24Note: We understand that names change, people change, and bodies change. We respect each individual's journey and privacy. If you have any concerns about a video or need us to remove content, please don't hesitate to contact us. We will handle your request with care and promptly address any issues.
Published July 15, 2026
Published July 15, 2026
Published July 15, 2026
Published July 15, 2026
Published July 15, 2026
Published July 14, 2026