Using VueJS Alongside Django
tkainrad.dev
tkainrad.dev
I have recently stepped out of the seemingly never-ending spiral of using React/Vue for everything frontend, and also discovered Django along the way.
For building prototypes, Django is the fastest framework I've ever used. After building out prototypes in Django I've come to a realization that at most 10% of my websites need to be truly reactive to user interaction without page reload. Turns out when I dropped this requirement when building something, I can actually finish the projects I start. And also turns out for the end-user, they don't even notice page reloads for most sites.
I'm working on a project that lets you build your Django app like you normally would with templates and views but you get a React app and all the benefits of a single page app. You don't need to write any APIs or use redux etc. It will have a bunch of components built in and if you don't need any more then you don't even need to build the javascript yourself, you just include the already built file. At the moment I have a sort of Django Admin equivalent example working and I'm looking for some feedback. If you or anyone else has any ideas about it I would love to hear, feel free to PM me (email in profile).
Curious how you feel your project fits in at this end of the ecosystem.
This is basically web 1.0. It's like we've come full-circle:
web 1.0 > web 2.0 > web 1.0
I'm not saying this is bad at all. I'm just amused that we are back to building web pages the way we did 10-15 years ago.
Regarding the page loads, have you looked at Turbolinks? https://github.com/turbolinks/turbolinks/blob/master/README....
It's basically a drop in upgrade, makes the whole site feel like an SPA with none of the work. Lovely.
I worked with Django full time for about 6 years. It scales up and out pretty well, too.
There was a peer comment about Rails. I'm a big fan of Rails, and Ruby is one of my favorite languages. But one thing I've noticed is the lack of magic inside the Django codebase vs. Rails' codebase. Spelunking in the codebases feels night and day different: I found Django very intuitive and Rails... less so. I think that was a combination of my lack of skill with Rails' internals, and the different in languages and communities. Python (and Django) try to follow the Zen of Python. Rails felt like every other method was monkey-patched and reduced to a single line or two. It was odd: I think the expressiveness of Ruby was actually a negative at large scale (sort of like Lisp vs. Go)
For 99% of use cases, you never need to get to this level of the frameworks, but it was an interesting experience.
One major reason is that a central tenet of Rails since its inception seems to be to eliminate any and all boilerplate or glue code. But boilerplate and glue code also serve a purpose of showing you how things are hooked up. And they tend to have very tiny one-time costs whereas abstracting them away may incur rather heavy costs of knowing the abstraction exists, how it works, where in the Rails code it's actually implemented, and always remembering all of this whenever you touch Rails again in the future even after a long hiatus.
Without glue code, you have to just know how everything works: How does the router know to route GET /users to UsersController and its index() method? You never see an invocation. And there seems to be middleware code that runs before my index() method is called. Where is it defined and what makes it run before my code? How do I get a list of all middleware that might run before my code? Oh, libraries can install their own middleware code? etc, etc.
You end up doing a pretty deep dive in the abstractions of Rails and your gems to understand these questions and all questions like them. You basically become a Rails expert when you truly hold all of this in your head. Meanwhile, you don't need to deep dive on any code to understand what `router.get('/users', UsersController.index)` might be doing.
To Rails' credit, I remember how much it was compared to things like Spring in its early days and I imagine the sort of XML config nightmare you could get with old Java frameworks is what DHH must have had in mind when he sought to improve the state of web development. I think Rails represents an interesting experiment in the timeline of software development where we played with zero boilerplate. And I think since then the ecosystem's views on boilerplate and glue code has swung back to a less squeamish middle ground.
I think this is really what people generally mean by "too much magic". DHH has a twitter thread (https://twitter.com/dhh/status/1226296264469336064) where he attacks the idea of "too much magic", but I think it's obvious to anyone who has used Rails that there is definitely a spectrum of magic and Rails certainly ranks towards one pole more the other.
And to defend DHH's twitter thread, he's always been kind of a harsh speaker like that. And you also have to keep in mind how long he's had to deal with the "ugh too much magic" dismissal. I would expect him to generally agree with this post.
Another tip would be to give django-admin a try when building dashboard projects (you can customize the look and feel quite a bit, ours are usually unrecognizable).
But you can always switch it for Jinja2, just a line in the settings file.
Hope more people discover (or re-discover) this.
DjangoREST is also a very solid framework if your use case is not a website and you get all the rest of Django for free...
Startups in my area are actually doing better than they are in our main tech hub despite most of them using what Silicon Valley would call fairly boring and extremely old tech stacks such as ASP MVC.
However I did not want to lose all the business logic and work that had gone into the views, models, forms and fields along with all the validation and authentication logic etc.
So I wrote some React code that grabs Django's HTML forms and dynamically turns all the HTML fields into React components. To do updates I just submit ordinary form data from React. This means I just create ordinary Django application forms and views and it works from the React app.
There's pros and cons to this approach. The code that does the HTML form to React form logic is more complex than I would like. On the other hand it's all constrained to a fairly tight area of React. Then there is the huge benefit of being able to make zero modifications to the Django back end. On the con side, it does feel like a bit of a frankenstein application. But its a small price to pay for not having to convert entire Django apps into API's.
I actually have lots of mini React apps at the front end that are loaded onto ordinary Django HTML template.
Is react happier being a component on page?
I asked Dan Abramov (a lead React developer) the same question and he said yes, indeed this is exactly what facebook do - they progressively added more and more small react components to their HTML pages.
In React you can also use "portals" to render to multiple HTML elements on an HTML page so you can do it both ways - one React app represented in multiple places in the DOM or you can just create many small React apps and plop them in multiple places on your page.
I'm not going to apologise for my crap code - I'm not creating art I'm making my own applications.
The thing to understand is that I actually do an XMLHTTPrequest to get the Django form and then in JavaScript a create a new separate DOM and put the form and its data in that. In React onchange events in the field I update that DOM that contains the form and its data. When I want to submit back to Django I submit the form from that DOM.
There's more to it than the code here but it should give you an idea:
djangoForm.jsx: https://gist.github.com/bootrino/65ed6a749799c37862e26562e1e...
DjangoRenderFormContext.js https://gist.github.com/bootrino/7e062770ed331753f18ce37ebd0...
This is probably partly due to the fact I find working with django rest framework serializers / views to be much nicer than django forms / views.
I think what puts most people off is the default requirement to go all-in and create an SPA with global state management and client-side managed routing, but this isn't actually necessary.
I have found incorporating Django with https://inertiajs.com to be a pretty solid alternative. I can write the whole frontend in React, and the backend in Django rest framework, but everything is still page based, so I don't require redux or any other kind of global state, just use the inertia utils to make page transitions.
NextJS is another alternative. To talk to a (django) API you need to go through getStaticProps and getServerSideProps which, depending on the location of your API server, can be a little slow, but you get pre-rendered HTML and page based routing so again no global state management required.
There's also the benefit of being able to lean on Django patterns for sessions.
[1]: https://github.com/rojoca/django-rest-inertia [2]: https://github.com/zodman/inertia-django [3]: https://github.com/zodman/django-inertia-demo
The problem with this approach is that you will need to manage another server process(nodejs) besides django for server-side rendering with nextjs.
[0] https://github.com/spiral/roadrunnerI find too many teams are so focused on using the latest fancy frameworks that they spend way more effort to build something using “new stuff” that could have been done in half or a quarter of the time with something like Django. For most projects, simple that’s built and works beats fancy that’s still being built.
“Server side templating can work really well when the website is static. The problem is that as you add more and more dynamic elements, you either have to go to the server and re-render everything with every change (which can be slow), or you can make the changes on the client side with something like jQuery. But the latter option presents a challenge because you'll eventually find yourself duplicating functionality.
For example, imagine a to-do list website that you render on the server with a templating language. It fetches the user's to-do items from the database and then renders the list. Now let's say you want to let a user add a new item without re-rendering the entire page. You could use AJAX to tell the server to add the new item, but now you need to update your UI list accordingly. You could use jQuery to construct a new DOM element and append it, but now that list element exists in two different places: in your jQuery code and in your template on the server. If your website becomes very dynamic, this situation can get really unwieldy from a programming perspective.
Both Angular (from what I've read) and React (from personal experience) are highly suitable for creating single page apps, with the goal of creating a dynamic, fast, and maintainable website. There are other benefits, as well as downsides, to using Angular and React, but I hope this clears it up a little bit. Both of them make server side templating unnecessary by moving UI logic/rendering from your server to the client's browser. So a user who visits your React powered to-list website would get the React code as a JS bundle, it would make an AJAX call to your server to get just the data, and then it would render the UI based on the data." -/u/VertiGuo (https://www.reddit.com/r/node/comments/6t22cr/back_end_templ...)
Your server can just return html which is ready to be inserted into DOM. You don't need to duplicate the node view creation logic client side.
There seems to be some confusion. With your list example, you still make an ajax call to create a new ToDo, the server returns html which is inserted into DOM.
You'll be making an ajax call anyway, except instead of just 200 reponse/json , you'll get html. This is server side rendered. There is no """hidden “ready to be inserted into the DOM” elements"""
Yes, forms are something which, depending on complexity, can be better served with SPA type solution.
If it's not complex, I'd still keep rendering server side and just add small bit of JS logic to update form header to add/remove to error list and just replace form input field with server response.
I don't know if I'm doing it wrong but I still use Gulp to compile everything. I use Django for the routing and intermix a tiny bit of dynamic server-side data (like login user or breadcrumbs) in the Django templates before they go to Vue.
Doing it like this I skip all the component complexity (I do use mixins) and can build front end with simple tools I understand. Possibly I could avoid a little repetition by using components, but the increase in abstraction I don't think would be worth it.
This is fast to develop. I've seen less complex single page apps using React or whatever take what seems like a lot more effort. But like I say, maybe I'm not doing it right. It works really well though.
Nice. Yeah, sometimes it's easier to get started this way without investing a day in getting webpack working...
When the time comes and you need webpack, then this package has been known to help the integration between the frontend build system (webpack) and django's static file serving functions: https://github.com/owais/django-webpack-loader
Instead I went with vanilla Webpack outputting files into Django `STATIC_ROOT`. That way Webpack is only concerned with producing the files and Django handles the whole file name hashing, compression etc in production.
I've found this to be much, much more straightforward. I'm planning to write a post about it.
> non-standard manifest plugin
I hear you, and this feels weird to me too, but then again node-land needs to talk to Django-land somehow so writing a .json file doesn't seem all that bad way to do it ;)
I hope to get the post out somewhere this week!
If it's just a weekend project, it's also fine to just include some external JS libraries directly.
What I seem to get away with is using Cookiecutter Django's* simple build pipeline based on npm and gulp. It is basically set up from the moment you finish the project initialization. It even comes with live reloading.
The pipeline is based on these files:
https://github.com/pydanny/cookiecutter-django/blob/master/%...
https://github.com/pydanny/cookiecutter-django/blob/master/%...
"When you google for Django + Vue, most results will be focused on using Django for your backend and Vue for a separate frontend project."
There are times when this makes sense, but there are times when you just need to sprinkle a but of Vue magic onto part of your site, without turning the whole thing into an SPA.
- easy to answer questions whilst watching the video
- quick to navigate to a specific question
- video visible at all times
- short+long summary of each answer visible when answering a specific question
- long description of question visible
- can be operated using mouse or keyboard (on laptop/desktop) or touch (tablet)
The rest of the site was regular Django, with a bit of jQuery here and there. I settled on:
- a regular Django form, with a single field to accept the a json string of the actual answers (the questions might change in future)
- a description of all the questions, descriptions, choices stored in a Django model, and sent to the page as json
- Vue rendering part of the page (the bit with the category selectors, question text, choices, descriptions)
Vue helped me deliver a pretty slick UI and didn't add any technical debt. (Not saying I didn't add any technical debt overall, just that the front-end part didn't contribute.)
You can also get live reloading working.
I was commenting more generally on the approach to building a web app with Django and including external js libraries in Django templates, which I've done in the past as project requirements have changed over time. After including external js libraries to Django templates, there is a lot less support in terms of resources and supporting libraries such as testing frameworks. If the client-side project is initialized with Vue, the project can benefit from the overwhelming amount of supporting resources.
That being said, mostly it's just a couple of pages that need VueJS such as e.g. a dashboard and such (depending on your app of course!).
If you use standard Django with Turbolinks it will give you the SPA "feel" that you require.
Backend: - Django Rest Framework - Django Background Tasks (great for running cron like jobs, and simpler than celery)
Frontend: - Vue SPA
All of this runs on docker-compose, with nginx used to route traffic to either the backend API or frontend files that were built using Vue-Cli.
Very happy with this stack. It's not super advanced, but it is easy to get up and running and quick at prototyping.
I wrote an adapter of sorts, between Django and the frontend, which is a Django base template specifically for the pages loading compiled Elm/JavaScript, that turns Django view context data into a JavaScript data structure that gets fed into the Elm code. In this way, the frontend JavaScript is initialized and ready to go without more page fetches. If I need to pass some extra data to it, I can simply add it in to get_context_data().
I like the idea of re-using everything Django has to offer but also being able to pass a few parameters into Elm and have it handle the view side. Where I get into trouble is when Elm and Django code both have to agree on data formats and schemas, but this is more my fault for not strongly abstracting the API.
But the most likely scenario? You have both: new apps being developed as SPAs and some older ones being maintained and the nice thing is it is very non-intrusive share Vue components between them by dropping a script tag into the legacy page.
So it's both legacy compatible, current-tech compatible, and my prediction is, better positioned for forward compatibilty with whatever comes next for the same reasons.
One of my minor frutstrations around having a large 4K monitor is that a lot of websites (especially blogs) force the width of the text to be very small and I end up with massive whitespace on the sides. Although this blog still has a small width for the main text, the sides are not wasted!
I used the Coder theme (https://github.com/luizdepra/hugo-coder/) but made quite a few changes over time. These changes include a slight increase in the main container width and the addition of information on the sides.
I really have a hard time reading long posts where I cannot see a TOC on the side, so this was a priority for me.
But then you would be limited to various manipulations of the initial data so "could" is certainly not "should".
Like this: https://stackoverflow.com/a/56461472
Also, jQuery always feels kinda hacky to me. I have a much better feeling with VueJS. Part of this could be owed to the fact that I work on Java Backends in my day job.
The above approach only needs very little generic wiring. The caveat is that it probably only works when there is little client data, which was the case for me.
My biggest gripe with it is that it leaves the business logic side of things totally up to the developer. While many like it this way, I'd find a cookbook of sorts helpful with reference implementations on stuff like fat models or service layers. This is something Next.js has done very well with their examples hosted on Github.
Vue is a great option to combine with server rendered markup (PHP, Python, etc) because it can read the templates directly from the DOM and doesn't need a build step like JSX does.
It's not the only option though. You could also use Preact + Htm (JSX in string templates) which doesn't need a build step either and is much smaller than Vue.
Because I just use django+drf for the api, I thought about using Flask + Flask REST.. However, after having worked with Django over 3 years, I found the learning curve quite steep.
Do you intend to use Vuex and/or Vue router at some point?
Something along the lines of: https://github.com/gothinkster/realworld which covers front-end and back-end separately
class MyApp(VueBase):
def __init__(self):
self.el = "#app"
self.msg = "Hello from vue class"
def created(self):
print('Created')
def v_show(self, evt):
print('show method', evt)
def computed_title(self):
return self.msg + " Computed"
app = MyApp()
app.mount_to("#app")#Vue methods declare with v_ #Computed and watch with computed_ and watch_