技术栈

node.js, vue.js, axios, python, django, orm, restful api, djangorestframework, mysql, nginx, jenkins.

环境配置

操作系统

Windows 7 旗舰版,Service Pack 1。

前端

Node.js

>node -v
v12.18.0
>npm -v
6.14.4

Vue.js

>vue -V(大写)
@vue/cli 4.4.1

后端

Python

>python --version
Python 3.7.2

Django

>python -m django --version
3.0.7

数据库

MySQL

>mysqladmin --version
mysqladmin  Ver 8.0.19 for Win64 on x86_64 (MySQL Community Server - GPL)

命令行登录 mysql,

>mysql -u root -p
Enter password: ******

查询数据库,

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| new_schema         |
| performance_schema |
| sakila             |
| sys                |
| world              |
+--------------------+
7 rows in set (0.00 sec)

代理

Nginx

在 nginx 安装目录执行start nginx,浏览器访问http://localhost:80

持续集成

Jenkins

安装后,会自动打开http://localhost:8080/

软件安装过程就不赘述了,聪明的你一定知道怎么安。

项目搭建

本文的目的是走通整个项目的链路,于是会 “弱化” 掉系统功能的实现。

创建后端工程

执行django-admin startproject djangotest创建项目。
cd djangotest,执行python manage.py startapp myapp创建应用。
python manage.py runserver,启动服务,访问http://localhost:8000/

创建 RESTful API

安装 mysqlclient 和 djangorestframework,

pip --default-timeout=6000 install -i https://pypi.tuna.tsinghua.edu.cn/simple mysqlclient
pip --default-timeout=6000 install -i https://pypi.tuna.tsinghua.edu.cn/simple djangorestframework

在 settings.py 中,添加'rest_framework'和'myapp',

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

    'rest_framework',

    'myapp',
]

同时修改数据库配置,

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'HOST': '127.0.0.1',
        'PORT': 3306,
        'NAME': 'world',
        'USER': 'root',
        'PASSWORD': '123456'
    }
}

在 myapp\models.py 添加 model,model 叫做 HellloDjango,有 2 个字段 id 和 name,

from django.db import models

# Create your models here.


class HelloDjango(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(null=False, max_length=64, unique=True)

执行python manage.py makemigrations,提交,

>python manage.py makemigrations
Migrations for 'myapp':
  myapp\migrations\0001_initial.py
    - Create model HelloDjango

执行python manage.py migrate,创建,

>python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, myapp, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying myapp.0001_initial... OK
  Applying sessions.0001_initial... OK

看看数据库,新增了 auth_和 django_开头的表,以及 model 映射的表 myapp_hellodjango,

mysql> show tables;
+----------------------------+
| Tables_in_world            |
+----------------------------+
| auth_group                 |
| auth_group_permissions     |
| auth_permission            |
| auth_user                  |
| auth_user_groups           |
| auth_user_user_permissions |
| city                       |
| country                    |
| countrylanguage            |
| django_admin_log           |
| django_content_type        |
| django_migrations          |
| django_session             |
| myapp_hellodjango          |
+----------------------------+
14 rows in set (0.00 sec)

插入 2 条测试数据,

mysql> insert into myapp_hellodjango(name) values('hello');
Query OK, 1 row affected (0.09 sec)

mysql> insert into myapp_hellodjango(name) values('django');
Query OK, 1 row affected (0.20 sec)

mysql> select * from myapp_hellodjango;
+----+--------+
| id | name   |
+----+--------+
|  2 | django |
|  1 | hello  |
+----+--------+
2 rows in set (0.00 sec)

照着官网的例子,在 myapp 目录下新增 urls.py,添加 rest 代码,

from django.conf.urls import url, include
from rest_framework import routers, serializers, viewsets

from .models import HelloDjango


# Serializers define the API representation.
class HelloSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = HelloDjango
        fields = ['id', 'name']


# ViewSets define the view behavior.
class HelloViewSet(viewsets.ModelViewSet):
    queryset = HelloDjango.objects.all()
    serializer_class = HelloSerializer


# Routers provide an easy way of automatically determining the URL conf.
router = routers.DefaultRouter()
router.register(r'hello', HelloViewSet)

urlpatterns = [
    url(r'demo/', include(router.urls)),
]

在 djangotest 下的 urls.py 中添加路由,

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/', include('myapp.urls'))
]

通过这 2 个 urls.py 文件的指定,api 接口的路径为,/api/demo/hello。
执行python manage.py runserver启动服务,使用 postman 来调用http://127.0.0.1:8000/api/demo/hello/
先发 1 个 post 请求,往数据库新增 1 条数据,

再发 1 个 get 请求,会看到返回了 3 条数据,2 条预先插入的数据,1 条 post 请求新增的数据,

