Build a Blog Application: Step-by-step Integration with Django and Vue 3
Tags: django, vue, axios, webdev
Published: 2024-05-14 14:31:59

In the realm of Django development, models, views, URLs, and templates form the cornerstone of every project. These elements converge to sculpt a full-stack Python based web framework, meticulously adhering to the MTV design pattern. However, this project does not employ Django templates for the presentation layer. Instead, it uses Vue 3, a powerful JavaScript framework for building dynamic user interfaces. This innovative solution splits the roles of client and server, revolutionizing the traditional approach to web development.

**Setting up Django**
Installing Django with pipenv
Setting up Django involves installing it with pipenv, the preferred package management system for creating a virtual environment. To install Django with pipenv, simply run the following command at the shell:	

```
pipenv install django
```
 
In addition to installing Django, you’ll also need to install the following packages: djangorestframework and djangocorsheaders. To do so, continue in your terminal or command prompt and execute the following commands.

```
pipenv install djangorestframework
```

```
pipenv install djangocorsheaders
```

Activate virtual environment

```
pipenv shell
```

**Creating your first project**
Django provides a command that allows you to create an initial project file structure. Run the following command in your shell prompt. Remember to add the dot at the end, so that Django can create the server files in the same folder.

```
django-admin startproject server .
```

To initialize the database migrations for your Django project, execute the following command in your terminal or command prompt: it ensures that your database schema aligns with your project’s models and configurations.

```
python manage.py migrate
```

To create a new Django app named "blog," navigate to your project directory in the terminal or command prompt and execute the following command:

```
python manage.py startapp blog
```

To create the blog data model in your Django project, you can define  a model named Article in your models.py Here’s how you can do it:

```
from django.db import models
from django.contrib.auth.models import User

class Article(models.Model):
      title = models.CharField(max_length=200)
      body = models.TextField()
      	
      def __str__(self):
            return self.title

```
In this model, we’ve defined an Article class that inherits from Django’s models.Model. It includes two fields: title field which is a character field with a maximum length of 200 characters, and body which is a text field to store the content of the article.

**Activating the Application**
To activate the newly created "blog" application, along with the third party apps we installed, navigate to your project's settings file (usually located at settings.py) and add blog to the INSTALLED_APPS list. Ensure that it follows the specified format, including third-party apps such as rest_framework and corsheaders. Here's how your INSTALLED_APPS list should look:

```
INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    
    # Third party apps
    'rest_framework',
    'corsheaders',
    
    # Local apps
    'blog',
]

```

**Creating and Applying Migrations**
To create and apply migrations for your Django project, execute the following commands in your terminal or command prompt:

```
python manage.py makemigrations
```

```
python manage.py migrate
```

 Create a superuser
To create a superuser for accessing the Django administration site, execute the following command in your terminal or command prompt:

```
python manage.py createsuperuser
```
This command will prompt you to enter a username, email (optional), and password for the superuser account. Once created, you can use these credentials to log in to the Django administration site and manage your blog application.

**Django administration site**
Now that the Blog model is synchronized with the database, we can establish a straightforward administration site to manage blog posts.
As depicted in the diagram, the Article model is prominently displayed within the admin site.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7z2euxkg6du7rwkt55dx.png)

Let's proceed by adding two articles using the admin site.
 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xcau987prqkwlhlm2rci.png)

**Building List and Detail Views**
These views define endpoints for listing all articles and retrieving a specific article. The article_list view fetches all articles from the database and serializes them. The article_detail view retrieves a single article based on its id.

```
from django.shortcuts import get_object_or_404
from rest_framework.decorators import api_view
from rest_framework.response import Response

from .models import Article
from .serializers import ArticleSerializer

@api_view()
def article_list(request):
      all_posts = Article.objects.all()
      serializer = ArticleSerializer(all_posts, many=True)
      return Response(serializer.data)

@api_view()
def article_detail(request, id):
      post = get_object_or_404(Article, id=id)
      serializer = ArticleSerializer(post)
      return Response(serializer.data)

```

**Making sure the APIs are working**
To verify that the APIs are functioning correctly, you can utilize django rest framework’s built-in UI.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eota5zxxtmxr8fcg0k36.png)


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h03bf1qvsfaqcng1yph3.png)

**Setting up the Vue.js**
Installing Vue 3
To set up Vue.js for your project, begin by installing Vue 3 using npm. Execute the following command in your terminal or command prompt:

```
npm create vue@latest
```

During the installation process, make sure to select 'Yes' when prompted to add Vue Router. This will incorporate Vue Router into your Vue 3 project, enabling client-side routing for seamless navigation within your application.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j8g61xnaen7o7ik380bd.png)

```
cd frontend
npm install
```

To install Axios, a popular HTTP client for making requests in Vue.js applications, execute the following command in your terminal or command prompt:

```
npm install axios
```
In this main.js file, Axios is registered as $axios in the global properties of the Vue app. This allows Axios to be easily accessible from within Vue components using this.$axios. The app is then mounted to the DOM element with the ID app, completing the setup.

```
import './assets/main.css'
import './assets/main.scss'

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import axios from 'axios'

const app = createApp(App)
app.config.globalProperties.$http = axios

app.use(router)

app.mount('#app')

```

**Fetching data and displaying API data with Vue.js
**
```
<template>
  <div class="container-fluid">
    <div class="container mt-6">
          <article class="media box has-background-light" v-for="(post, index) in posts" :key="index">
            
            <div class="media-content">
              <div class="content">
                <p>
                  <p class="title is-4">
                    <RouterLink :to="{ name: 'full', params: { id: post.id } }">{{ post.title }}</RouterLink>
                    
                  </p>
                  {{ truncateWords(post.body, 30) }}
                </p>
              </div>
              
            </div>
            <div class="media-right">
              <figure class="image is-128x128">
                <img src="../assets/profile.jfif" alt="">
              </figure>
            </div>
          </article>

    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      posts: []
    }
  },

  methods: {
    async getPosts() {
      try {
        const response = await         this.$http.get('http://127.0.0.1:8000/articles/');
        this.posts = response.data;
        console.log(this.posts)
        
      } catch (error) {
        console.log(error)
        
      }
    },

    truncateWords(text, limit) {
      const words = text.split(' ');
      if (words.length > limit) {
        return words.slice(0, limit).join(' ') + '...';
      }
      return text;
    }
  },

  created() {
    this.getPosts();
  },
}
</script>

```

**Setting up the Vue Router**
In this setup, Vue Router is configured to use the HTML5 history mode for routing. Two routes are defined: one for the home page (/) which corresponds to the ListView component, and another for viewing detailed articles (/articles/:id)which corresponds to the DetailedView component.

```
import { createRouter, createWebHistory } from 'vue-router'
import ListView from '../views/ListView.vue'
import DetailedView from '../views/DetailedView.vue'

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    {
      path: '/',
      name: 'home',
      component: ListView
    },
    {
      path: '/articles/:id',
      name: 'full',
      component: DetailedView
    }
  ]
})

export default router

```

Detailed

```
<template>
  <div class="container">
    <h1 class="title">{{ article.title }}</h1>
    <p>{{ article.body }}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      article: null
    }
  },

  created() {
    this.fetchArticle();
  },

  methods: {
    async fetchArticle() {
      try {
        const id = this.$route.params.id;
        const response = await this.$http.get(`http://127.0.0.1:8000/articles/${id}/`)
        this.article = response.data;
        
      } catch (error) {
        console.log(error)
        
      }
    }
  }
}
</script>
```

**The Final Project**
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sjfd2weo2rj4aa4poeoc.png)


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8jct308z9o1w729pad6f.png)