Build an App with VueJS and Django Part One

Ndagi Stanley
πŸ‘οΈ 2,559 views
πŸ’¬ comments

Prelude

Sometime back I challenged myself to build a Django app and use VueJS for it’s frontend. There are a lot of Laravel and VueJS tutorials out there and even within Scotch. Actually VueJS caught the attention of Taylor Otwell, the creator of Laravel, so much so that he personally endorses it. I however struggled to find a good resource on combining Django and VueJS. Here's a tutorial that would have been an answer to my questions back then. A testament of my learnings so far.

Introduction

This is your gateway to having the VueJS's sweetness in a Django app. Later articles will dig deeper as I cover routing and state management.

What this article will cover:

  • Introduce you to VueJS
  • Introduce you to Django
  • Using Jinja templates for the view layer of your Django app
  • Implement VueJS within the Jinja templates

After each step, there is an link to the codebase as it should be after that step.

VueJS

It is said that simplicity is a prerequisite of reliability. Evan You must have been meditating on this saying when he thought of creating VueJS. During the past year it's become a favourite among developers. Compared to JS frameworks like Angular and React, many agree that VueJS's learning curve is the most gentle. This means it is easier to get the fundamentals rapidly. VueJS is backed by Laravel and JSFiddle and is at over 65,000 Github stars (as at the writing of this post).

This Hello World example on JSFiddle is the best place to start. There are several resources that dig deeper to show you the building blocks of VueJS. The VueJS docs' API and guide as well as these tutorials set you up on the road to success.

Django

Django is a Python framework that promises to be the web framework for perfectionists with deadlines. Django uses the MVC (Model, View, Controller) pattern dabbed MTV (Model, Template, View).

To get started open the terminal (I’m assuming that you have python and pip installed) then run pip install django. It is best that you do this in a virtual environment. Proceed with the following commands:

django-admin startproject newapp
cd newapp
python manage.py startapp journal

Doing this will give you the following folder structure:

To run the server simply run python manage.py runserver. Accessing http://localhost:8000/ on your browser will give you such a page: Don't mind the warning highlighted in red. They relate to the database which we are not concerned about for this tutorial.

Codebase up to this point

Jinja templates in Django

Quit the server with CONTROL-C and let's create our first view, shall we? Open the views.py file in the journal directory and these two lines.

def index(request):
    return render(request, 'index.html')

Create a templates folder in the same level as the journal folder and create a file: index.html in that folder. We'll start with a very simple index.html. Here's what I have:

<!DOCTYPE html>

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Journal</title>
</head>

<body>
    <p>Welcome to My Journal</p>
</body>

</html>

We then need to map this view to a url in order to call it. Simply put, we want to see this index.html when we get to a specific url on our browser. This is done by editing the newapp/urls.py. You'll notice that line 19 to 21 is a list called urlpatterns. Line 20 tells us that the url route admin is registered. This means that we have an admin dashboard ready for you out-of-the-box with Django. More about that later. If you are curious here is a link to the docs covering this. Since we want to display the index.html as the page served at http://localhost:8000/ we'll import the index function within journal/views.py and map it to the url route /.

We do this by adding two lines to the newapp/urls.py:

from journal.views import index above urlpatterns and

url(r'^$', index, name='index'), within urlpatterns.

Your urls.py will now look like this:

from django.conf.urls import url
from django.contrib import admin
from journal.views import index

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^$', index, name='index'),
]

Lastly, our view needs to know where the template index is located. Open the settings.py within newapp and search for TEMPLATES. Within it, you'll notice a key in the object called 'DIRS' whose value is an empty list. Enter os.path.join(BASE_DIR, "templates"), so that that line reads:

        'DIRS': [os.path.join(BASE_DIR, "templates"),]

That's about it. Back to the terminal run python manage.py runserver access http://localhost:8000/ on your browser and voila your page is displayed.

That was easy, right? We just added 4 lines of code, edited one line in the settings.py and added one file the index.html.

This section is entitled Jinja templates in Django so let's see what that means. Jinja2 is a full featured template engine for Python which is both beautiful and powerful. I plan to tackle template inheritance and context handling for now.

This is a journal and for the sake of simplicity in this tutorial, we'll display a list of three days (titles with the words: Day 1, Day 2 and Day 3). To do this edit views.py file to include:

context = {
        'days': [1, 2, 3],
    }

just before the return statement. Make sure your indentation is right. Change index.html to days.html then add context as an additional argument in the render method. The file should look like this eventually:

from django.shortcuts import render

# Create your views here.

def index(request):
    context = {
        'days': [1, 2, 3],
    }
    return render(request, 'days.html', context)

In the spirit of modularity, I'll split index.html into masterpage.html and days.html.

Copy the lines above the HTML paragraph tag - p and paste into a new file masterpage.html in the templates folder. We'll add a Jinja2 block in the next line: {% block content %} {% endblock %} and finally add HTML </body> and </html> closing tags. The file will look like this:

<!DOCTYPE html>

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Journal</title>
</head>

<body>
{% block content %} {% endblock %}
</body>

</html>

