使用Django + Vue.js快速而优雅地构建前后端分离项目

 

2017-02-11 20:43 | https://sanwen8.cn/p/68bJEgr.html

使用Django + Vue.js快速而优雅地构建前后端分离项目_第1张图片

作者|高远 我为什么要选择Django与Vue.js?

首先介绍一下我看重的点:

Django (MVC框架)

  • Python

  • ORM

  • 简单、清晰的配置

  • Admin app

Django 仅因为 Python 的血统,就已经站在了巨人的肩膀上,更不用说加上配置管理( SaltStack、Ansible )、数据分析( Pandas )、任务队列( Celery )、Restful API( Django REST framework )、HTTP请求( requests ),再加上高度抽象的ORM、功能强大的 Query Expressions、简单清晰的配置以及不得不着重提及的堪称神器的自带App: Admin。

有了它你再也不用将一些经常变化的配置写在文件里面,每次增删改都重新发布一次,你只需要定义出配置的 data scheme ,只需要几行代码,Django Admin便为你提供美观,并带有权限控制的增删改查界面,而且可以通过ORM为它生成的API来做到定制化的更新,比如直接读某个wiki上的配置,自动的写入数据库,伪代码如下:

import pandas as pd
settings = pd.read_html('http://某个gitlab的README 或者 某个redmine wiki')
settings = clean(settings)
update(settings)

最后还可以使用 django-celery 的 celery-beat 按 Interval/crontab 的方式扔更新配置的任务到 celery 队列里面,最最重要的是,这些都可以在Django Admin后台直接配置哦,还不够优雅?请联系我

Vue.js (MVVM框架)

  • 数据双向绑定

  • 单文件组件

  • 清晰的生命周期

  • 学**曲线平滑

  • vue-cli

前端是我的弱项,我需要一个 MVVM 框架来提升交互和节约时间,在试过 AngularJS ,ReactJS,Vue.js之后我选择了Vue.js,因为我觉得写 Vue.js 代码的感觉最接近写 Python。

着重提一下单文件组件:

使用Django + Vue.js快速而优雅地构建前后端分离项目_第2张图片

特别清晰,一个文件包含且仅包含三块:

  1. 前端渲染的模板

  2. 专为此模板写渲染逻辑的

  3. 专为此模板写样式的

这样可以达到什么效果呢?一个文件一个组件,每个组件有它自己的逻辑与样式,你不用关心什么 local 什么 global ,CSS样式加载先后、覆盖问题,因为它是『闭包』的,而且『自给自足』。

当然组件之间也是可以通信的,举个例子,我有一个组件叫 ListULB ,使用表格展示了我拥有的所有 ULB (负载均衡),ListULB 做了一件事,从 API 获取 ULB 对象列表并 for 循环展现出来, ListULB 可以放到某个页面里,可以放到弹框里,放到模态框里,任何地方都可以,因为这个组件对外交互的只有API。

如果我现在要写一个组件叫 AddVServer ,功能是可以为任意一个 ULB 对象添加VServer,我的写法是将在 AddVServer 组件创建的时候,将 ULB 对象传给 AddVServer 组件,这样AddVServer 组件拿到这个对象,就可以直接根据对象的ID等,创建出当前行的ULB的VServer了,伪代码如下:


  for **ulb_object** in ulbs_list:
    {{ ulb_object.name }}
    {{ ulb_object.id }}
    

注意双星号包着的对象,在 ListULB 组件里面是每行的ULB,传给AddServer组件之后,变成了 current_ulb 对象,拿到id为 current_ulb.id 尽情的为它创建 VServer 吧!

如果我要为指定 VServer 创建 RServer 呢?一样的!

看出来了吧,进行开发之前,前端组件的结构与数据的结构对应起来可以省好多时间,数据驱动前端组件,棒吗?

谁不喜欢优雅的代码呢, 『Data drive everything』 多么省脑细胞!

以上就是我选择Python与Vue.js的原因。

Django与Vue.js是如何结合起来?

首先我选择了Vue.js的前端渲染,自然放弃了Django的后端模板引擎渲染。

然后业务逻辑放到了前端,放弃了Django的View(其实也就是前后端分离必要的条件)。

保留了Django的 Controller (URLconf) 来实现前端路由的父级路由,可以达到不同页面使用不同的前端框架, 页面内部使用各自独有的前端路由的效果,万一老大给你配了前端呢,万一前端只想写 ReactJS 呢?

保留了Django的 Model ,前面说了Django的ORM太好用了,而且可以配合Django Admin。

所以综合来说就是:

M(Django) + C(Django) + MVVM (Vue.js) = M + MVVM + C = MMVVMC

为了容易理解,并没有使用Django自称的MTV模式理解,感兴趣看看我画的图:

