Django CRUD Application with VueJs as front-end | VueJs + Django Rest Framework + MySQL example – Part 3: VueJs Client

django-vue-crud-example-django-rest-api-mysql-feature-image

This tutorial is part 3 of Django-VueJs-MySQL series. Today, we will create Vuejs Client to make HTTP request & receive response from Django Server.

>> Part 1: Overview
>> Part 2: Django Server

Video

[Coming soon…]

VueJs Client Overview

Goal

The image below shows overview about Vuejs Components that we will create:

django-vue-crud-example-django-rest-api-mysql-client-components

Project Structure

django-vue-crud-example-django-rest-api-mysql-vue-project-structure

– package.json with 3 main modules: vuevue-routeraxios.
– 4 components: CustomersListCustomerAddCustomerSearchCustomer.
– router.js defines routes, each route has a path and maps to a component.
– http-common.js initializes HTTP Client with baseUrl and headers for axios HTTP methods.
– vue.config.js configures port for Vue App.

For more details about how to use Vue Router in this example, please visit:
Vue Router example – with Nav Bar, Dynamic Route & Nested Routes

Setup VueJs Project

Init Project

Point cmd to the folder you want to save Project folder, run command:
vue create vue-django

You will see 2 options, choose default

vue-create-project-config

Add Vue Router to Project

– Run command: npm install vue-router.
– Import router to src/main.js:

import Vue from 'vue'
import App from './App.vue'
import router from './router'

Vue.config.productionTip = false

new Vue({
  router,
  render: h => h(App),
}).$mount('#app')

Initialize HTTP Client

Install axios with command: npm install axios.
Then create http-common.js file:

import axios from 'axios';

export default axios.create({
    baseURL: 'http://localhost:8000/api',
    headers: {
        'Content-type': 'application/json'
    }
});

Configure Port for Vue App

vue.config.js

module.exports = {
  devServer: {
    port: 4200
  }
}

Implement VueJs Client App

Components

List of Items

components/CustomersList.vue

<template>
    <div class="list row">
        <div class="col-md-6">
            <h4>Customers List</h4>
            <ul>
                <li v-for="(customer, index) in customers" :key="index">
                    <router-link :to="{
                            name: 'customer-details',
                            params: { customer: customer, id: customer.id }
                        }">
                            {{customer.name}}
                    </router-link>
                </li>
            </ul>
        </div>
        <div class="col-md-6">
            <router-view @refreshData="refreshList"></router-view>
        </div>
    </div>
</template>
 
<script>
import http from "../http-common";
 
export default {
  name: "customers-list",
  data() {
    return {
      customers: []
    };
  },
  methods: {
    /* eslint-disable no-console */
    retrieveCustomers() {
      http
        .get("/customers/")
        .then(response => {
          this.customers = response.data; // JSON are parsed automatically.
          console.log(response.data);
        })
        .catch(e => {
          console.log(e);
        });
    },
    refreshList() {
      this.retrieveCustomers();
    }
    /* eslint-enable no-console */
  },
  mounted() {
    this.retrieveCustomers();
  }
};
</script>
 
<style>
.list {
  text-align: left;
  max-width: 450px;
  margin: auto;
}
</style>

Item Details

components/Customer.vue

<template>
  <div v-if="this.customer">
    <h4>Customer</h4>
    <div>
      <label>Name: </label> {{this.customer.name}}
    </div>
    <div>
      <label>Age: </label> {{this.customer.age}}
    </div>
    <div>
      <label>Active: </label> {{this.customer.active}}
    </div>
  
    <span v-if="this.customer.active" v-on:click="updateActive(false)" class="button is-small btn-primary">Inactive</span>
    <span v-else="" v-on:click="updateActive(true)" class="button is-small btn-primary">Active</span>
  
    <span class="button is-small btn-danger" v-on:click="deleteCustomer()">Delete</span>
  </div>
  <div v-else="">
    <br>
    <p>Please click on a Customer...</p>
  </div>
</template>
 
<script>
import http from "../http-common";
 
export default {
  name: "customer",
  props: ["customer"],
  methods: {
    /* eslint-disable no-console */
    updateActive(status) {
      var data = {
        id: this.customer.id,
        name: this.customer.name,
        age: this.customer.age,
        active: status
      };
 
      http
        .put("/customers/" + this.customer.id, data)
        .then(response => {
          this.customer.active = response.data.active;
          console.log(response.data);
        })
        .catch(e => {
          console.log(e);
        });
    },
    deleteCustomer() {
      http
        .delete("/customers/" + this.customer.id)
        .then(response => {
          console.log(response.data);
          this.$emit("refreshData");
          this.$router.push('/');
        })
        .catch(e => {
          console.log(e);
        });
    }
    /* eslint-enable no-console */
  }
};
</script>

Add Item

components/AddCustomer.vue

<template>
  <div class="submitform">
    <div v-if="!submitted">
        <div class="form-group">
          <label for="name">Name</label>
          <input type="text" class="form-control" id="name" required="" v-model="customer.name" name="name">
        </div>
    
        <div class="form-group">
          <label for="age">Age</label>
          <input type="number" class="form-control" id="age" required="" v-model="customer.age" name="age">
        </div>
    
        <button v-on:click="saveCustomer" class="btn btn-success">Submit</button>
    </div>
    
    <div v-else="">
      <h4>You submitted successfully!</h4>
      <button class="btn btn-success" v-on:click="newCustomer">Add</button>
    </div>
  </div>
