参考网上其他文章,学习实践
Demo采用Vue作为前端框架,代替Django自身的模版引擎;Django作为服务端提供API接口,使得前后端完全分离,更适合单页应用的开发构建。
单页应用,多页应用的区别:https://www.jianshu.com/p/4c9c29967dd6
一、构建Django项目
Pycharm自动生成Django项目(Django1.11)
创建一个app,命名为myapp
settings.py配置文件,配置mysql数据库(MySQL5.7),将myapp添加至installed_apps列表中
myapp的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
- myapp的views中新增两个结构,一个是show_books返回所有的书籍列表(通过JsonResponse返回能被前端识别的json格式数据),二是add_book接受一个get请求,往数据库里添加一条book数据
from django.shortcuts import render
from .models import Book
from django.views.decorators.http import require_http_methods
from django.core import serializers
from django.http import JsonResponse
import json
# Create your views here.
@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)
- myapp目录下,新增一个urls.py文件,把新增的两个接口添加到路由里
from django.conf.urls import url, include
from . import views
urlpatterns = [
url(r'add_book$', views.add_book),
url(r'show_books$', views.show_books),
]
将myapp下的urls.py添加到系统urls.py中
from django.conf.urls import url, include
from django.contrib import admin
import myapp.urls
from django.views.generic import TemplateView
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^api/', include(myapp.urls)),
url(r'^$', TemplateView.as_view(template_name='index.html')),
]
在项目根目录下,控制台中输入命令,自动创建数据库
python manage.py makemigrations
python manage.py migrate
-
启动项目,通过postman测试一下我们刚写的接口
屏幕快照 2019-08-19 11.07.50.png
二、构建Vue前端项目
- 在项目根目录下,新建一个前端工程目录
vue-init webpack appfront
进入appfront目录,运行命令
npm install //建立vue所需的node依赖
npm install vue-resource
npm install element-ui
- 后缀为vue的文件是Vue.js框架定义的单文件组件,其中<template>标签中的内容可以理解为是类html的页面结构内容,<script>中的是js方法、数据方面的内容,<style>中的则是css样式方面的内容
<template>
<div id="app">
<img src="./assets/logo.png">
<router-view/>
</div>
</template>
<script>
export default {
name: 'App'
}
</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>
- 在src/component文件夹下新建一个名为Home.vue的组件,通过调用之前在Django上写好的api,实现添加书籍和展示书籍信息的功能。在样式组件上我们使用了饿了么团队推出的element-ui,这是一套专门匹配Vue.js框架的功能样式组件
<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 scope="scope"> {{ scope.row.pk }} </template>
</el-table-column>
<el-table-column prop="book_name" label="书名" min-width="100">
<template scope="scope"> {{ scope.row.fields.book_name }} </template>
</el-table-column>
<el-table-column prop="add_time" label="添加时间" min-width="100">
<template 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>
- 在src/router目录的index.js中,我们把新建的Home组件,配置到vue-router路由中
import Vue from 'vue'
import Router from 'vue-router'
import Home from '@/components/Home'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'Home',
component: Home
}
]
})
- 在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自带的服务器,浏览器会自动打开, 我们能看到页面
屏幕快照 2019-08-19 11.23.34.png
尝试新增书籍,新增的书籍信息会实时反映到页面的列表中,这得益于Vue.js的数据双向绑定特性
在前端工程目录下,输入npm run build,如果项目没有错误的话,就能够看到所有的组件、css、图片等都被webpack自动打包到dist目录下了
三、整合Django和Vue.js
目前我们已经分别完成了Django后端和Vue.js前端工程的创建和编写,但实际上它们是运行在各自的服务器上,和我们的要求是不一致的。因此我们须要把Django的TemplateView指向我们刚才生成的前端dist文件
- 找到根urls.py,使用通用视图创建最简单的模板控制器,访问 『/』时直接返回 index.html:
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^api/', include(myapp.urls)),
url(r'^$', TemplateView.as_view(template_name='index.html')),
]
- settings.py中配置一下模版,使Django知道从哪里找到index.html
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',
],
},
},
]
- 在settings.py中配置一下静态文件的搜索路径
# Add for vuejs
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'appfront/dist/static')
]
-
配置完成,我们在project目录下输入命令python manage.py runserver,就能够看到我们的前端页面在浏览器上展现,此时服务的端口已经是Django服务的8000而不是node服务的8080了
屏幕快照 2019-08-19 11.30.52.png
最后附上一张,整个项目代码的目录