Django integration

How to use Vue with Django - free starter

This guide will provide you with a free template for a Django application, with MongoDB database and Vue + Bootstrap 5 front-end.


Prerequisites

Before starting the project make sure to install the following utilities:


Creating a new Django application

Let's create a fresh Django application so we can go through all the steps together. For this tutorial we'll be using MySQL database.

Step 1

Creating a MySQL database

In order to create a new database you need to run the following command

        
            
    mdb database init -db mysql8
    
        
    
  • Create a new user
  • Provide username, password, database name and description.

CLI will display your username, password, database name and connections string. Now you can go to phpMyAdmin where you will be able to handle the administration of the MySQL database.

Note Note: Your final username and database name may differ from your inputs. MDB GO may add some characters to randomize username and database name.

Important Do not close your terminal window until you save your credentials somewhere. This is the only time we will show you your database password. If you won't save it you'll loose it.

Step 2

Creating Django API.

Install Django on your computer.

Note: If you are getting errors, try typing these commands in cmd instead of in the vsc terminal or check your Python environment settings.

        
            
    python -m pip install -e django/
    
        
    
        
            
    py -m pip install Django
    
        
    

Step 3

Now we have to install the necessary modules that are needed for the Django backend to work properly.

To create Django web API we will need Django Rest Framework. We should also install module that will be responsible for handling CORS issues in our developement application.

        
            
    pip install djangorestframework
    pip install django-cors-headers      
    
        
    

Step 4

Navigate to your project directory, i.e. mdb-vue-django-app.

Let's create a Django project by typing the code below. We are naming our project as MDBdjangoAPI but you can go with whatever you want.

        
            
    django-admin startproject MDBdjangoAPI
     
        
    

Step 5

Now that we have the project installed, let's create a new app in which we will put all the functions our REST API will need. Navigate to the newly created directory with django project and enter the following the code.

        
            
    cd MDBdjangoAPI
    python manage.py startapp restAPI
    
        
    
        
            
    cd MDBdjangoAPI
    py manage.py startapp restAPI
    
        
    

Step 6

Register the app and new modules in settings.py. If you are not sure about the name of your app, go to restAPI/apps.py and copy the class name. We have to register corsheaders in middlware section aswell.

        
            
    ...

    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'rest_framework',
        'corsheaders',
        'restAPI.apps.RestapiConfig'
    ]

    CORS_ORIGIN_ALLOW_ALL = True

    MIDDLEWARE = [
        'corsheaders.middleware.CorsMiddleware', 
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.common.CommonMiddleware',
        'django.middleware.csrf.CsrfViewMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
    ]

    ...
    
        
    

Step 7

Nowe we have to create a model needed for our app. We need _id field which value will be autofilled. We also need name and desc with max length set as below.

        
            
    from django.db import models

    # Create your models here.


    class Tasks(models.Model):
        _id = models.AutoField(primary_key=True)
        name = models.CharField(max_length=60)
        desc = models.CharField(max_length=255)

    
        
    

Step 8

Django requires to install mysqlclient in version 1.4.0 or higher so it can work with MySQL properly. Without it you wouldn't be able to communicate with database and manipulate its contents.

        
            
    pip install mysqlclient
    
        
    

Step 9

Go to settings.py, go to DATABASES section and add the database connection details. Change the values between brackets for the ones you got after creating a new MySQL Database from our starter.

        
            
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql',
            'NAME': '[Your database name]',
            'USER': '[Your user name]',
            'PASSWORD': '[Your password]',
            'HOST': 'mysql.db.mdbgo.com',
            'PORT': '3306'
        }
    }
    
        
    

Step 10

Make migration with our models to the database.

        
            
    python manage.py makemigrations restAPI
    
        
    
        
            
    py manage.py makemigrations restAPI
    
        
    

Step 11

Check restAPI/migrations/0001_initial_py if everything is correct and then migrate the data. Now you can see the changes after logging to https://phpmyadmin.mdbgo.com/

        
            
    python manage.py migrate restAPI  
    
        
    
        
            
    py manage.py migrate restAPI  
    
        
    

Step 12

Create serializers for our models. This will allow us to convert complex types into python datatypes that can be rendered in json format.

        
            
    from rest_framework import serializers
    from restAPI.models import Tasks


    class TaskSerializer(serializers.ModelSerializer):
        class Meta:
            model = Tasks
            fields = ('_id', 'name', 'desc')

    
        
    

Step 13

