DRF_Vue3_ElementPlus_TypeScript部署搭建

后端开发环境

后端开发环境为:

  • Win 10(64位)
  • Python 3.7.2
  • Django 3.1.3
  • djangorestframework 3.12.2

安装Django

在虚拟环境下,输入命令 pip install django==3.1.3

(venv) E:\drf> pip install django==3.1.3

Collecting django==3.1.3
  Using cached 
  ...
  ...
Successfully installed django-3.1.3

系统打印出以上文字表示 Django 安装成功了。(提示符以 (venv) 开头)

由于国内复杂的网络环境, Pip 的下载可能非常缓慢甚至失败。国内用户请更换国内的镜像下载源。

创建Django项目

还是在虚拟环境下,在drf文件夹中创建一个叫 drf_vue_blog 的Django项目:

(venv) E:\drf> django-admin startproject drf_vue_blog

查看drf文件夹,发现多了drf_vue_blog文件夹,其结构应该是这样:

drf_vue_blog
│  manage.py
│
└─drf_vue_blog
    │  settings.py
    │  urls.py
    │  wsgi.py
    └─ __init__.py

这就是我们刚创建出来的项目了。

运行Django服务器

Django 自带一个轻量的 Web 开发服务器,被叫做 runserver。

开发服务器是为了让你快速开发Web程序,通过它可以避开配置生产环境的服务器的繁琐环节。

开发服务器会自动的检测代码的改变,并且自动加载它,因此在修改代码后不需要手动去重启服务器,非常的方便。

要运行这个服务器,首先要进入drf_vue_blog文件夹,即含有manage.py文件的那个:

(venv) E:\drf> cd drf_vue_blog
(venv) E:\drf\drf_vue_blog>

输入命令python manage.py runserver

(venv) E:\drf\drf_vue_blog> python manage.py runserver
Performing system checks...
...
Django version 3.1.3, using settings 'drf_vue_blog.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.

系统打印出这些信息,说明服务器启动成功了。

打开 Chrome 浏览器,输入http://127.0.0.1:8000/ ,网页中看到一个绿色的小火箭,恭喜你,项目已经正常运行了。

Django切换Mysql数据库

1、安装MySQL数据库

2、在项目的settings.py文件中修改数据库设置

DATABASES = {
​ ‘default’: {
​ ‘ENGINE’: ‘django.db.backends.mysql’,
​ ‘NAME’: ‘dbname’,
​ ‘USER’:‘root’,
​ ‘PASSWORD’:‘123456’,
​ ‘HOST’:‘127.0.0.1’,
​ ‘PORT’:‘3306’,
​ }
}

3、安装mysql python库

pip install PyMySQL

# 网络原因安装不上的话用这个命令
pip install pymysql -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com

4、然后在项目的__init__.py文件下输入

import pymysql
pymysql.install_as_MySQLdb()pymysql.install_as_MySQLdb()

5、然后运行就不会出错了

python3 manage.py makemigrations
python3 manage.py migrate

drf 开发预备

首先在命令行创建博客文章的 App:

(venv) > python manage.py startapp article

创建一个简单的博客文章模型:

# article/models.py

from django.db import models
from django.utils import timezone

# 博客文章 model
class Article(models.Model):
    # 标题
    title = models.CharField(max_length=100)
    # 正文
    body = models.TextField()
    # 创建时间
    created = models.DateTimeField(default=timezone.now)
    # 更新时间
    updated = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.title

前后端分离中涉及到一个重要的概念:序列化(后面讲解)。Django 有一个非常优秀的库 djangorestframework(后称 DRF)。它可以帮我们封装好序列化的底层实现,让开发者专注于业务本身。

安装 DRF 及其他依赖库:

pip install djangorestframework==3.12.2
pip install markdown==3.3.3
pip install django-filter==2.4.0

然后将 App 注册列表:

# drf_vue_blog/settings.py

INSTALLED_APPS = [
    ...

    'rest_framework',
    'article',
]

