Creating an Inclusive Django Community with Kenya Phelps
Published July 15, 2026
This video features Brent O'Connor, Joshua Weaver and Levi Mann at DjangoCon US 2022 in San Diego, California, USA.
Our team of two front-end and six backend-stack engineers needed to build a web application that consisted of 60 complex and simple, pages and forms on a tight deadline. We knew we couldnβt meet our deadline by building a SPA, so we opted to use Vue and Django in a Hybrid type approach that would allow our back-end engineers to render Vue components from Django templates.
This talk was presented at: https://2022.djangocon.us/talks/the-best-of-both-worlds-using-vue-and-in/
LINKS:
Follow Brent O'Connor π
On Twitter: https://twitter.com/epicserve
Follow Joshua Weaver π
On Twitter: https://twitter.com/josh7weaver
Follow Levi Mann π
On Twitter: https://twitter.com/lmann2014
Follow DjangCon US π
https://twitter.com/djangocon
Follow DEFNA π
https://twitter.com/defnado
https://www.defna.org/
The team chose a hybrid architecture for a USDA nutrition-program system with about 80 forms: Django handles form definitions, validation, submission, and simple server-rendered pages, while Vue supplies reusable components for complex interactions. They extended Django/DRF rendering with custom field attributes, template packs, and template tags that serialize props into Vue component markup, allowing backend developers to build forms without learning a separate frontend stack. The approach requires each Vue component to contain valid HTML form controls and manage its own state, and it adds some frontend setup and boilerplate. It met the deadline, preserved a consistent UI, and produced forms that the team found easier to test and debug than full SPAs, though they noted server-side rendering as a future improvement and considered HTMX less suitable for keeping complex and simple forms consistent.
Summarised automatically from the transcript.
Automatically transcribed, so expect mistakes in names and technical terms.
Speaker 1: Hello and welcome to our talk on using Vue and Django together in a hybrid approach. My name is Brent O'Connor and I am the Director of Engineering here at Canopy. Before we dive into the technical details, take a look at this form. There's quite a bit of custom field formatting and logic at play here. that responds to user selections. What if I told you that one of our full stack developers made this without even having to touch JavaScript? Today, my team and I will be talking about how we were able to accomplish this using a hybrid approach using both View and Django. A few months ago, the USDA contracted our company to build a system for a national nutrition program to keep track of their annual plans and annual reports.
Speaker 1: During our initial mock-up of the MVP, we realized that this system would require approximately 80 different form views. Half requiring complex logic and half requiring no logic or very simple logic. How could we reach this hard deadline in only five months? We determined we needed to build our system as a single-page app using Vue and Netlify for the front end. We then decided to use Fast API with Graphene on the back end. We also concluded we would need to break it up into 10 different microservices, all running on Kubernetes, of course, with multiple redundant cloud providers. Hopefully you picked up on my sarcasm. As tempting as it might be to start chasing the tech community's new shinies, to reach our deadline, we actually needed to leverage our team's existing skills and abilities.
Speaker 1: and let wisdom prevail. It's very tempting to try fun and exciting new technologies, but we value a good work and life balance. we knew that there was no way that our front-end engineers could be the only ones working on the front end, especially for approximately 80 forms. We needed a way for back-end engineers to get involved in a way that they could knock out form views with a simple UI without having to learn a new tech stack. Instead, we opted for reliable and productive rather than exciting an overkill. Sort of like a Toyota Prius. Django is reliable and productive. With Django, you can build Django forms quickly and easily.
Speaker 1: But we knew we would have to build our more complex forms like full-fledged View SPAs. This is when we started trying to think about how we could have complex view form components that looked and behaved the same way in our Django forms. This is when the hybrid approach was born We asked ourselves, what if Django could render a form, but instead of rendering the standard form input elements, it could use our custom view input components. With that in mind, I'm going to turn it over to Levi Mann, who is one of our senior software engineers who's responsible for building our custom component renderer.
Speaker 2: In order to leverage our team of primarily back-end developers, we wanted to build out this hybrid pattern in a similar way to Django Forms and common tools like Crispy Forms. Here's the normal pattern we are all familiar with, using a model form to define the form and crispy forms to render the fields in the template. Besides the familiarity to Django developers, we like a few things about this pattern We can generate a form from a model pretty quickly with very little code. The template is simple. In other words, we can use a template tag or filter and we don't have to do any form styling or copy our form elements from place to place And we leverage Web 1. 0 form submission so we don't have to worry about adding API endpoints, which can add another layer of complexity. These are all things that save time and simplify the process.
Speaker 2: So in this new pattern, we want to maintain these benefits. There are a few things that we need to get this pattern working. Add some additional functionality to the base form class. override the field templates to output view component tags instead of basic inputs, and provide the correct props to the view component. First, we need to work on the form class. In this project we decided to use Django Rest framework serializers for our forms. I could go deeper into what was behind this decision, but the main reason for this was to avoid duplicating validation and save logic since we had several places using the same logic for a form and an API endpoint. The main thing that we added to the serializer-based class is a meta class property called field attributes. This gives us a way to define rendering attributes to fields such as labels or help text.
Speaker 2: Once we have the serializer or form class set up, we need to get our form class rendering view components instead of standard HTML components. To do this, we simply leverage the Django Rest framework template pack functionality, or Django widget templates if using Django forms. There are several files for different types of inputs that we can override. Let's look at an example of how we would override the text area field template. As you can see, all we need in the widget file is the component tag that Vue is expecting to render and the Django context variables that we will pass to the component as props. At this point, it seems like we should be able to use the built-in field rendering of Django Rest Framework or Django. Both should be able to handle taking the widget template and placing it into the HTML. But there's one problem, JavaScript.
Speaker 2: As you see in the widget template, there are several props that the Vue component needs to function. In order for Vue to understand what we are passing in as props, We need to serialize the field properties into a form that JavaScript can use. To do this, we create a custom template tag. So this template tag is a little messy and is one area that we would like to polish down the road. But let's look through the basics of what it's doing so we can get a better idea of what's needed to get something like this working. Just note that we are still using the default Django REST framework field rendering functionality. First, we need to point the renderer to the custom template pack that we created earlier. Then we serialize the field attributes, things like the field's value or errors. that won't be able to be parsed by JavaScript. An easy example of this is Boolean
Speaker 2: values. In Python, Booleans have the first character capitalized. If we drop this into the template, JavaScript doesn't know what to do with it. So we set the Boolean to a lowercase value and then JavaScript is happy. The last thing we do is handle any custom cases a Django REST framework doesn't support. An example of this is date time inputs. As I said earlier, a similar tag could be written for Django forms with only a few changes, so this solution isn't exclusive to Django Rest framework. In addition to the View Field tag, we added a View Form tag, which basically calls ViewField on each field. With these template tags, we are finally ready to render our form. In order to illustrate this, let's take a look at the output of the Django REST FrameReck render form tag. As you can see, it outputs the basic HTML form elements. Now let's look at what our custom view
Speaker 2: form tag outputs. In this case, the component tag is there with all the props serialized. And that's it, that's all we need for the back end. We're defining our form and rendering view component tags properly for each field. Now it's up to the frontend to initialize the view app and render the components. Since I'm allergic to JavaScript, our senior front-end engineer will explain the details of how we implemented the view code.
Speaker 3: Thanks, Levi. Now we do still have a couple more things to do before we're done. Specifically on the front end, we have two more things that we need to do. The first is that we need to make sure that all of our components have a valid HTML element in them. And the second is that we need to make sure that all of our components are tracking their own state internally. So let's address number one. Each input must have its own valid HTML element inside it. With a traditional SPA, we don't have to worry as much about Valid HTML tags being inside a form tag because when you're ready to hit the back end, you're going to be hitting an API and you just grab the data you need and post to the API. With our hybrid form, all that we have is what's inside of that form tag. So we've got to make sure we have valid inputs and selects and other valid HTML input tags inside of there.
Speaker 3: For us, the biggest problem we had with this was with our drop-down components. That's because we were using VueSelect as a third-party library to render our drop-downs and we had a wrapper around it. Ironically, VueSelect doesn't have a select input in it. So we had to add one. Here you can see the select input that we added. It's got a class of D none on it. And that basically hides it visually, but the select input is there in the HTML when the form posts back to the Django backend, all of the data is there that it needs and it's able to work properly. The other place that you might experience this is If you have a date picker or something else like that, where often people build custom solutions and they may or may not put valid HTML
Speaker 3: form input tags in them. So those are the probably your most likely is uh if you have a drop down or if you have a date time picker, but there could be other places where you need to do some customizing like this. The second requirement that we have is that all of our components track their own state internally. So you see in our diagram with the traditional SPA, the Vuex store or the Pinna store that you have is going to be outside of your form input component. With our new hybrid approach, we don't have any of the context of a parent component or an external store, so the child component has to keep track of its own state as the user is interacting with it, typing text, changing the inputs. All of those changes only affect the child component itself and they stay there. They don't get admitted, they don't do anything else. So it is a little simpler in some ways because it's encapsulated.
Speaker 3: So with those two requirements satisfied, really the only thing left to do is stand up our view app. What we have here is the template that we will render, and you see the view form template tag that Levi had talked about earlier that will create all of the view component names in our markup and you see at the bottom there's a setup forms function. This function is basically just a wrapper function around views create app function. So we create the application, we register our components that we want to use globally, and then we call app. mount. And that tells you to parse through the HTML and render all those components that we Told it to render. So there are a few things I wanted to note. There is more JavaScript boilerplate code with this approach than you would have with a traditional SPA. And that's primarily just because you have to stand up the view app
Speaker 3: for every single page. The second thing is that with this approach, we do have a possibility for optimizing it by using server-side rendering. Basically right now all of the rendering that Vue does happens on the client side. If we were to do that rendering on the server side, it would mean that First, we don't have to ship the view compiler with the final bundle. And second, it would get rid of an instance where your screen might flash white while view is working. Because if we render on the server side, they're going to get to the browser already rendered, so we're not going to have that flash of white screen. So server side rendering is something that we really wanted to do, but just didn't have time to tackle. So if one of you gets inspired and takes this on, hit us up on Twitter and let us know how it went. With this approach, you can use any JavaScript library that you like.
Speaker 3: If you really love React, you can use React components. If you're super cool and you want to use web components, that's okay too. As long as it takes data in through a template, you can use that JavaScript library. And with that, I'm going to send it back to Brint.
Speaker 1: One thing you might be wondering is why not use HTMX or some other technology? We did look at using HTMX and even tried some experiments with it. While it could have been a solution that worked, we ended up concluding that we would still have an inconsistent UI. because of our more complex forms where we knew we would have to use Vue. The other thing that scared us away was the fact that this was a very complex system. And we didn't know what problems we might run into when trying to make some of the more complex forms work. Something else you might be wondering is. Was it all worth it? In the end, we met our deadline leveraging our team strengths, and we've been pleased with the end results of having form views that are easy for our Django engineers to crank out
Speaker 1: We also ended up maintaining a consistent user interface and user experience that uses some of the same front-end libraries as our more complex SPA views. If we were going to do this all over again, One of the things we might do differently is to try to convert more of our SPA type views into this hybrid approach. Because one of the things that we found is the hybrid forms tend to be easier to test and debug and less prone to bugs. In the end, we also improve the overall developer experience for both the front-end and back-end engineers. And finally, the front-end engineers will stop threatening to quit if we use more jQuery. Thank you for listening. We hope that we've inspired you to consider this hybrid approach and think about how you can leverage your team's talents and abilities.
Speaker 1: I
Speaker 3: said I'm just gonna apologize in advance.
Speaker 1: Hey, how's it going teleprompter? What's up, buddy?
Speaker 3: So with number one
Speaker 1: And I am the director of engineering here at Canopy.
Speaker 2: We're defining the form and we're rendering the view componentized properly for each dun dun Don't have your fruities on the fellow.
They needed to deliver roughly 80 forms in five months and wanted back-end developers to build simple forms without learning a new front-end stack. Django offered a reliable, productive way to handle simpler forms while Vue could power the complex ones.
Discussed at 2:03Define the form as usual, override Django or Django REST Framework widget templates so they output Vue component tags, and pass the field data as component props. Custom template tags render individual Vue fields or the complete Vue-backed form.
Discussed at 4:19A custom template tag serializes the field attributes into JavaScript-compatible values before placing them in the Vue component tag. It also handles Python-specific values such as booleans and custom cases such as date-time inputs.
Discussed at 5:51Each component must contain a valid HTML form element, such as an input or select, so its value is included when the form posts to Django. Components must also manage their own state internally because there is no parent component or external store coordinating them.
Discussed at 7:45The team met its deadline, gave Django engineers an easier way to create forms, and maintained a consistent UI with the same front-end libraries used by its complex SPA views. They also found the hybrid forms easier to test and debug and less prone to bugs.
Discussed at 11:57Note: 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