创建前端工程

在 djangotest 根目录下,执行vue create vuetest,创建 vue 工程。
默认安装,一路回车,啪啪啪。
开始创建,

Vue CLI v4.4.1
a  Creating project in D:\cicd\vuetest.
a  Initializing git repository...
aa Installing CLI plugins. This might take a while...

创建成功,

a  Successfully created project vuetest.
a  Get started with the following commands:

 $ cd vuetest
 $ npm run serve

执行cd vuetestnpm run serve,前端工程就启动起来了,访问http://localhost:8080/
Welcome to Your Vue.js App,

前端调后端接口

此时 djangotest 的目录结构为,

├─djangotest
│  ├─djangotest
│  ├─myapp  # app
│  ├─vuetest  # 前端
│  ├─manage.py

修改 vuetest\src\components\HelloWorld.vue,添加{{info}},用来展示后端 api 返回的数据,

<div class="hello">
  {{info}}
    <h1>{{ msg }}</h1>

同时在<script>中使用 axios 添加 ajax 请求,请求http://127.0.0.1:8000/api/demo/hello/
将 response.data 赋值给 info,

<script>
export default {
  name: 'HelloWorld',
  props: {
    msg: String
  },
  data() {
    return {
        info: 123
    }
  },
  mounted () {
    this.$axios
      .get('http://127.0.0.1:8000/api/demo/hello/')
      .then(response => (this.info = response.data))
      .catch(function (error) { // 请求失败处理
        console.log(error);
      });
  }
}
</script>

为了运行起来,需要安装 axios,

npm install --save axios

并在 vuetest\src\main.js 中引入,

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

Vue.config.productionTip = false

Vue.prototype.$axios = axios;

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

分别启动后端和前端服务,

python manage.py runserver
cd vuetest
npm run serve

嚯!ajax 请求失败了,F12 可以看到报错信息,
localhost/:1 Access to XMLHttpRequest at 'http://127.0.0.1:8000/api/demo/hello/' from origin 'http://localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
django 的端口是 8000,vue 的端口是 8080,vue 在请求 django 的时候,出现了跨域问题。浏览器有个同源策略,域名 + 端口 + 协议都相同才认为是同一来源。
通过配置 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  # 添加

此时 vue 就可以请求到 django 提供的接口了,http://localhost:8080/

前后端结合

vuetest目录下创建vue.config.js,这是因为 django 只能识别 static 目录下的静态文件,这里指定 vue 生成静态文件时套一层 static 目录,

module.exports = {
    assetsDir: 'static'
};

在 vuetest 目录下执行npm run build,生成静态文件到 vuetest/dist 文件夹。

修改 urls.py,指定 django 的模板视图,

from django.conf.urls import url
from django.contrib import admin
from django.urls import path, include
from django.views.generic import TemplateView

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

在 settings.py 中配置模板目录为 dist 文件夹,

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

指定静态文件目录为 vuetest/dist/static,

# Add for vuejs
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, "vuetest/dist/static"),
]

浏览器访问http://localhost:8000/
显示的不再是 django 的欢迎页面,而是 vue 的页面。
前后端结合完成。vue 的 8080 可以停了。

Nginx 转发

nginx 常用 3 个命令,启动,重新加载,停止,

nginx start
nginx -s reload
nginx -s stop

修改\conf\nginx.conf,监听端口改为 8090,添加转发proxy_pass http://localhost:8000;

server {
     listen       8090;
     server_name  localhost;

     #charset koi8-r;

     #access_log  logs/host.access.log  main;

     location / {
         root   html;
         index  index.html index.htm;
         proxy_pass http://localhost:8000;
     }

执行nginx start,浏览器访问http://localhost:8090/
也能正常访问 djangotest。
通过 nginx 将 8090 转发到了 8000。

持续集成

本来想弄个 pipline 的,无奈家里这台破机器安装失败,windows 也没有 linux 对 jenkins 支持好,只能将就做个鸡肋版本。
New Item,命名为 vuetest,添加 vue 的 build 脚本,

d:
cd D:\cicd\djangotest\vuetest
npm run build

New Item,命名为 djangotest,添加 django 的 build 脚本,

d:
cd D:\cicd\djangotest
python manage.py runserver

直接执行会报错 python 不是可运行命令。添加 python 环境变量,在首页左下角,

把路径 D:\Python37 添加为环境变量 path 并保存,

建好的这 2 个 job 就可以用来编译 vue 和启动 django 了,

如果你觉得这篇文章写的还不错的话,关注公众号 “测试老树”,你的支持就是我写文章的最大动力。


↙↙↙阅读原文可查看相关链接,并与作者交流