接着还需要添加 DRF 的登录视图,以便 DRF 自动为你的可视化接口页面生成一个用户登录的入口:

后续开发出接口页面后试着把这行代码删掉,看看会有什么不同。

# drf_vue_blog/urls.py

...
from django.urls import include

urlpatterns = [
    ...
    path('api-auth/', include('rest_framework.urls')),
]

最后记得数据迁移:

(venv) > python manage.py makemigrations
(venv) > python manage.py migrate

准备工作就做好了。

序列化与Django

前后端分离的核心思想之一,就是两端交互不通过模板语言,而只传输需要的数据。因此问题就来了。

在 Django 程序的运行过程中,变量都是存储在服务器的内存中;更要命的是,后端 Django 程序中存储的是 Python 变量,而前端的浏览器中是 Javascript 变量,这两者是无法直接通过你家的网线进行传递和交流的。因此需要规定一个“标准格式”,前后端都根据这个标准格式,对资源进行保存、读取、传输等操作。

JSON 就是这种标准格式之一。它很轻量,表示出来就是个字符串,可以直接被几乎所有的语言读取,非常方便。

举个例子,把 Python 对象转换为 JSON ,这被称为序列化(serialization):

>>> import json
>>> person = dict(name='Trump', age=82)
>>> json.dumps(person)
# 这是个字符串
'{"age": 82, "name": "Trump"}'

把 JSON 转换为 Javascript 对象,被称为反序列化

>>> json_str = '{"age": 82, "name": "Trump"}'
>>> json.loads(json_str)
# 这是个 js 对象
{'age': 82, 'name': 'Trump'}

总之,把变量从内存中变成可存储或传输的过程称之为序列化,反过来把变量内容从序列化的对象重新读到内存里称之为反序列化

回顾 Django 传统流程对一个网络请求的处理:

def a_list(request):
    articles = Article.objects.all()
    return render(..., context={'articles': articles})

视图函数将数据作为上下文返回,通过模板引擎将上下文渲染为页面中的数据。

Restful 的处理流程仅增加了一步,即对数据序列化的处理:

def a_list(request):
    articles = Article.objects.all()
    # 序列化数据
    serializer = Serializer(articles, many=True)
    return JsonResponse(serializer.data, safe=False)

数据被序列化为 Json 字符串,直接交由前端处理。

这就是前后端分离的雏形:

  • 后端提供数据;
  • 前端专注于操作数据、渲染页面。

这里又出现了与前后端分离联系很紧密的新概念:Rest(表现层状态转化) 和 Restful。Restful 架构是指客户端和服务器之间的交互、操作符合 Rest 规范,即:每一个URI代表一种资源;客户端和服务器之间,传递资源的表现层;客户端通过四个HTTP动词,对服务器端资源进行操作,实现"表现层状态转化"。有点难理解,推荐读物阮一峰的博客和知乎文章。

Hello World

按照这个思路,我们来写一个文章列表接口吧。

article 模型在前面已经写好了,接下来写视图:

# article/views.py

from django.http import JsonResponse
from article.models import Article
# 这个 ArticleListSerializer 暂时还没有
from article.serializers import ArticleListSerializer

def article_list(request):
    articles = Article.objects.all()
    serializer = ArticleListSerializer(articles, many=True)
    return JsonResponse(serializer.data, safe=False)

代码一共就 3 行:

  • 取出所有文章的 QuerySet
  • 根据 QuerySet 数据,创建一个序列化器;
  • 将序列化后的数据以 Json 的形式返回。

跟说好的一样,返回的东西不再是传统的模板了,而是 Json 数据。

代码里的序列化器 ArticleListSerializer 我们还没写,接下来就完成它。

新建一个 article/serializers.py 的文件,写入下面的代码:

# article/serializers.py

from rest_framework import serializers

class ArticleListSerializer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    title = serializers.CharField(allow_blank=True, max_length=100)
    body = serializers.CharField(allow_blank=True)
    created = serializers.DateTimeField()
    updated = serializers.DateTimeField()