使用Django + Vue.js快速而优雅地构建前后端分离项目_第3张图片

代码块中的修改都会用爽星号括起来,比如: changed

说明:本文为了精简篇幅,默认您已经安装了必要的 命令行界面(CLI),比如 vue-cli等。

1. 创建Django项目

命令:

django-admin startproject ulb_manager

结构:

.
├── manage.py
└── ulb_manager
    ├── __init__.py
    ├── settings.py
    ├── urls.py
    └── wsgi.py

2. 进入项目根目录,创建一个 App 作为项目后端

命令:

cd ulb_manager
python manage.py startapp backend

即:App 名叫做 backend

结构:

.
├── backend
│   ├── __init__.py
│   ├── admin.py
│   ├── migrations
│   │   └── __init__.py
│   ├── models.py
│   ├── tests.py
│   └── views.py
├── manage.py
└── ulb_manager
    ├── __init__.py
    ├── settings.py
    ├── urls.py
    └── wsgi.py

3. 使用vue-cli创建一个Vue.js项目作为项目前端

命令:

vue-init webpack frontend

即:项目名叫 frontend结构:

.
├── backend
│   ├── __init__.py
│   ├── admin.py
│   ├── migrations
│   │   └── __init__.py
│   ├── models.py
│   ├── tests.py
│   └── views.py
├── frontend
│   ├── README.md
│   ├── build
│   │   └── ....
│   ├── config
│   │   ├── dev.env.js
│   │   ├── index.js
│   │   ├── prod.env.js
│   │   └── test.env.js
│   ├── index.html
│   ├── package.json
│   ├── src
│   │   ├── App.vue
│   │   ├── assets
│   │   │   └── logo.png
│   │   ├── components
│   │   │   └── Hello.vue
│   │   └── main.js
│   ├── static
│   └── test
│       └── ...
├── manage.py
└── ulb_manager
    ├── __init__.py
    ├── settings.py
    ├── urls.py
    └── wsgi.py

结构总结:可以看到项目根目录有两个新文件夹,一个叫 backend ,一个叫 frontend,分别是:backend Django的一个App;frontend Vue.js项目。

4. 接下来我们使用 webpack 打包VusJS项目

命令:

cd frontend
npm install
npm run build

结构:

我引入了一些包,比如element-ui等,你的static里面的内容会不同,没关系 index.html 和 static 文件夹相同就够了。

dist
├── index.html
└── static
    ├── css
    │   ├── app.42b821a6fd065652cb86e2af5bf3b5d2.css
    │   └── app.42b821a6fd065652cb86e2af5bf3b5d2.css.map
    ├── fonts
    │   ├── element-icons.a61be9c.eot
    │   └── element-icons.b02bdc1.ttf
    ├── img
    │   └── element-icons.09162bc.svg
    └── js
        ├── 0.8750b01fa7ffd70f7ba6.js
        ├── vendor.804853a3a7c622c4cb5b.js
        └── vendor.804853a3a7c622c4cb5b.js.map

构建完成会生成一个 文件夹名字叫dist,里面有一个 index.html 和一个 文件夹static.

5. 使用Django的通用视图 TemplateView

找到项目根 urls.py (即ulb_manager/urls.py),使用通用视图创建最简单的模板控制器,访问 『/』时直接返回 index.html。

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

6. 配置Django项目的模板搜索路径

上一步使用了Django的模板系统,所以需要配置一下模板使Django知道从哪里找到index.html。

打开 settings.py (ulb_manager/settings.py),找到TEMPLATES配置项,修改如下:

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

注意这里的 frontend 是Vue.js项目目录,dist则是运行 npm run build 构建出的index.html与静态文件夹 static 的父级目录这时启动Django项目,访问 / 则可以访问index.html,但是还有问题,静态文件都是404错误,下一步我们解决这个问题

7. 配置静态文件搜索路径

打开 settings.py (ulb_manager/settings.py),找到 STATICFILES_DIRS 配置项,配置如下:

# Add for Vue.js
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, "frontend/dist/static"),
]

这样Django不仅可以将/ulb 映射到index.html,而且还可以顺利找到静态文件此时访问 /ulb 我们可以看到使用Django作为后端的Vue.js helloworldALL DONE。

8. 开发环境

因为我们使用了Django作为后端,每次修改了前端之后都要重新构建(你可以理解为不编译不能运行)。

除了使用Django作为后端,我们还可以在dist目录下面运行以下命令来看效果:

hs(即: http server)

使用Django + Vue.js快速而优雅地构建前后端分离项目_第4张图片

但是问题依然没有解决,我想过检测文件变化来自动构建,但是构建是秒级的,太慢了,所以我直接使用Vue.js的开发环境来调试。

npm run dev

