整合 Django 2.x + Vue.js 框架快速搭建web項目

1、 背景

在工做中咱們常常需要構建一些基於web的項目,例如內部測試平臺、運維繫統等。本篇主要介紹如何使用後端Django + 前端Vue.js的技術棧快速地搭建起一套web項目的框架。css

爲何使用Django和Vue.js?html

Django是Python體系下最成熟的web框架之一,因爲Python語言的易用性和受衆面廣,Django框架也因其可以快速開發網站應用的特性成爲了中小型網站開發框架首選。且Django具有的數據分析( Pandas )、任務隊列( Celery )、Restful API( Django REST framework )、ORM(相似java的hibernate)等一衆功能都使得用戶在面對任何建站需求時都可以駕輕就熟。前端

Vue.js是當下很火的一個JavaScript MVVM庫,它是以數據驅動和組件化的思想構建的。相比於Angular.js,Vue.js一樣支持雙向綁定、mustache標籤語法等特性,並提供了更加簡潔、更易於理解的API,使得咱們可以快速地上手並使用Vue.js。vue

本篇使用Vue.js做爲前端框架,代替Django自己較爲孱弱的模板引擎,Django則做爲服務端提供api接口,使得先後端實現徹底分離,更適合單頁應用的開發構建。java

2、 項目使用的相關技術和環境

  • 操做系統:Windows 10node

  • 數據庫:MySQL 5.7python

  • 後端:Django 2.x 框架,Python 3.xmysql

  • 前端:HTML/CSS/jQuery/JavaScript/Vue.jswebpack

注:python 相關的模塊,經過使用 python 自帶的 pip 安裝器安裝。前端相關的第三方包,咱們使用 node 自帶的 npm 包管理器安裝。ios

3、 構建Django項目

一、 進入一個安全目錄,打開命令行提示符 CMD,輸入命令:

django-admin startproject myproject
複製代碼

目錄結構:

二、進入 myproject 這個目錄,新建一個虛擬環境並激活:

cd myproject
# 需安裝 Virtualenv 
pip install virtualenv
# 新建虛擬環境
virtualenv env
# 激活虛擬環境
.\env\Scripts\activate
# 安裝 django
pip install django
# 提早安裝好所需模塊
pip install requests
複製代碼

激活虛擬環境的標記:

132LcQ.md.png

三、建立一個app:

python manage.py startapp myapp
複製代碼

目錄結構:

Snipaste 2020 01 31 17 09 41

四、在myproject下的settings.py配置文件中,把默認的sqlite3數據庫換成咱們的mysql數據庫:

# Database
# https://docs.djangoproject.com/en/2.2/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'myproject',
        'USER': 'root',
        'PASSWORD': 'admin',
        'HOST': '127.0.0.1',
    }
}
# 舒適小提示
# 將上面的 user 和 password 換成你本身的,同時保證本身有一個數據庫名爲 myproject 的數據庫
複製代碼

並把app加入到installed_apps列表裏:

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

複製代碼

五、 在app目錄下的models.py裏咱們簡單寫一個model以下:

from django.db import models

# Create your models here.
class Book(models.Model):
    book_name = models.CharField(max_length=64)
    add_time = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.book_name
複製代碼

只有兩個字段,書名book_name和添加時間add_time。若是沒有指定主鍵的話django會自動新增一個自增id做爲主鍵

在命令行窗口輸入命令:

# 安裝 mysqlclient
pip install mysqlclient
# 數據庫遷移
python manage.py makemigrations myapp
python manage.py migrate
複製代碼

13hZHs.md.png

經過命令行查詢數據庫,看到book表已經自動建立了/經過圖形化工具 Navicat Premium 12 直接查看是否有這個數據庫表:

image

六、 在app目錄下的 views.py 裏咱們新增兩個接口,一個是show_books返回全部的書籍列表(經過JsonResponse返回能被前端識別的json格式數據),二是add_book 接受一個get請求,往數據庫裏添加一條book數據

from django.shortcuts import render

# Create your views here.
# 須要導入相關的模塊
from django.http import JsonResponse
from django.views.decorators.http import require_http_methods
from django.core import serializers
import requests
import json

from .models import Book

@require_http_methods(["GET"])
def add_book(request):
    response = {}
    try:
        book = Book(book_name=request.GET.get('book_name'))
        book.save()
        response['msg'] = 'success'
        response['error_num'] = 0
    except  Exception as e:
        response['msg'] = str(e)
        response['error_num'] = 1

    return JsonResponse(response)