序列化类看起来与 Django 的 Form 表单类非常的类似。它指定了接口数据中各个字段的具体类型,自动对请求响应中的数据进行序列化和反序列化的转换。其底层实现逻辑已经由 DRF 框架封装好了,在入门阶段通常不需要你操心。

最后将各级 urls.py 配置好:

# drf_vue_blog/urls.py
...
urlpatterns = [
    ...
    path('api/article/', include('article.urls', namespace='article')),
]

以及:

# article/urls.py

from django.urls import path
from article import views

app_name = 'article'

urlpatterns = [
    path('', views.article_list, name='list'),
]

代码部分就完成了。

接下来创建一个管理员用户,并在后台中随意给 article 添加几个测试数据,启动服务器并在浏览器中访问 http://127.0.0.1:8000/api/article/,可以看到页面中返回的 Json 字符串如下(稍作了排版):

[
    {
        "body": "Maybe say somthing here...",
        "created": "2020-06-15T09:24:18Z",
        "id": 1,
        "title": "My first post",
        "updated": "2020-06-15T09:24:38.622789Z"
    },
    {
        "body": "Second test..",
        "created": "2020-06-15T09:24:38Z",
        "id": 2,
        "title": "Another post",
        "updated": "2020-06-15T09:24:58.253400Z"
    },
    {
        "body": "Some content also..",
        "created": "2020-06-15T09:24:58Z",
        "id": 3,
        "title": "Third article with awesome things",
        "updated": "2020-06-15T09:25:25.602840Z"
    }
]

虽然简陋,但是你已经成功完成了一个简单的接口。

创建管理员用户、在后台中添加数据是非常基础的内容,如果不清楚请参照 这篇文章。

如果你进入后台发现页面没有样式,那是因为静态文件未配置路由引起的。解决这个问题请参考 配置静态文件。

前端开发环境

准备工作

开发前端时会用到 npm(类似 Python 的包管理工具 Pip),这是 Node.js 官方提供的包管理工具。

所以准备工作的第一步,安装 Node.js,下载地址在官网,安装时基本就是一路 next。

完毕后打开命令行(依旧默认是 PowerShell),输入:

> npm -v
6.14.9

显示版本号就表示安装成功了。

npm 站点在国外,如果你遇到安装速度慢的问题,可以用指令 npm config set registry https://registry.npm.taobao.org 修改为国内镜像源。

接下来就可以安装 Vue 的命令行工具,它可以帮助我们方便的搭建 Vue 项目的骨架:

> npm install -g @vue/cli

# 这里省略一段神秘的安装文字...

> vue --version
@vue/cli 4.5.9

同样的,显示版本号就表示安装成功了。

深入了解见Vue-Cli文档。

如果运行命令报错"vue: 无法加载文件 C:\xxx\vue.ps1,因为在此系统上禁止运行脚本",则需要通过 PowerShell 解除 Execution_Policies 运行策略限制。方法见这里。

进入 Django 项目的根目录,用命令行工具搭建 Vue 骨架:

# 改为你的项目根路径
> cd D:\Developer\Py\drf_vue_blog
> vue create frontend

一定要选择安装 Vue 3:

前面说了,Vue 3 和 Vue 2 变化比较大,装错了后面章节的代码可能都跑不起来。

# 选择第二项,即安装 Vue3

Vue CLI v4.5.9
? Please pick a preset:
  Default ([Vue 2] babel, eslint)
> Default (Vue 3 Preview) ([Vue 3] babel, eslint)
  Manually select features 

然后等待安装完成:

Vue CLI v4.5.9
Creating project in D:\Developer\Py\drf_vue_blog\frontend.
Installing CLI plugins. This might take a while...

...

added 1243 packages from 946 contributors in 22.141s

63 packages are looking for funding
  run `npm fund` for details

Invoking generators...
Installing additional dependencies...

added 75 packages from 83 contributors in 9.281s

