Creating a Django VueJS app with Django Rest Framework
Before you read any further, this tutorial will not be going over setting up Django and VueJS together, if you haven’t already got a project set up follow my easy step-by-step guide on Setting Up Django and VueJS.
What will we make?
By the end of this tutorial, you will have or will know how to make a fully functioning single page Notes web app with a VueJS frontend and a Django backend. It will look like this, not the prettiest, but this isn’t a graphics tutorial:
I’m assuming some background knowledge in Django, Django Rest Framework and VueJS, as such, I’ll try to keep the tutorial as brief and simple as possible! Click these links if you want to learn more about Django Rest Framework or VueJS.
Adding a few dependencies
Building on top of the step-by-step setup guide, we will need to install djangorestframework
in our python virtual environment and axios
in our Vue project using npm
(if you haven’t already) for making our API calls.
# inside your project root directory
pip install djangorestframework# inside your frontend directory
npm install --save axios
Step 1: Making our Django API
First, let’s make our “Notes” app and add it to our Django settings.py
along with the rest_framework
app.
# inside your project root directory
python manage.py startapp notes# djangovue/settings.py
INSTALLED_APPS = [
# ...
'notes',
'rest_framework',
]
Creating our Note model
As we’re going to store notes in the database, we’ll need to create a Note model in our notes/models.py
file. To keep it simple, it will just be a model with a CharField
with a maximum length of 180.
from django.db import modelsclass Note(models.Model):
note = models.CharField(max_length=180)
Registering our model with Django Rest Framework
To make our model work with Django Rest Framework we need to set up a serializer for our Note
model, to tell Django what fields should be returned and how to serialize them into JSON. To do this we can create a serializers.py
file in the notes app with the following contents:
from rest_framework import serializers
from notes.models import Noteclass NoteSerializer(serializers.ModelSerializer):
note = serializers.CharField(max_length=180, allow_blank=False)
class Meta:
model = Note
fields = ['note']
This will result in a Note Object being serialized to:
{
"note": "Note contents here..."
}
Then we can make our Restful API view in notes/views.py
using Django Rest Framework’s ModelViewSet
, this will generate all the different methods for us to get and add notes to the database:
from rest_framework import viewsets
from notes.models import Note
from notes.serializers import NoteSerializerclass NoteViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows notes to be viewed or edited.
"""
queryset = Note.objects.all()
serializer_class = NoteSerializer
Finally, we need to configure our Notes app URLs to point to our view, to do this we will use the Django Rest Framework Default Router to register our URLs in the notes app directory:
# notes/urls.pyfrom rest_framework import routers
from django.urls import path, include
from notes import viewsrouter = routers.DefaultRouter()
router.register(r'notes', views.NoteViewSet)urlpatterns = [
path('', include(router.urls)),
]
and then register our Notes app URLs in our main Django project URLs, here we include them with the api/
prefix:
# djangovue/urls.pyfrom django.contrib import admin
from django.urls import path, include
from django.http import HttpResponse
from django.shortcuts import rendervue_urls = [
path('', lambda request: HttpResponse(render(request, 'vue_index.html'))),
]urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('notes.urls')),
path('', include(vue_urls)),
]
This URL setup will allow us to access our API endpoint for our notes at:
http://localhost:8000/api/notes/
Don’t forget to create our new migrations and migrate them before we run the server with the commands:
python manage.py makemigrations
python manage.py migrate
python manage.py runserver
Step 2: Making our Vue app
Having already configured Vue and Django to work together in the previous tutorial we won’t need to set up any Django URLs as our root URL will already be rendering our Vue app so we can start creating our Vue app straight away!
The Note Component
First, let’s make a Note component that we can use in our app to display a note. I’m not going to explain the CSS styling as they’re not the best designs, and I know you can do better! In frontend/src/components
create a file called Note.vue
and add the following contents:
<template>
<div>
{{ text }}
</div>
</template><script>
export default {
name: "Note",
props: {
text: {
type: String,
required: true,
},
},
}
</script><style scoped> div {
display: flex;
width: 200px;
min-height: 100px;
padding: 10px;
background-color: beige;
border: none;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.4);
border-radius: 5px;
font-family: Avenir, Helvetica, Arial, sans-serif;
font-size: 1em;
margin: 5px;
}</style>
This is a very simple component that takes a single prop
called text
which it displays inside a div
with some styling.
We can test our component by adding it to our App.vue
file:
<template>
<div id="app">
<Note text="Test Note Text" />
</div>
</template>
<script>
import Note from "./components/Note";export default {
name: 'App',
components: {
Note,
},
}
</script>
This is a good time to start running our watch
script that we added to our package.json
in the previous tutorial so our Vue app is automatically built as we edit it and we just need to refresh the page to see our changes (make sure you’re still running the Django server in another terminal window):
npm run watch
If we go to http://locahost:8000 our app will now look like this:
Adding a form to add new notes
We need a way of adding new notes to our database, so let’s update our App.vue
to include a form, like before, I won’t explain the styling!
First, let’s add some data to our app, we’ll need a notes
array to store all our notes in as objects with a singular note
attribute, and a newNote
variable to store the text for the new note that we want to add. We will also add a addNote()
method which will add the value of the newNote
to the notes
array and then clear the value of newNote
.
<script>
import Note from "./components/Note";export default {
name: 'App',
components: {
Note,
},
data() {
return {
notes: [],
newNote: "",
};
},
methods: {
addNote: function() {
this.notes.push(this.newNote);
this.newNote = "";
},
},
}
</script>
Here’s an example of how the data will look when populated:
notes = [
{
"note": "Some cool note"
},
{
"note": "Another note here"
}
];
newNote = "I'm typing a new note";
Then in the template section of the App.vue
file we can use a v-for
to display all of our notes, a form with a textarea
with a v-model
linking its value to the newNote
variable, and finally we can set the form to call our addNote()
method when it is submitted:
<template>
<div id="app">
<h2>My Notes</h2>
<div class="notes">
<Note v-for="(note, index) in notes" :key="index"
:text="note.note" />
</div>
<h2>Add a New Note</h2>
<form @submit.prevent="addNote">
<textarea v-model="newNote" placeholder="Note Content...">
</textarea>
<button>Add Note</button>
</form>
</div>
</template>
Add a bit of styling and our App.vue
file should now look like this:
<template>
<div id="app">
<h2>My Notes</h2>
<div class="notes">
<Note v-for="(note, index) in notes" :key="index"
:text="note.note" />
</div>
<h2>Add a New Note</h2>
<form @submit.prevent="addNote">
<textarea v-model="newNote" placeholder="Note Content...">
</textarea>
<button>Add Note</button>
</form>
</div>
</template><script>
import Note from "./components/Note";export default {
name: 'App',
components: {
Note,
},
data() {
return {
notes: [],
newNote: "",
};
},
methods: {
addNote: function() {
this.notes.push(this.newNote);
this.newNote = "";
},
},
}
</script><style>
html, body {
margin: 0;
padding: 0;
font-family: Avenir, Helvetica, Arial, sans-serif;
}
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: left;
color: #2c3e50;
width: 100vw;
min-width: 300px;
max-width: 1000px;
margin: 0 auto;
}.notes {
display: flex;
flex-direction: row;
flex-wrap: wrap;
margin: 20px;
justify-content: center;
}h2 {
margin: 10px;
}form {
margin: 20px;
display: flex;
flex-direction: column;
}form textarea {
resize: none;
height: 220px;
margin: 0 10px;
background: beige;
outline: none !important;
border: none;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.4);
border-radius: 5px;
padding: 10px;
font-family: Avenir, Helvetica, Arial, sans-serif;
font-size: 1em;
}form button {
background: lightgreen;
color: darkgreen;
padding: 10px;
border: none;
border-radius: 5px;
margin: 10px;
cursor: pointer;
outline: none !important;
}form button:hover {
background-color: #a0fea0;
}
</style>
Linking our Vue App with our Django API
We now have an API, and a Vue frontend, but no data is being passed between the two. We can now add our API calls inside the Vue app using axios
!
To make a request using Axios we can simply call axios.get()
or axios.post()
which will return a Promise. For more information on using Axios, have a look at the Axios Documentation.
We will only need 2 API calls to make our app work, one to get our notes from the database, and one to add a new note to the database. To do this we will update our script
section of our App.vue
file:
<script>
import Note from "./components/Note";
import axios from 'axios';export default {
name: 'App',
components: {
Note,
},
data() {
return {
notes: [],
newNote: "",
};
},
methods: {
loadNotes: function() {
axios.get('/api/notes/').then(
response => {
this.notes = response.data;
}
);
},
addNote: function() {
axios.post('/api/notes/', {note: this.newNote}).then(
response => {
this.newNote = "";
this.notes.push(response.data);
}
);
}
},
created() {
this.loadNotes();
},
}
</script>
We have now updated our addNote()
method to make a POST
request with some data containing the value of our newNote
variable.
Then using the .then()
method we handle the response by clearing the newNote
variable and adding the response data to the notes
array, the data from the API should already be structured into a list of objects, each with a note
attribute as that’s what we’ve set our serializer to produce.
We also have a new loadNotes()
method which makes a GET
request to our API to retrieve the notes from the database and set the value of the notes
array to the response data.
Finally, we have added a created()
method to the App
component. This will be called when the App
component is created, and will immediately call the loadNotes()
method, loading the notes from the database into our app.
Check it works!
You can now go to http://localhost:8000 in your browser and have a look at your working app!
If you have installed the Vue DevTools for Chrome or Firefox, you can have a look at the data each component has, here’s what my DevTools say when I select my App
component:
We can see the notes
array is populated and as I haven’t typed anything inside of the “Add Note” textarea, the newNote
variable is empty.
🎉 Congratulations! You’ve just built your first Django VueJS app that uses the Django Rest Framework.
This is the groundwork for building a very basic app, if you want to practice your skills, try adding:
- A “Delete Note” button to each Note (You’ll probably have to modify the Note Serializer).
- Error handling on the API requests, as Axios returns a Promise you can use the
.catch()
method.
If this helped you get to grips with Django and VueJS, brilliant! Go ahead and start making awesome sites!