Next, create another file days.html still in the templates folder. Start the file with {% extends 'masterpage.html' %}. Open a block content by adding this line {% block content %} and then copy the HTML paragraph tag - p from the index.html and paste it the next line. Close the block by adding {% endblock %}. Your days.html file should end up like this:

{% extends 'masterpage.html' %}
{% block content %}
<p>Welcome to My Journal</p>
{% endblock %}

To confirm everything is A-Okay, let's update the views.py by changing the index.html in " " (quotes) to days.html. Running the server at this point should not show any visual change. Take time to understand how index.html is now split into the two files: masterpage.html and days.html.

Let's now display the days. One simply adds the following code snippet immediately after the HTML paragraph tag - p:

<ul>
  {% for day in days %}
    <li><h4> Day {{ day }}</h4></li>
  {% endfor %}
</ul>

The curly braces: {{ and }} we use here are used to bind the dynamic data to the HTML list tag - li enabling us to iterate through the list of days in views.py context to display the list of days. Reloading the page will show you this:

At this point we can delete the index.html in the templates folder.

So far, we have seen how the TV (Template-View) in the MTV (Model-Template-View) of Django is done. We can now incorporate VueJS.

Codebase up to this point

Implement VueJS within the Jinja templates

It is easy to get started with VueJS. We'll tweak the JSFiddle link I shared earlier to have it working in our view.

First off, we'll import vuejs by including: <script src="https://unpkg.com/vue"></script> in the head of masterpage.html.

Next, we'll go to days.html and sandwich the HTML paragraph tag - p and unordered-list tag - ul into a div of id=app. The name app is open to your discretion. Just remember to replace app hereon with the name you decide to use.

We'll add the HTML script tag here to define a client-side script (JavaScript). Most times the script is placed in another .js file and imported to the html file but having it here will help us better see how the DOM will relate to the script. The script in this case will be:

<script>
new Vue({
  delimiters: ['[[', ']]'],
  el: '#app',
  data: {
    title: 'Welcome to My Journal'
  }
})
</script>

The curly braces: {{ and }} we used earlier are called interpolation delimiters. By default, VueJS's interpolation delimiters are the same as that of Jinja templates: {{ and }}. Good news, we can change the delimiters to anything we want and that's what I have done in the line delimiters: ['[[', ']]']. I want the text in the paragraph tag - p to be interpolated from VueJS so let's replace the text with [[ title ]]. The page days.html will now read:

{% extends 'masterpage.html' %}
{% block content %}
<div id="app">
    <p> [[ title ]] </p>
    <ul>
        {% for day in days %}
        <li>
            <h4> Day {{ day }}</h4>
        </li>
        {% endfor %}
    </ul>
</div>
<script>
new Vue({
  delimiters: ['[[', ']]'],
  el: '#app',
  data: {
    title: 'Welcome to My Journal'
  }
})
</script>
{% endblock %}

We can go ahead and replace the days from context data to be populated from the script. We do this by adding days to the data object: days: [1,2,3]. We'll change the HTML unordered-list tag - ul to:

    <ul>
        <li v-for="day in days">
            <h4> Day [[ day ]]</h4>
        </li>
    </ul>

Again the entire file looks like this:

{% extends 'masterpage.html' %}
{% block content %}
<div id="app">
    <p> [[ title ]] </p>
    <ul>
        <li v-for="day in days">
            <h4> Day [[ day ]]</h4>
        </li>
    </ul>
</div>
<script>
new Vue({
  delimiters: ['[[', ']]'],
  el: '#app',
  data: {
    title: 'Welcome to My Journal',
    days: [1,2,3]
  }
})
</script>
{% endblock %}

Reload the page on your browser. At this point the contents of the page are being rendered by VueJS. On Google Chrome, you can install VueJS devtools here and you'll be able to see the data within the components.

Codebase up to this point

Conclusion

We have created a simple Django app touching on the TV (Template-View) of the MTV (Model-Template-View) of Django. We've seen how we extend our views using Jinja2 template engine. We then concluded by replacing the context data with data from VueJS.

In the next articles, we shall take develop the app further by adding CRUD functions to this app. CRUD means Create, Read, Update and Delete, thereby allowing the user to do these functions to their journal entries. Secondly, because we are using VueJS and Django; we shall have contextual data from the DB directly consumed from the View Layer in Django as well as expose this data to API endpoints to be consumed by a separate VueJS application. This is to present you with an alternative way to build your app.

Bonus Point

Actually we can have the data v-for reading from the context-data in views.py like this: <li v-for="day in {{days}}"> thereby making the data.html read:

{% extends 'masterpage.html' %}
{% block content %}
<div id="app">
    <p> [[ title ]] </p>
    <ul>
        <li v-for="day in {{days}}">
            <h4> Day [[ day ]]</h4>
        </li>
    </ul>
</div>
<script>
new Vue({
  delimiters: ['[[', ']]'],
  el: '#app',
  data: {
    title: 'Welcome to My Journal'
  }
})
</script>
{% endblock %}

Ndagi Stanley

1 post

I am a software and data engineer. I am passionate in IOT and enjoy breaking complexities into chewable bites :) I am an outdoors guy: camps, hikes ..