Django + Vue 實(shí)現(xiàn)前后端分離單頁項(xiàng)目

一、背景

Django 作為后端框架,提供 api 接口,Vue.js 作為前端框架,代替 Django 薄弱的模板引擎,使得前后端完全分離,也適合單頁應(yīng)用的開發(fā)構(gòu)建。

本項(xiàng)目為一個(gè)單頁項(xiàng)目,實(shí)現(xiàn)功能:

  • 運(yùn)行 Django 項(xiàng)目,可查詢所有書籍
  • 可添加書籍,并實(shí)時(shí)刷新

項(xiàng)目整體目錄結(jié)構(gòu)

├── django_vue/ 
    ├── approot/        # django app
    ├── django_vue      # 項(xiàng)目主配置
    |    ├── settings.py    
    ├── frontend/       # 前端文件
    |    ├── build/ 
    |    ├── config/    
    |    ├── dist/  # 打包后前端文件放的位置
    |    ├── node_modules/  # 依賴、庫
    |    ├── src/   
    |    ├── static/    # 純靜態(tài)資源
    ├── db.sqlite3      
    ├── manage.py

二、環(huán)境準(zhǔn)備

  • 后端:
    • Django 2.1.1:可用 DRF
    • Python3.6.9
    • django-cors-headers:解決跨域問題
  • 前端:
    • Vue.js
    • Element-UI:餓了么推出的前臺(tái) UI 框架,有許多精美的 UI 文件
    • vue-resource:提供 ajax 請(qǐng)求服務(wù)
    • vue-cli 腳手架:快速搭建 Vue 項(xiàng)目

三、構(gòu)建 Django 項(xiàng)目

1、創(chuàng)建項(xiàng)目和 app

python manage.py startproject django_vue
python manage.py startapp approot

2、新增模型類 approot/models.py

from django.db import models


class Book(models.Model):
    name = models.CharField(max_length=64, verbose_name='書名')
    add_time = models.DateTimeField(auto_now_add=True, verbose_name='添加時(shí)間')

    def __str__(self):
        return self.name

遷移數(shù)據(jù)庫文件:

python manage.py makemigrations
python manage.py migrate

3、配置路由:

項(xiàng)目根目錄,django_vue/urls.py

from django.contrib import admin
from django.urls import path, include, re_path
from django.views.generic.base import TemplateView

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/', include('approot.urls')),  # 提供 api 接口,連接 app:approot
    
    # Vue 前端頁面,后面會(huì)用到
    re_path(r'^$', TemplateView.as_view(template_name='index.html')),
]

新增 approot/urls.py

from django.urls import path

from approot import views

urlpatterns = [
    path('book/list/', views.BookListView.as_view(), name='book_list'),
    path('book/create/', views.BookCreateView.as_view(), name='book_create'),
]

新增兩個(gè) api 接口(路由),分別用于查詢所有書籍和添加書籍。

4、視圖函數(shù) approot/views.py

import json

from django.core import serializers
from django.http import JsonResponse
from django.views.generic.base import View

from approot.models import Book


class BookListView(View):
    """書籍列表"""

    def get(self, request):
        res = {'code': 0, 'msg': '查詢成功', 'data': []}
        try:
            book_list = Book.objects.all()
            book_list = json.loads(serializers.serialize("json", book_list))
            res['data'] = book_list
        except Exception as e:
            res['code'] = -1
            res['msg'] = '查詢失敗'

        return JsonResponse(res)


class BookCreateView(View):
    """添加書籍"""

    def get(self, request):
        res = {'code': 0, 'msg': '添加成功', 'data': []}
        try:
            name = request.GET.get('name')
            Book.objects.create(name=name)
        except Exception as e:
            res['code'] = -1
            res['msg'] = '添加失敗'

        return JsonResponse(res)

這里采用的是 Django CBV,當(dāng)然也可以使用 DRF。

5、配置文件:django_vue/settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    'approot',
    'corsheaders',  # 跨域訪問設(shè)置
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'corsheaders.middleware.CorsMiddleware',  # 跨域訪問設(shè)置
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