Open views.py and start working on API methods we'll be using. We are going to create GET, POST, PUT and DELETE endpoints that will support the functionality of the front-end app.

        
            
    from django.shortcuts import render
    from django.views.decorators.csrf import csrf_exempt
    from rest_framework.parsers import JSONParser
    from django.http.response import JsonResponse

    from restAPI.models import Tasks
    from restAPI.serializers import TaskSerializer

    # Create your views here.


    @csrf_exempt
    def taskAPI(request, id=0):

        if request.method == 'GET':
            tasks = Tasks.objects.all()
            tasks_serializer = TaskSerializer(tasks, many=True)

            return JsonResponse(tasks_serializer.data, safe=False)

        elif request.method == 'POST':
            task_data = JSONParser().parse(request)
            tasks_serializer = TaskSerializer(data=task_data)

            if tasks_serializer.is_valid():
                tasks_serializer.save()
                return JsonResponse("Task Added Succesfully", safe=False)

            return JsonResponse("Task Failed to Add", safe=False)

        elif request.method == 'PUT':
            task_data = JSONParser().parse(request)
            task = Tasks.objects.get(_id=id)
            tasks_serializer = TaskSerializer(task, data=task_data)

            if tasks_serializer.is_valid():
                tasks_serializer.save()
                return JsonResponse("Task Updated successfully", safe=False)

            return JsonResponse("Task Failed to Update")

        elif request.method == "DELETE":
            task = Tasks.objects.get(_id=id)
            task.delete()

            return JsonResponse("Task Deleted", safe=False)

    
        
    

Step 14

Create a new urls.py file where we will define routes for our API methods.

        
            
    from django.urls import path
    from restAPI import views

    urlpatterns = [
        path('tasks/', views.taskAPI),
        path('tasks/<slug:id>', views.taskAPI)
    ]
    
        
    

Step 15

Add new path to urls.py in the main project directory.

        
            
    from django.contrib import admin
    from django.urls import path, include

    urlpatterns = [
        path('admin/', admin.site.urls),
        path('', include('restAPI.urls'))
    ]
    
        
    

Step 16

Make migrations and run backend server. Copy the link to your app and paste it to your browser. You should see an empty array after adding /tasks at the end.

        
            
    python manage.py makemigrations restAPI
    python manage.py migrate restAPI
    python manage.py runserver
    
        
    
        
            
    py manage.py makemigrations restAPI
    py manage.py migrate restAPI
    py manage.py runserver
    
        
    

Creating MDB Vue application

If our backend is working correctly we should start creating a new Vite application. If you have encountered any problems, you should go back and try each step again.

Note: Don't forget to go back to your root folder before next step. Folders MDBdjangoAPI and mdb5-free-vue should be in the same directory.

Step 1

Create a new vite project with our MDB starter. Run the command below and select MDB5 Free Vue starter.

        
            
            mdb init
      
        
    

Your folder structure should look like this

        
          mdb-vue-django-app/
          ├── mdb5-free-vue
          └── MDBdjangoAPI
        
      

Step 2

Let's make some changes to the created vue app. First we need to install axios inside our mdb5-free-vue directory.

        
            
            npm install axios
      
        
    