</template>
 
<script>
import http from "../http-common";
 
export default {
  name: "add-customer",
  data() {
    return {
      customer: {
        id: 0,
        name: "",
        age: 0,
        active: false
      },
      submitted: false
    };
  },
  methods: {
    /* eslint-disable no-console */
    saveCustomer() {
      var data = {
        name: this.customer.name,
        age: this.customer.age
      };
 
      http
        .post("/customers/", data)
        .then(response => {
          this.customer.id = response.data.id;
          console.log(response.data);
        })
        .catch(e => {
          console.log(e);
        });
 
      this.submitted = true;
    },
    newCustomer() {
      this.submitted = false;
      this.customer = {};
    }
    /* eslint-enable no-console */
  }
};
</script>
 
<style>
.submitform {
  max-width: 300px;
  margin: auto;
}
</style>

Search Items

components/SearchCustomers.vue

<template>
  <div class="searchform">
    <h4>Find by Age</h4>
    <div class="form-group">
      <input type="number" class="form-control" id="age" required="" v-model="age" name="age">
    </div>

    <div class="btn-group">
      <button v-on:click="searchCustomers" class="btn btn-success">Search</button>
    </div>

    <ul class="search-result">
      <li v-for="(customer, index) in customers" :key="index">
        <h6>{{customer.name}} ({{customer.age}})</h6>
      </li>
    </ul>
  </div>
</template>
 
<script>
import http from "../http-common";

export default {
  name: "search-customer",
  data() {
    return {
      age: 0,
      customers: []
    };
  },
  methods: {
    /* eslint-disable no-console */
    searchCustomers() {
      http
        .get(`/customers/age/${this.age}/`)
        .then(response => {
          this.customers = response.data; // JSON are parsed automatically.
          console.log(response.data);
        })
        .catch(e => {
          console.log(e);
        });
    }
    /* eslint-enable no-console */
  }
};
</script>
 
<style>
.searchform {
  max-width: 300px;
  margin: auto;
}
.search-result {
  margin-top: 20px;
  text-align: left;
}
</style>

Routing

Define Routes

src/router.js

import Vue from "vue";
import Router from "vue-router";
import CustomersList from "./components/CustomersList.vue";
import AddCustomer from "./components/AddCustomer.vue";
import SearchCustomers from "./components/SearchCustomers.vue";
import Customer from "./components/Customer.vue";

Vue.use(Router);

export default new Router({
    mode: "history",
    routes: [
        {
            path: "/",
            name: "customers",
            alias: "/customer",
            component: CustomersList,
            children: [
                {
                    path: "/customers/:id",
                    name: "customer-details",
                    component: Customer,
                    props: true
                }
            ]
        },
        {
            path: "/add",
            name: "add",
            component: AddCustomer
        },
        {
            path: "/search",
            name: "search",
            component: SearchCustomers
        }
    ]
});

App template with Navbar and router-view

src/App.vue:

<template>
    <div id="app" class="container-fluid">
        <div class="site-info">
            <h1>ozenero</h1>
            <h3>Vue Django example</h3>
        </div>
        <nav>
            <router-link class="btn btn-primary" to="/">Customers</router-link>
            <router-link class="btn btn-primary" to="/add">Add</router-link>
            <router-link class="btn btn-primary" to="/search">Search</router-link>
        </nav>
        <br/>
        <router-view/>
    </div>
</template>
 
<script>
export default {
  name: "app"
};
</script>
 
<style>
.site-info {
  color: blue;
  margin-bottom: 20px;
}
 
.btn-primary {
  margin-right: 5px;
}
 
.container-fluid {
  text-align: center;
}
</style>

Run

– Run Django server with command: python manage.py runserver .
– Vue.js Client: npm run serve.

Open Browser with Url: http://localhost:4200/.

Source Code

vue-rest-client-django-mysql

16 thoughts on “Django CRUD Application with VueJs as front-end | VueJs + Django Rest Framework + MySQL example – Part 3: VueJs Client”

  1. Hey

    Great article. Would you mind maybe making a full example with the django bits included? API authentication etc?
    And github/lab/etc, would be more convenient to browse the source.

    Best wishes,
    Z

  2. Great blog! Do you have any helpful hints for aspiring writers?
    I’m planning to start my own website soon but I’m a
    little lost on everything. Would you recommend starting with a
    free platform like WordPress or go for a paid option?
    There are so many choices out there that I’m totally confused ..
    Any recommendations? Many thanks!

  3. It’s in point of fact a nice and helpful piece of information. I’m happy that you simply shared this helpful information with us.
    Please stay us up to date like this. Thank you for sharing.

  4. Hey there! This is my first visit to your blog!
    We are a group of volunteers and starting a new project
    in a community in the same niche. Your blog provided us valuable information to work on. You have done
    a outstanding job!

  5. fantastic submit, very informative. I ponder why the opposite experts of this
    sector do not realize this. You must continue your writing.
    I’m sure, you have a great readers’ base already!

Leave a Reply

Your email address will not be published. Required fields are marked *