2021年9月17日 19:01 by wst
djangodjango/vue合作开发
环境
# 创建一个名字为vue的虚拟环境
conda create --name=vue python=3.7
conda activate vue
pip install Django
python -c "import django;print(django.__version__)"
django-admin startproject django_vue
cd django_vue
python manage.py migrate
python manage.py runserver 8000
http://localhost:8000
python manage.py startapp api_test
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'api_test',
]
# -*- coding: utf-8 -*-
from django.db import models
class Book(models.Model):
book_name = models.CharField(max_length=128)
add_time = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.book_name
只有两个字段,书名book_name和添加时间add_time。 如果没有指定主键的话Django会自动新增一个自增id作为主键。
from django.shortcuts import render
from django.views.decorators.http import require_http_methods
from django.core import serializers
from django.http import JsonResponse
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)
from django.urls import include, path
from .views import *
urlpatterns = [
path(r'add_book$', add_book, ),
path(r'show_books$', show_books, ),
]
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include("api_test.urls"))
]
python manage.py makemigrations api_test
python manage.py migrate
"api_test_book"
"auth_group"
"auth_group_permissions"
"auth_permission"
"auth_user"
"auth_user_groups"
"auth_user_user_permissions"
"django_admin_log"
"django_content_type"
"django_migrations"
"django_session"
"sqlite_sequence"
Django生成的表名将以app名加上model中的类名组合而成。
python manage.py runserver 8000
启动服务,通过httpie测试一下我们刚才写的两个接口。
http://127.0.0.1:8000/api/add_book?book_name=mikezhou_talk
http://127.0.0.1:8000/api/add_book?book_name=测试开发技术
{
"list": [{
"model": "api_test.book",
"pk": 1,
"fields": {
"book_name": "mikezhou_talk",
"add_time": "2021-09-14T16:48:11.853Z"
}
}, {
"model": "api_test.book",
"pk": 2,
"fields": {
"book_name": "测试开发技术",
"add_time": "2021-09-14T16:48:39.424Z"
}
}],
"msg": "success",
"error_num": 0
}
有关Vue的模块(包括vue)可以使用node自带的npm包管理器安装。推荐使用淘宝的 cnpm 命令行工具代替默认的 npm。
npm install -g cnpm --registry=https://registry.npm.taobao.org
2、先用cnpm安装vue-cli脚手架工具(vue-cli是官方脚手架工具,能迅速帮你搭建起vue项目的框架):
cnpm install -g vue-cli
3、安装好后,在django_vue项目根目录下,新建一个前端工程目录:
vue init webpack frontend
在创建项目的过程中会弹出一些与项目相关的选项需要回答,按照真实情况进行输入即可。
4、安装 vue 依赖模块
cd frontend
cnpm install
cnpm install vue-resource
cnpm install element-ui
├── api_test
│ ├── admin.py
│ ├── apps.py
│ ├── __init__.py
│ ├── migrations
│ ├── models.py
│ ├── __pycache__
│ ├── tests.py
│ ├── urls.py
│ └── views.py
├── db.sqlite3
├── django_vue
│ ├── asgi.py
│ ├── __init__.py
│ ├── __pycache__
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── frontend
│ ├── build
│ ├── config
│ ├── index.html
│ ├── node_modules
│ ├── package.json
│ ├── package-lock.json
│ ├── README.md
│ ├── src
│ └── static
├── manage.py
└── README.md
本文为了读者方便查看,是直接将vue前端工程放在django项目目录下,实际多人协作开发过程中, 完全是可以放在不同代码仓库下面的。
<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.$http.get('http://127.0.0.1:8000/api/add_book?book_name=' + this.input)
.then((response) => {
var res = JSON.parse(response.bodyText)
if (res.error_num === 0) {
this.showBooks()
} else {
this.$message.error('新增书籍失败,请重试')
console.log(res['msg'])
}
})
},
showBooks () {
this.$http.get('http://127.0.0.1:8000/api/show_books')
.then((response) => {
var res = JSON.parse(response.bodyText)
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>
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
import ElementUI from 'element-ui'
import VueResource from 'vue-resource'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(ElementUI)
Vue.use(VueResource)
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
components: { App },
template: ''
})
import Vue from 'vue'
import App from './App'
import router from './router'
import ElementUI from 'element-ui'
import VueResource from 'vue-resource'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(ElementUI)
Vue.use(VueResource)
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
components: { App },
template: ''
})
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
PS: 注意中间件的添加顺序。
对于已经存在的前端项目,直接:
cd prject_home
npm install
npm run build
目前我们已经分别完成了Django后端和Vue.js前端工程的创建和编写,但实际上它们是运行在各自的服务器上,和我们的要求是不一致的。因此我们须要把Django的`TemplateView指向我们刚才生成的前端dist文件即可。
from django.contrib import admin
from django.urls import path, include
from django.views.generic.base import TemplateView
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include("api_test.urls")),
path('', TemplateView.as_view(template_name="index.html"))
]
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'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',
],
},
},
]
# Add for vuejs
STATICFILES_DIRS = [
os.path.join(BASE_DIR, "frontend/dist/static"),
]
注意此时服务的端口已经是Django服务的8000而不是node服务的8080了,说明我们已经成功通过Django集成了Vue前端工程。
添加以下配置项到settings.py中
# 部署时将作为放置静态文件的目录
STATIC_ROOT = 'static_deploy'
安装uwsgi,建议使用conda安装。
conda install uwsgi==2.0.19 -n vue
配置uwsgi启动文件:uwsgi.ini
[uwsgi]
# 项目根目录路径(full path)
chdir = /data/django_vue
# Django的 wsgi 文件
module = django_vue.wsgi:application
# virtualenv目录 (full path)
home = /data/anaconda3/envs/vue
# 自动载入
py-autoreload=1
# 设置日志
daemonize = %(chdir)/logs/uwsgi.log
log-maxsize = 5000000
master = true
# 最大工作进程数(CPU密集型建议设为CPU核心数,IO密集型建议设为CPU核心数的两倍)
processes = 1
# unix套接字文
socket = %(chdir)/socket.sock
# socket文件权限
chmod-socket = 777
# 退出时清空环境
vacuum = true
# 用以重启服务
stats=%(chdir)/uwsgi.status
pidfile=%(chdir)/uwsgi.pid
配置nginx,内容如下:
server {
listen 80;
server_name django_vue.com www.django_vue.com;
access_log /data/django_vue/logs/access.log;
error_log /data/django_vue/logs/error.log;
root /data/django_vue;
location /static/ {
#如果你的static目录不在root里,可以配置 alias /path/to/your/mysite/static;
alias /data/django_vue/static_deploy/;
}
location / {
uwsgi_pass unix:///data/django_vue/socket.sock;
include uwsgi_params; # the uwsgi_params file you installed
}
}
构建前端代码
cd frontend
npm run build
收集静态文件
# 此时静态文件会全部放到static_deploy中
python manage.py collectstatic
启动uwsgi服务
uwsgi --ini /data/django_vue/uwsgi.ini
该实战示例为大家充分展示了现在主流的前后端分离方式,由前端框架,如Vue.js来构建实现前端界面,再通过后端框架, 如Django来实现API数据提供,两者通过接口进行通讯、相辅相成、最终实现一个完整Web项目。
附:源码下载地址,关注公众号,回复“django_vue”