Remove style.css file (don't forget to delete it from main.ts file) and remove HelloWorld file from components directory.

Step 3

Let's create a .env file inside a mdb5-free-vue directory. We have to add VITE_ before the name of your variable, because it's the only way to expose them to Vite-processed code. Don't forget to change LINK_TO_YOUR_BACKEND to the link your backend is running right now.

        
            
            VITE_API = "LINK_TO_YOUR_BACKEND_APP"
      
        
    

Step 4

Add new content to Home.vue file inside the views directory.

Since our starter database contains some sample models and routes, let's use them. We will create an application that will show a list of tasks. We also intend to create a functonality for adding new tasks, changing their content and removing them.

We have already prepared the code for you, so go ahead and copy and paste it into App.vue.

Ok, so what's actually going on there. We use MDB components, MDBBtn, MDBModal, MDBListGroup, MDBInputs and few other. The modal will be responsible to show inputs that will allow you to add, edit and send tasks to the database. The Manage tasks button, gives possibilty to modify or remove tasks. At the end, the list group will display our data.

        
            
      <template>
        <MDBContainer class="mt-5">
          <MDBRow class="pt-5">
            <MDBCol class="text-center">
              <MDBBtn color="primary" @click="taskModal = true">Add task</MDBBtn>
            </MDBCol>
          </MDBRow>
          <MDBRow class="mt-3 p-5" style="min-height: 40vh">
            <MDBCol class="d-flex justify-content-center align-items-center">
              <div v-if="taskList.length === 0">
                Nothing to display. Add a few tasks.
              </div>
              <MDBListGroup v-else class="list-group-light" style="min-width: 22rem">
                <MDBListGroupItem
                  class="d-flex justify-content-between align-items-center gap-5"
                  v-for="task in taskList"
                  :key="task._id"
                >
                  <div>
                    <div class="fw-bold">
                      {{ task.name }}
                    </div>
                    <div class="text-muted">{{ task.desc }}</div>
                  </div>
                  <div>
                    <MDBIcon
                      class="text-primary me-3"
                      title="edit"
                      icon="pen"
                      style="cursor: pointer"
                      @click="() => editModal(task)"
                    />
                    <MDBIcon
                      class="text-danger"
                      title="delete"
                      icon="trash"
                      style="cursor: pointer"
                      @click="() => deleteTask(task._id)"
                    />
                  </div>
                </MDBListGroupItem>
              </MDBListGroup>
            </MDBCol>
          </MDBRow>
        </MDBContainer>
        <MDBModal
          id="addNewTaskModal"
          tabindex="-1"
          labelledby="addNewTaskModalLabel"
          v-model="taskModal"
        >
          <MDBModalHeader>
            <MDBModalTitle id="exampleModalLabel">{{
              isEdited.edited ? "Edit task" : "Add task"
            }}</MDBModalTitle>
          </MDBModalHeader>
          <MDBModalBody>
            <form>
              <div class="my-4">
                <MDBInput
                  label="Name"
                  type="text"
                  v-model="newTaskName"
                  counter
                  :maxlength="60"
                />
              </div>
              <div class="my-4">
                <MDBInput
                  label="Description"
                  type="text"
                  v-model="newTaskDesc"
                  counter
                  :maxlength="255"
                />
              </div>
            </form>
          </MDBModalBody>
          <MDBModalFooter>
            <MDBBtn
              color="secondary"
              @click="
                {
                  resetInputs();
                  taskModal = false;
                }
              "
              >Close</MDBBtn
            >
            <MDBBtn
              color="primary"
              @click="handleSaveChanges"
              :disabled="!canSendData"
              >{{ isEdited.edited ? "Save changes" : "Add task" }}</MDBBtn
            >
          </MDBModalFooter>
        </MDBModal>
      </template>
      
        
    
        
            
      <script setup lang="ts">
        import { ref, onMounted, computed } from "vue";
        import {
          MDBContainer,
          MDBRow,
          MDBCol,
          MDBListGroup,
          MDBListGroupItem,
          MDBBtn,
          MDBModal,
          MDBModalTitle,
          MDBModalHeader,
          MDBModalBody,
          MDBModalFooter,
          MDBInput,
          MDBIcon,
        } from "mdb-vue-ui-kit";
        import axios from "axios";
        
        interface SingleTask {
          _id: number;
          name: string;
          desc: string;
        }
        
        const taskList = ref<SingleTask[]>([]);
        const taskModal = ref(false);
        const newTaskName = ref("");
        const newTaskDesc = ref("");
        const isEdited = ref({ edited: false, value: -1 });
        const API_URL = ref("");
        
        const canSendData = computed(() => {
          if (newTaskName.value.trim() === "" || newTaskDesc.value.trim() === "") {
            return false;
          }
          return true;
        });
        
        const resetInputs = () => {
          newTaskName.value = "";
          newTaskDesc.value = "";
          isEdited.value = { edited: false, value: -1 };
        };
        
        const handleSaveChanges = async () => {
          if (!canSendData.value) {
            return;
          }
        
          isEdited.value.edited
            ? updateTask(isEdited.value.value, newTaskName.value, newTaskDesc.value)
            : createTask(newTaskName.value, newTaskDesc.value);
          resetInputs();
          taskModal.value = false;
        };
        
        const editModal = (task: SingleTask) => {
          newTaskName.value = task.name;
          newTaskDesc.value = task.desc;
          isEdited.value = { edited: true, value: task._id };
        
          taskModal.value = true;
        };
        
        const getTaskList = () => {
          axios
            .get(`${API_URL.value}tasks/`)
            .then((res) => (taskList.value = res.data));
        };
        
        const createTask = (name: string, desc: string) => {
          const data = { name, desc };
          axios.post(`${API_URL.value}tasks/`, data).then(() => {
            getTaskList();
          });
        };
        
        const deleteTask = (id: number) => {
          axios.delete(`${API_URL.value}tasks/${id}`).then(() => {
            getTaskList();
          });
        };
        
        const updateTask = (id: number, name: string, desc: string) => {
          const data = { name, desc };
          axios.put(`${API_URL.value}tasks/${id}`, data).then(() => {
            getTaskList();
          });
        };
        
        onMounted(() => {
          API_URL.value = import.meta.env.VITE_API;
          getTaskList();
        });
        </script>
      
        
    

Step 5

The app should be fully functional and should work correctly with backend

        
            
        npm start
      
        
    

Optimization

If you want to further optimize your application please visit:


Backend features

Django:

This example was created with use of Django. Our app is connected to MySQL database and is ready to receive get, post, put and delete requests.


Frontend features

MDB UI KIT:

To create the project we used our ui kit, with which we can build basic views very quickly.

Views and Layouts:

In this project we used the App.vue file, created by the Vite tool in which we placed our vue code. We have successfully integrated the backend with the MDB package and can send basic requests to backend Django application.