CORS_ORIGIN_ALLOW_ALL = True  # 新增的跨域訪問設(shè)置

# 模板文件
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        # 'DIRS': [os.path.join(BASE_DIR, 'templates')]
        'DIRS': ['frontend/dist'],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

LANGUAGE_CODE = 'zh-Hans'

TIME_ZONE = 'Asia/Shanghai'

STATIC_URL = '/static/'

# 靜態(tài)文件
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, "frontend/dist/static"),
]

這里需要提前安裝 pip install django-cors-headers,用于解決 Vue 模板向 Django 后端請(qǐng)求跨域問題。

至此后端基本已配置完畢,可用 postman 測試下接口是否能夠正常返回。

注意:模板文件和靜態(tài)文件中的 dist 目錄,會(huì)在 Vue 打包時(shí)生成

四、構(gòu)建 Vue.js 前端項(xiàng)目

前端總共可以分為以下幾個(gè)步驟:

  • 安裝 node.js、nrm,更換源為淘寶源

  • 安裝 vue-cli,并 使用 vue-cli 腳手架快速生成 Vue.js 模板文件

  • 安裝 Element-UI、vue-resource 等依賴

  • 運(yùn)行測試:npm run dev

  • 打包:npm run build

安裝 node.js

node.js 安裝沒什么好說的,一路安裝即可,記得要勾選 Add PATH,添加到環(huán)境變量。

官網(wǎng)地址

安裝成功后,測試:

C:\Users\hj>node --version
v12.18.0

C:\Users\hj>npm --version
6.14.4

nrm 安裝

nrm 用于提供一些常用 NPM 包鏡像地址,包括 cnpm、淘寶鏡像等

npm i nrm -g    # 全局安裝
nrm ls  # 查看當(dāng)前可用鏡像源地址
nrm use taobao  # 切換鏡像源為 淘寶,速度會(huì)快很多

C:\Users\hj>nrm ls

  npm -------- https://registry.npmjs.org/
  yarn ------- https://registry.yarnpkg.com/
  cnpm ------- http://r.cnpmjs.org/
* taobao ----- https://registry.npm.taobao.org/
  nj --------- https://registry.nodejitsu.com/
  npmMirror -- https://skimdb.npmjs.com/registry/
  edunpm ----- http://registry.enpmjs.org/

vue-cli 快速生成項(xiàng)目

1、安裝

npm install -g vue-cli      // 全局安裝

2、創(chuàng)建工程項(xiàng)目

cd django_vue   // 切換到后端項(xiàng)目根目錄
vue init webpack frontend   // 初始化項(xiàng)目,需要手動(dòng)配置一系列配置,如:項(xiàng)目描述、作者、打包方式,是否使用 ESLint 規(guī)范代碼等

目錄結(jié)構(gòu)

├── frontend/   
    ├── build/      # webpack 編譯任務(wù)配置文明:開發(fā)環(huán)境與生產(chǎn)環(huán)境
    ├── config
    |    ├── index.js   # 項(xiàng)目核心配置
    ├── src/
    |    ├── main.js    # 程序入口文件
    |    ├── App.vue    # 程序入口 vue 組件
    |    ├── components/    # 組件
    |    ├── assets/    # 資源文件夾,一般放圖片之類的
    |    ├── router/    # 路由
    ├── static/     # 純靜態(tài)資源
    ├── index.html
    └── node_modules/   # 項(xiàng)目中的依賴模塊
    └── .babelrc    # bael 配置文件
    └── .editorconfig   # 編輯配置文件
    └── .gitignore  # 忽略文件
    └── package.json    # 項(xiàng)目文件,記載一些命令和依賴還有簡要項(xiàng)目描述信息
    └── README.md   
    └── index.html  # 入口模板文件

安裝其他依賴

Element-UI

提供 UI 組件

npm install element-ui --save
npm install element-theme -g    # 全局安裝

引入 frontent/src/main.js

import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(ElementUi)

vue-resource

用于向后端發(fā)起請(qǐng)求

 npm i vue-resource --save 

引入 frontent/src/main.js

import VueResource from 'vue-resource'
Vue.use(VueResource)

新增組件 Home