69 packages are looking for funding
  run `npm fund` for details

Running completion hooks...

Generating README.md...

Successfully created project frontend.
Get started with the following commands:

 $ cd frontend
 $ npm run serve

出现这段文字说明 Vue 安装完成了。

与 Django 需要运行服务器类似,作为前后端分离的项目,在开发时前端同样也需要运行前端的服务器。

根据文字提示,进入 frontend 目录,运行 Vue 的开发服务器:

> cd frontend
> npm run serve

 DONE  Compiled successfully in 2134ms

  App running at:
  - Local:   http://localhost:8080/
  - Network: http://172.20.10.2:8080/

  Note that the development build is not optimized.
  To create a production build, run npm run build.

http://localhost:8080/ 即可看到 Vue 的欢迎页面了。

进行后续章节的开发时,我们需要同时运行后端 http://127.0.0.1:8000/ 和前端 http://localhost:8080/ 两个服务器,别搞混了。

安装Axios

虽然现在前后端 Django + Vue 都有了,但还缺一个它们之间通信的手段。Vue 官方推荐的是 axios 这个前端库。

命令行进入 frontend 目录,安装 axios:

> npm install axios

喝口茶就安装完成了。

解决跨域

跨域问题是由于浏览器的同源策略(域名,协议,端口均相同)造成的,是浏览器施加的安全限制。说简单点,Vue 服务器端口(8080)和 Django 服务器端口(8000)不一致,因此无法通过 Javascript 代码请求后端资源。

解决办法有两种。

第一种方法是创建 frontend/vue.config.js 文件并写入:

module.exports = {
    devServer: {
        proxy: {
            '/api': {
                target: `http://127.0.0.1:8000/api`,
                changeOrigin: true,
                pathRewrite: {
                    '^/api': ''
                }
            }
        }
    }
};

这个 Vue 的配置文件给前端服务器设置了代理,即将 /api 地址的前端请求转发到 8000 端口的后端服务器去,从而规避跨域问题。

另一种方法是在后端引入 django-cors-middleware 这个库,在后端解决此问题。

此方法具体步骤百度很多,就不赘述了。

两种解决方法都可以,本文将选择第一种即前端代理的方法。

安装ElementPlus

npm install element-plus --save
# 进入mian.ts,完整引入
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'

app.use(ElementPlus)

Vue结构

本教程假定读者已经具有了 Javascript / Html / Css 等前端基础知识,因此不会展开讲相关内容。但为了理解 Vue 的基本结构,让我们来看三个重要的文件。

index.html

此文件路径位于 frontend/public/index.html,内容如下:

DOCTYPE html>
<html lang="en">
  <head>
    ...
  head>
  <body>
    ...
    <div id="app">div>
    
  body>
html>

这个页面是整个前端工程提供 html 的入口,里面的

是 Vue 初始化应用程序的根容器。

不过在前端工程化的思想中,我们很少会直接去写这类 html 文件。

main.js

此文件位于 frontend/src/main.js ,内容如下:

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

createApp(App).mount('#app');

它的作用就是把后续你要写的 Vue 组件挂载到刚才那个 index.html 中。

如果你有些前端的初始化配置,都可以写到这里。

App.vue

此文件位于 frontend/src/App.vue ,内容如下:

<template>
    <img alt="Vue logo" src="./assets/logo.png">
    <HelloWorld msg="Welcome to Your Vue.js App"/>
template>

<script>
    import HelloWorld from './components/HelloWorld.vue'
    export default {
        name: 'App',
        components: {
            HelloWorld
        }
    }
script>

<style>
    #app {
        font-family: Avenir, Helvetica, Arial, sans-serif;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
        text-align: center;
        color: #2c3e50;
        margin-top: 60px;
    }
style>

仔细看一下,这个文件似乎就是对应 Vue 的欢迎页面嘛!

Vue 采用组件化的思想,把同一个组件的内容打包到一起。比如这个默认的 App.vue 文件,明显