@require_http_methods(["GET"])
def show_books(request):
    response = {}
    try:
        books = Book.objects.filter()
        response['list']  = json.loads(serializers.serialize("json", books))
        response['msg'] = 'success'
        response['error_num'] = 0
    except  Exception as e:
        response['msg'] = str(e)
        response['error_num'] = 1

    return JsonResponse(response)
複製代碼

能夠看出,在ORM的幫忙下,咱們的接口實際上不須要本身去組織SQL代碼

補充:有小夥伴說「添加書本用GET方法是否是不太合適啊」,首先,原做者也是採用 get 請求添加書本,因此我沒有將其改爲 post 請求的想法,既然有小夥伴提出來,那我這裏說一下若是要將添加書本的方法改爲post 的步驟。 首先,基於上面的代碼,修改 app目錄下的 views.py的內容:

# @require_http_methods(["GET"])
@require_http_methods(["POST"]) #修改
def add_book(request):
    response = {}
    try:
        # book = Book(book_name=request.GET.get('book_name'))
        book = Book(book_name=request.POST.get('book_name')) #修改
        book.save()
        response['msg'] = 'success'
        response['error_num'] = 0
    except  Exception as e:
        response['msg'] = str(e)
        response['error_num'] = 1

    return JsonResponse(response)
複製代碼

修改事後,若是直接在 postman 軟件上測試 add_book 這個接口會出現 403 錯誤,如圖:

注:測試 post 請求時,按照下圖操做便可:

解決辦法:

七、在app目錄下,新增一個urls.py文件,把咱們新增的兩個接口添加到路由裏:

from django.urls import path,re_path
# 導入 myapp 應用的 views 文件
from . import views

urlpatterns = [
    re_path(r'add_book$', views.add_book),
    re_path(r'show_books$', views.show_books)
]
複製代碼

此外,咱們還要把app下的urls添加到project下的urls中,才能完成路由:

from django.contrib import admin
from django.urls import path,re_path
from django.conf.urls import url, include
import myapp.urls

urlpatterns = [
    path('admin/', admin.site.urls),
    re_path(r'^api/', include(myapp.urls)),
]
複製代碼

八、輸入命令,啓動服務器:

python manage.py runserver
複製代碼

測試接口方式一:經過瀏覽器,因爲這些請求都是 get 請求,因此能夠直接在瀏覽器上直接看到服務器返回的結果 直接在瀏覽器地址欄輸入 http://localhost:8000/api/show_books,你會看到下面這張圖片的效果:

image

直接在瀏覽器地址欄輸入http://localhost:8000/api/add_book?book_name=test,你會看到下面這張圖片的效果:

image
測試接口方式二:postman

經過postman測試一下咱們剛纔寫的兩個接口:

add_book 接口:

image

show_books 接口:

image

學習連接:

4、 構建Vue.js前端項目

前提:安裝了 NODE,安裝好以後會自帶 npm 包管理器

一、 先用npm安裝vue-cli腳手架工具(vue-cli是官方腳手架工具,能迅速幫你搭建起vue項目的框架):

npm install -g vue-cli
複製代碼

安裝好後,在project項目根目錄下,新建一個前端工程目錄:

vue-init webpack appfront  //安裝中把vue-router選上,咱們需要它來作前端路由
複製代碼

如今咱們能夠看到整個文件目錄結構是這樣的:

image

二、 在目錄src下包含入口文件main.js,入口組件App.vue等。後綴爲vue的文件是Vue.js框架定義的單文件組件,其中標籤中的內容能夠理解爲是類html的頁面結構內容,標籤中的是js的方法、數據方面的內容,而則是css樣式方面的內容:

img

三、安裝並引入 element ui 和 axios

首先在命令行當中安裝 element ui 和 axios

npm install element-ui
npm install axios
複製代碼

而後在 src/main.js 在原有代碼的基礎上,添加下面代碼:

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

// 引入 axios
import axios from 'axios'
Vue.prototype.$axios = axios

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  axios,
  components: { App },
  template: '<App/>'
})

複製代碼

四、在src/component文件夾下新建一個名爲Home.vue的組件,經過調用以前在Django上寫好的api,實現添加書籍和展現書籍信息的功能。