frontend/src/components/ 下添加新的組件 Home.vue

<template>
  <div class="home">
    <el-row display="margin-top: 10px">
      <el-input v-model="input" placeholder="請(qǐng)輸入書名" style="display:inline-table; width: 30%; float:left"></el-input>
      <el-button type="primary" @click="addBook()" style="float:left; margin: 2px;">新增</el-button>
    </el-row>

    <el-row>
      <el-table :data="bookList" style="width: 100%" border>
        <el-table-column prop="id" label="編號(hào)" min-width="100">
          <template scope="scope"> {{ scope.row.pk }}</template>
        </el-table-column>
        <el-table-column prop="book_name" label="書名" min-width="100">
          <template scope="scope"> {{ scope.row.fields.name }}</template>
        </el-table-column>
        <el-table-column prop="add_time" label="添加時(shí)間" min-width="100">
          <template scope="scope"> {{ scope.row.fields.add_time }}</template>
        </el-table-column>
      </el-table>
    </el-row>
  </div>
</template>

<script>
  export default {
    name: "home",
    data () {
      return {
        input: '',
        bookList: [],
      }
    },
    mounted: function(){
      this.showBooks()
    },
    methods: {
      addBook() {
        this.$http.get('http://127.0.0.1:8000/api/book/create?name=' + this.input)
          .then((response) => {
            var res = JSON.parse(response.bodyText);
            if (res.code === 0) {
              this.showBooks()
            } else {
              this.$message.error('新增書籍失敗,請(qǐng)重試');
              console.log(res['msg']);
            }
          })
      },
      showBooks() {
        this.$http.get('http://127.0.0.1:8000/api/book/list')
          .then((response) => {
            var res = JSON.parse(response.bodyText);
            console.log('查詢書籍:', res);
            if (res.code === 0) {
              this.bookList = res['data']
            } else {
              this.$message.error("查詢書籍失??!");
              console.log(res['msg']);
            }
          })
      }
    }
  }
</script>

<style scoped>
  h1, h2 {
    font-weight: normal;
  }

  ul {
    list-style-type: none;
    padding: 0;
  }

  li {
    display: inline-block;
    margin: 0 10px;
  }

  a {
    color: #42b983;
  }
</style>

其中 el-rowElement-UI 中所有,$.httpvue-resource 所有。該組件作用:

  • template:展示一個(gè)表格(書籍信息)
  • script:向后端發(fā)送請(qǐng)求,請(qǐng)求數(shù)據(jù)(查詢、新增書籍)

注意:這里使用到 Element-UI、vue-resource,還需要在 frontend/src/router/index.js 進(jìn)行引用

配置前端路由

frontend/src/router/index.js

import Vue from 'vue'
import Router from 'vue-router'
// import HelloWorld from '@/components/HelloWorld'
import Home from '@/components/Home'    // 一定要先引用 Home 組件,否則運(yùn)行失敗
import ElementUi from 'element-ui'
// import '@/theme-et/index.css'
Vue.use(ElementUi)

Vue.use(Router)

export default new Router({
  // routes: [
  //   {
  //     path: '/',
  //     name: 'HelloWorld',
  //     component: HelloWorld
  //   }
  // ]

  routes: [
    {
      path: '/',
      name: 'Home',     // Home 組件名稱
      component: Home
    }
  ]
})

打包測試

在此之前我們已經(jīng)用 postman 測試了后端 api 接口,這里將測試前端能夠正常展示頁面。

1、測試運(yùn)行前端項(xiàng)目

cd frontend
npm run dev

訪問:http://localhost:8080/F12 診斷查看下是否報(bào)錯(cuò)。

2、打包

cnpm install  // 安裝依賴,或 npm install

cd frontend
npm run build   // 打包,會(huì)在 frontend/ 下生成 dist 目錄,其中有static/、index.html

打包成功的信息:

image

關(guān)閉前端 npm run dev,運(yùn)行后端 python manage.py runserver,訪問 http://127.0.0.1:8000/#/ 即可,若一切正常的話可以看到如下界面:

image

項(xiàng)目源碼:https://github.com/hj1933/django_vue

參考文章

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容