本篇使用Vue.js作为前端框架,代替Django本身较为孱弱的模板引擎,Django则作为服务端提供api接口,使得前后端实现完全分离,更适合单页应用的开发构建。
一、环境准备
pycharm、python、django、node.js、mysql。这些工具的安装这里不做赘述,百度都有。
二、新建django项目
三、运行项目,测试以下django项目是否新建成功
方法一:
方法二:
四、修改数据库配置(我不想用django本身自带的db.sqlite3,想用自己在阿里云搭建的mysql)
修改后:
DATABASES = {
'default': {
# 'ENGINE': 'django.db.backends.sqlite3',
# 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
'ENGINE': 'django.db.backends.mysql',
'NAME': 'django_vue_elementUi',
'USER': 'user',
'PASSWORD': 'password',
'HOST': '110.110.110.110',
}
}
五、设计数据库表
在myDjangoApp下的models.py设计表
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models
# Create your models here.
class Book(models.Model):
book_name = models.CharField(max_length=64)
create_time = models.DateTimeField(auto_now=True)
def __unicode__(self):
return self.book_name
六、在myDjangoApp下的views.py新增两个接口
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import json
from django.http import JsonResponse
from django.core import serializers
from django.shortcuts import render
from django.views.decorators.http import require_http_methods
from models import Book
# Create your views here.
# add_book接受一个get请求,往数据库里添加一条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, e:
response['msg'] = str(e)
response['error_num'] = 1
return JsonResponse(response)
# show_books返回所有的书籍列表(通过JsonResponse返回能被前端识别的json格式数据)
@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, e:
response['msg'] = str(e)
response['error_num'] = 1
return JsonResponse(response)
七、添加路由
1、在myDjangoApp目录下,新增一个urls.py文件,把新增的两个接口添加到路由里面(在django_vue_elementUi目录下的urls.py也可以直接加上这两个接口的路由,但是为了不使该主路由过于臃肿,所以这里要新建一个分支路由)
from django.conf.urls import url
import views
urlpatterns = [
url(r'^add_book/', views.add_book),
url(r'^show_books/', views.show_books),
]
2、把这个分支路由加到主路由中,在django_vue_elementUi目录下的urls.py中添加如下代码
from django.conf.urls import url
from django.contrib import admin
from django.conf.urls import include
from myDjangoApp import urls
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^api/', include(urls)),
]
八、新建数据库django_vue_elementUi
九、生成数据库迁移文件并且执行迁移文件来完成数据库表的创建
发现报错了
报错原因是因为没有安装MySQLdb,所以要进入命令行页面去执行pip install PyMySQL
安装成功后在django_vue_elementUi目录下的__init__.py中加入
import pymysql
pymysql.install_as_MySQLdb()
再执行数据库迁移文件生成命令makemigrations,生成迁移文件
执行迁移文件migrate
验证数据库表是否真的生成
如上图数据表与字段都已经创建成功了,但是会发现多了一个字段id,且id还是主键,但是我的models.py并没有设置这个字段。为什么呢?
答案:django默认在makemigrations会为表对象创建主键id,id = models.AutoField(primary_key=True)。如果你在models.py已经创建过主键的话就不会自动创建这个id主键了。
十、启项目测试接口是否能用
执行runserver,用postman调用接口
数据已经入库,表示这个添加图书的接口已经测试成功,那么测试以下查询的接口
查询接口也已经ok。
十一、把项目停掉,构建前端Vue项目
1、先安装vue-cli脚手架,再创建vue工程目录
npm install -g vue-cli
2、安装好后,在django_vue_elementUi项目根目录下,新建一个前端工程目录:
vue-init webpack appfront
3、进入appfront目录安装vue所需要的依赖
npm install
前端项目已经构建完成
十二、新建一个新组件来调用自己写好的api
我们在src/component文件夹下新建一个名为Book.vue的组件,通过调用之前在Django上写好的api,实现添加书籍和展示书籍信息的功能
1、新建组件
2、修改代码
<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.create_time }} </template>
</el-table-column>
</el-table>
</el-row>
</div>
</template>
<script>
export default {
name: 'Book',
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>
3、配置路由
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
import Book from '@/components/Book'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'HelloWorld',
component: HelloWorld
},
{
path: '/book',
name: 'Book',
component: Book
}
]
})
4、安装element-ui
npm i element-ui -S
十三、使用 webpack 打包vue项目
npm run build
启前端项目
npm run dev
浏览器打开网址会跳转默认首页,说明前端项目已经构建成功
加上book的路由后访问
发现展现的页面不是自己想要的,打开谷歌浏览器调试器
根据错误提示,是说elementUi的组件没有正确的注册,那么去appfront/src/main.js注册
// 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 '../node_modules/element-ui/lib/theme-chalk/index.css'
import ElementUI from 'element-ui'
Vue.config.productionTip = false
Vue.use(ElementUI)
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
})
保存后再去执行一次
npm run build
npm run dev
刷新页面发现我们的ui已经出来了,大功告成。
十四、整合django与Vue,使他们成为一个整体
目前我们已经分别完成了Django后端和Vue.js前端工程的创建和编写,但实际上它们是运行在各自的服务器上,和我们的要求是不一致的。因此我们须要把Django的TemplateView指向我们刚才生成的前端dist文件即可。
1、使用Django的通用视图 TemplateView修改静态指向路径
from django.conf.urls import url
from django.contrib import admin
from django.conf.urls import include
from myDjangoApp import urls
#新增
from django.views.generic import TemplateView
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^api/', include(urls)),
#新增
url(r'^vue/', TemplateView.as_view(template_name="index.html"))
]
2、修改静态资源文件路径
启django项目,
python manage.py runserver
用浏览器打开http://localhost:8000/vue/#/book
注意服务的端口已经是Django服务的8000而不是node服务的8080了!
但是为什么没有数据显示出来?数据库中明明有一条数据的,而且新增功能也不能用
所以再次打开谷歌浏览器的调试器
他说我Book.vue组件里的get没有定义,那么我们回到Book.vue看代码
原来是$http不能被解析,百度大法后得知前端要调用后端需要引入vue-resource
那么先来安装vue-resource再去引用
npm install --save vue-resource
如果发现列表抓取不到数据,可能是出现了跨域问题,打开浏览器console确认:
这时候我们须要在Django层注入header,用Django的第三方包django-cors-headers来解决跨域问题:
pip install django-cors-headers
settings.py 修改:
重新build
npm run build
重新run
python manage.py runserver
数据已经加载出来了,那么测试下添加的功能
一切ok。