使用Django + Vue.js快速而优雅地构建前后端分离项目_第5张图片

毫秒,但是有个新问题,使用Vue.js的开发环境脱离了Django环境,访问Django写的API,出现了跨域问题,有两种方法解决,一种是在Vue.js层上做转发(proxyTable),另一种是在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',
]

这里要注意中间件加载顺序,列表是有序的。

  • settings.py 添加

CORS_ORIGIN_ALLOW_ALL = True

至此,我的开发环境就搭建完成了。

9. 生产环境部署

接下来就是生产环境的部署了,例如,我们可以尝试将项目部署到UCloud云主机上:

  • 创建主机

注册 UCloud,找到主机管理列表,如下所示:

这里注意记住你的外网IP,下面的IP替换成你的。

9.2 环境搭建与部署

CentOS 系统可以使用 yum 安装必要的包。

如果你使用git来托管代码:

yum install git

如果你要在服务器上构建前端:

yum install nodejs
yum install npm
yum install nginx

我们使用 uwsgi 来处理 Django 请求,使用 nginx 处理 static 文件(即之前 build 之后 dist 里面的static,这里默认前端已经打包好了,如果在服务端打包前端需要安装nodejs,npm等)。

安装uWsgi:

yum install uwsgi

或者:

pip install uwsgi

我们使用配置文件启动uwsgi,比较清楚。

uwsgi配置文件::

[uwsgi]
socket = 127.0.0.1:9292
stats = 127.0.0.1:9293
workers = 4

项目根目录:

chdir = /opt/inner_ulb_manager
touch-reload = /opt/inner_ulb_manager
py-auto-reload = 1

在项目跟目录和项目同名的文件夹里面的一个文件

module= inner_ulb_manager.wsgi
pidfile = /var/run/inner_ulb_manager.pid
daemonize = /var/log/inner_ulb_manager.log
nginx 配置文件:
server {
    listen 8888;
    server_name 120.132.**.75;
    root /opt/inner_ulb_manager;
    access_log /var/log/nginx/access_narwhals.log;
    error_log /var/log/nginx/error_narwhals.log;

    location / {
            uwsgi_pass 127.0.0.1:9292;
            include /etc/nginx/uwsgi_params;
    }
    location /static/ {
            root  /opt/inner_ulb_manager/;
            access_log off;
    }
    location ^~ /admin/ {
            uwsgi_pass 127.0.0.1:9292;
            include /etc/nginx/uwsgi_params;
    }
}

/opt/inner_ulb_manager/static 即为静态文件目录,那么现在我们静态文件还在 frontend/dist 怎么办,不怕,Django给我们提供了命令:

先去settings里面配置:

STATIC_ROOT = os.path.join(BASE_DIR, "static")

然后在存在manage.py的目录,即项目跟目录执行:

python manage.py collectstatic

这样frontend/dist/static里面的东西就到了项目根目录的static文件夹里面了。

那么为什么不直接手动把构建好的dist/static拷过来呢?因为开始提过Django自带的App:admin 也有一些静态文件(CSS,JS等),它会一并collect过来,毕竟Nginx只认项目跟目录的静态文件,它不知道Django把它自己的需求文件放到哪了。

开头说过Django配置灵活,那么我们专门为Django创建一个生产环境的配置:

prod.py

prod.py 与 默认 settings.py 同目录。

导入公共配置

from .settings import *

生产环境关闭DEBUG模式

DEBUG = False

生产环境开启跨域

CORS_ORIGIN_ALLOW_ALL = False

特别说明,下面这个配置不需要,因为前端是Vue.js构建的,它默认使用static作为静态文件入口,我们Nginx配置static为入口即可,保持一致,没Django什么事。

STATIC_URL = '/static/'

如何使用这个配置呢,进入 wisg.py 即uwsgi配置里面的module配置修改为:

import os

from django.core.wsgi import get_wsgi_application

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "**inner_ulb_manager.prod**")

application = get_wsgi_application()

启动uwsgi

uwsgi --ini inner_ulb_manager.ini

启动ngingx

service nginx start

至此,部署就完成了。

10. 效果图

List 组件:

使用Django + Vue.js快速而优雅地构建前后端分离项目_第6张图片

传单个 ULB 对象给 Detail 组件使用即可Detail 组件:

使用Django + Vue.js快速而优雅地构建前后端分离项目_第7张图片

当然里面还实现了前面提到的 ULB 的 VServer 创建,VServer 的 RServer 的创建等。

作者介绍

使用Django + Vue.js快速而优雅地构建前后端分离项目_第8张图片

高远,3 年 DevOps 经验,UCloud 网络产品运维工具开发负责人,专注于小型团队项目的快速构建与开发,希望用软件工程的思想更优雅的改变世界。

你可能感兴趣的:(Python)