DEV Community

Cover image for Using Vue.js Alongside Django Template
Aymane Mimouni
Aymane Mimouni

Posted on

Using Vue.js Alongside Django Template

Let’s imagine this scenario, you’re building a Django project, everything going very well. At some point, you needed to level up the interactivity of your app. what are you gonna do?

You will probably achieve the desired behavior using jquery or you will keep your models, build an API, and use a JavaScript SPA for the frontend.

What if I told you that you can keep everything you build with Django, get the interactivity and convenience of Vue.js, without all the overhead of a SPA setup?

This post aims to show that you can start to use Vue with your Django projects immediately without any sophisticated setup that will take hours to complete.

A Demo App

https://cdn-images-1.medium.com/max/1600/1*Ekn2UmjkdM6H6oJNZHt45A.png

For a demo, I made a simple todo app, so I can play around with vue.js alongside the Django template.

The app shows the users' tasks, and the user can perform basic crud actions.

It looks really simple, but I but it’s a great way to practice some of the key concepts of Vue.

Try to create it yourself, and of course, If you get stuck, you can always get back to my code.

github.com/aymaneMx/vuejs-alongside-django

Setup

If you check out the official Vue guide, they have links to a CDN where you can simply include Vue via a <script> tag into your Django template:

<script src="<https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js>"></script>
Enter fullscreen mode Exit fullscreen mode

<div id="vue-app">
  [[ message ]]
</div>
Enter fullscreen mode Exit fullscreen mode

var app = new Vue({
  delimiters: ["[[", "]]"],
  el: '#vue-app',
  data: {
    message: 'Hello Vue!'
  }
})
Enter fullscreen mode Exit fullscreen mode

That is it, we have already created our very first Vue app! Couple of things to notice here:

  • el: stand for element, and it provides the Vue instance an existing DOM element to mount on.
  • Usually, we don’t need to define the limiters explicitly but here we need to because the default delimiters of Vue are the same as the default delimiter of Django, so we need to use something else for Vue and that’s why we’re using [[ ]] here instead of {{ }} .

Access Django Data from Vue

The easiest way is to access a Django template variable from Vue, is by using the built-in Django json_script filter.

{{ django_variable | json_script:"js-data" }}
Enter fullscreen mode Exit fullscreen mode

Go check the documentation, it's a pretty cool way to outputs a Python object as JSON, wrapped in a <script> tag, ready for use with JavaScript.

Unfortunately, This solution doesn't always work!

and that what happened to me when I tried to use the variable tasks in the demo app:

# todo/views.py
def home_view(request):
    tasks = Task.objects.all()
    context = {
        'tasks': tasks,
    }
    return render(request, 'home.html', context)
Enter fullscreen mode Exit fullscreen mode

I get the following error!

Object of type QuerySet is not JSON serializable Django.
Enter fullscreen mode Exit fullscreen mode

The way I solved this issue is by creating a task serializer,

# todo/serializers.py
from rest_framework import serializers
from todo.models import Task

class TaskSerializer(serializers.ModelSerializer):
    class Meta:
        model = Task
        fields = "__all__"
Enter fullscreen mode Exit fullscreen mode

and I use it in my view:

from django.shortcuts import render
from todo.models import Task
from todo.serializers import TaskSerializer

def home_view(request):
    tasks = Task.objects.all()
    context = {
        'tasks': TaskSerializer(tasks, many=True).data,
    }
    return render(request, 'home.html', context)
Enter fullscreen mode Exit fullscreen mode

Consuming APIs

In the demo app, I was able to create, delete, update tasks, but only on the frontend side, nothing changed in the backend!

So I had to create a simple API that the Vue app can consume and display data from.

Next, I found myself googling how Vuejs consume APIs?

There are several ways to do so, but a very popular approach is to use Axios, which is also recommended in the official Vue Docs.

Same as Vue, You can include Axios via a script tag to your Django template.

<script src="<https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js>"></script>
Enter fullscreen mode Exit fullscreen mode

To pass Django’s CSRF protection mechanism, Axios needs to include the respective cookie in its requests. To accomplish this is to set global Axios defaults:

<script>
    axios.defaults.xsrfCookieName = 'csrftoken';
    axios.defaults.xsrfHeaderName = "X-CSRFTOKEN";
</script>
Enter fullscreen mode Exit fullscreen mode

For example, let’s delete a task using Axios, assuming that /api/<pk>/delete/ is the right endpoint.

var url = '/api/' + task_id + '/delete/';
axios
    .delete(url)
    .then(response => {
        this.deleteTask(task_id)
    })
    .catch(error => {
        console.log(error);
    });
Enter fullscreen mode Exit fullscreen mode

This call can be done within a Vue instance’s mounted hook or any other place where you can put JavaScript code.

All done!

That wasn’t so hard! Now you can focus on building cool things with Vue on top of an API driven by Django.

Top comments (2)

Collapse
 
karim_ghibli profile image
Karim-Ghibli • Edited

Sorry, so you still end up using an API, right? I am a noob, so please explain what is the point of this practice? Basically seems there's no way to have the easy binding of objects like DTL allows, right? So, the advantage this approach gives is simply not having to remove/rewrite all the old DTL templates, but since we have to write an API anyway, it seems the code becomes a mess this way, no? If we know BEFOREHAND that we will need interactivity of Vue (or any other frontend framework like that), this approach would be a mistake, right? In that case we should start without DTL, just start with DRF right away and build the frontend separately, correct?

I ask if my mental model of this is right, that's why every statement has a question at the end :D, since maybe I misunderstand or don't know something completely.

Collapse
 
tahseen09 profile image
Tahseen Rahman

I have done this before. And it's amazing to use both for an amazing experience