<template>
  <div class="home">
    <el-row display="margin-top:10px">
        <el-input v-model="input" placeholder="請輸入書名" 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="編號" min-width="100">
            <template slot-scope="scope"> {{ scope.row.pk }} </template>
          </el-table-column>
          <el-table-column prop="book_name" label="書名" min-width="100">
            <template slot-scope="scope"> {{ scope.row.fields.book_name }} </template>
          </el-table-column>
          <el-table-column prop="add_time" label="添加時間" min-width="100">
            <template slot-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.$axios.get('http://127.0.0.1:8000/api/add_book?book_name=' + this.input)
        .then((res) => {
            // console.log(res)
            var res = res.data;
            if (res.error_num == 0) {
              this.showBooks()
            } else {
              this.$message.error('新增書籍失敗,請重試')
              console.log(res['msg'])
            }
        })
    },
    showBooks(){
      this.$axios.get('http://127.0.0.1:8000/api/show_books')
        .then((res) => {
            var res = res.data;
            // console.log(res)
            if (res.error_num == 0) {
              this.bookList = res['list']
            } else {
              this.$message.error('查詢書籍失敗')
              console.log(res['msg'])
            }
        })
    }
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<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>

複製代碼

五、若是發現列表抓取不到數據,多是出現了跨域問題,打開瀏覽器console確認:

img

這時候咱們需要在Django層注入header,用Django的第三方包django-cors-headers來解決跨域問題:

pip install django-cors-headers
複製代碼

修改 settings.py

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'corsheaders.middleware.CorsMiddleware', # 新增的中間件
    '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
複製代碼

六、在前端工程目錄下,輸入npm run dev啓動node自帶的服務器,瀏覽器會自動打開, 咱們能看到頁面:

image

七、在前端工程目錄下,輸入npm run build,若是項目沒有錯誤的話,就可以看到全部的組件、css、圖片等都被webpack自動打包到dist目錄下了:

image

5、 整合Django和Vue.js

目前咱們已經分別完成了 Django 後端Vue.js 前端工程的建立和編寫,但實際上它們是運行在各自的服務器上,和咱們的要求是不一致的。所以咱們需要把Django的TemplateView指向咱們剛纔生成的前端dist文件便可。

我我的理解,若是要想實現先後端分離的項目,基本不用看下面的內容,Vue.js 實現項目的前臺,Django 實現項目的後臺,提供 api。

一、 找到project目錄的urls.py,使用通用視圖建立最簡單的模板控制器,訪問 『/』時直接返回 index.html:

from django.contrib import admin
from django.urls import path,re_path
from django.conf.urls import url, include
import myapp.urls
from django.views.generic import TemplateView # 新增的

urlpatterns = [
    path('admin/', admin.site.urls),
    re_path(r'^api/', include(myapp.urls)),
    re_path(r'^$', TemplateView.as_view(template_name="index.html")), # 新增的
]

複製代碼

二、 上一步使用了Django的模板系統,因此須要配置一下模板使Django知道從哪裏找到index.html。在project目錄的settings.py下:

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': ['appfront/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',
            ],
        },
    },
]
複製代碼

三、 咱們還須要配置一下靜態文件的搜索路徑。一樣是project目錄的settings.py下:

# Add for vuejs
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, "appfront/dist/static"),
]
複製代碼

四、 配置完成,咱們在project目錄下輸入命令python manage.py runserver,就可以看到咱們的前端頁面在瀏覽器上展示:

image

注:服務的端口已是Django服務的8000而不是node服務的8080了

6、 部署

因爲python的跨平臺特性,所以理論上只要在服務器上安裝好全部的依賴,直接把項目目錄拷貝到服務器上便可運行。這裏只提一點:若是爲項目配置了nginx做爲反向代理,那麼要在nginx中配置全部的靜態文件path都指向Django項目中配置的靜態文件url,在settings.py中可配置url路徑:

# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.11/howto/static-files/

STATIC_URL = '/static/'
複製代碼

7、 其餘

實例項目的源碼均可以點擊下面的連接進入下載:

github.com/xyyojl/djan…

最後,特別感謝原做者 .╂遊牧 寫的博客 整合 Django + Vue.js 框架快速搭建web項目,整篇博客文章參考原做者寫的博客,採用最新的技術棧重寫那個實例項目,跟原來的會有寫不同。

相關文章
相關標籤/搜索