Web开发 ------ 基于Django+Vue网上购物商城:前后端分离
完整项目地址:https://gitee.com/dadadaliuliuliiu/ShopProject
一、为什么需要前后端分离?
1.前后端分离的优点
- PC、APP、Pad多端适应的需求。
- SPA, 单页Web应用(single page web application)
我们使用过的很多看起来很高级的网页,比如网易云或者京东等页面,他们比较厉害的一点是页面看起来只有一个,不管你点击什么地方,永远不会刷新页面,都是感觉是在一个页面上完成的操作。 - 前后端开发职责不清晰(eg: template是由前端还是后端负责?)
- 开发效率问题,前后端互相等待
前端一直配合后端,能力受限
后台开发语言和模板高度耦合,导致开发语言严重依赖.
2.前后端分离的缺点
- 前后端学习门槛增加, 前端工作量加大
- 数据依赖导致文档的重要性增加
- SEO的难度增加: baidu --> westos —> api
SEO(Search Engine Optimization)搜索引擎优化。 - 后端开发模式迁移增加成本
总结: 前后端分离是未来的趋势,像APP开发不需要SEO优化的。
二、RESTful API
rest_frameworkhttps://blog.csdn.net/dakengbi/article/details/90764412框架是基于Django的,帮助我们快速开发符合restful规范的接口框架,它主要适用于前后端分离项目。
RESTful:Representational State Transfer(表象层状态转变),只是一种架构方式的约束,给出一种约定的标准,完全严格遵守RESTful标准并不是很多,也没有必要。但是在实际运用中,有RESTful标准可以参考,是十分有必要的。在工作中对api接口规范、命名规则、返回值、授权验证等进行一定的约束
- 轻量, 直接通过HTTP协议(不需要额外的协议), 执行GET/POST/DELETE/PUT/PATCH操作.
- 面向资源,(goods/1,1就是资源)一目了然,具有自解释性。
- 数据描述简单,一般使用xml或者json做数据通讯。
三、渐进式框架Vue
Vue 是一套用于构建用户界面的渐进式框架。Vue 被设计为可以自底向上逐层应用。Vue两大核心思想:组件化和数据驱动。
View通过View-Model的DOM Listeners将事件绑定到Model上,而Model则通过Data Bindings来
管理View中的数据,View-Model从中起到一个连接桥的作用。
1.Vue环境搭建
npm install vue-cli -g //全局安装 vue-cli
1.安装 nodejs
2.命令行安装cnpm, 使用淘宝的镜像源
安装nodejs后就有了npm命令
(base) F:\ziliao\python_kaifa\my_code\12_django项目2\vueExecise\online-store-vue-master>npm install -g cnpm --registry=https://registry.npm.taobao.org
C:\Users\daliu\AppData\Roaming\npm\cnpm -> C:\Users\daliu\AppData\Roaming\npm\node_modules\cnpm\bin\cnpm+ cnpm@6.1.1 added 685 packages from 953 contributors in 31.308s
3.下载Vue项目代码
4.切换到文件目录, 安装依赖包
(base) F:\ziliao\python_kaifa\my_code\12_django项目2\vueExecise\online-store-vue-master>cnpm install
5.独立的运行前端项目
(base) F:\ziliao\python_kaifa\my_code\12_django项目2\vueExecise\online-store-vue-master>npm run dev
6.浏览器访问http://localhost:8080
2.商品分类数据与Vue
接口相关代码都放在src/api/api.js里面,调试接口的时候我们首先需要新建一个自己的host,然后替换要调试的host
新建local_host
let local_host = 'http://47.105.62.144:8000';
替换商品类别默认的host
//获取商品类别信息
export const getCategory = params => {
if('id' in params){
return axios.get(`${local_host}/categorys/`+params.id+'/');
}
else {
return axios.get(`${local_host}/categorys/`, params);
}
};
这个时候在pycharm进入服务器激活虚拟环境运行ShopProject项目
访问 http://127.0.0.1:8080
发现不显示商品分类了,是因为这涉及到了跨域问题,前端与后端分处不同的域名。
3.跨域问题解决
什么是跨域?
CORS全称Cross-Origin Resource Sharing,意为跨域资源共享。当浏览器从一个资源(域名)去访问另一个不同域名或者同域名不同端口的资源时,就会发出跨域请求。如果此时另一个资源不允许其进行跨域资源访问,那么访问的那个资源就会遇到跨域问题。
域名:
主域名不同 http://www.baidu.com/index.html -->http://www.sina.com/test.js
子域名不同 http://www.666.baidu.com/index.html -->http://www.555.baidu.com/test.js
域名和域名ip http://www.baidu.com/index.html -->http://180.149.132.47/test.js
端口:
http://www.baidu.com:8080/index.html–> http://www.baidu.com:8081/test.js
协议:
http://www.baidu.com:8080/index.html–> https://www.baidu.com:8080/test.js
后端服务器解决跨域问题的方法:
我们需要为后端添加跨域访问的支持。使用CORS中间键来解决后端对跨域访问的支持。
1.安装模块: django-cors-headers
django-cors-headers 使用说明:https://github.com/ottoyiu/django-cors-headers
(venv) [root@todolist-server ShopProject]# pip install -i https://pypi.douban.com/simple django-cors-headers
2.添加子应用coreschema到INSTALL_APPS中
'coreschema',
3.添加中间件corsheaders.middleware.CorsMiddleware
MIDDLEWARE = [
# 要放的尽可能靠前,必须在CsrfViewMiddleware之前。我们直接放在第一个位置就好了
'corsheaders.middleware.CorsMiddleware',
# ........
]
4.设置允许跨域:
CORS_ORIGIN_ALLOW_ALL = True
5.访问 http://127.0.0.1:8080 数据就可以填充进来了
此时运行ShopProject项目
访问 http://127.0.0.1:8080
商品分类详情信息就能显示了
四、Vue基本组件
1.第一个Vue程序实现步骤
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- 1. 导入开发版本的Vue.js -->
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<!--
3. 使用简洁的模板语法把数据渲染到页面上
id='idname' #idname
class='clsname' .clsname
-->
<!--Vue实例的作用范围是什么呢?
Vue会管理el选项命中的元素及其内部的后代元素.
-->
<p>{{name}}</p>
<div id="app" class="appcls">
{{ name }} -{{ age }}
<h3 style="color: red">{{name}}</h3>
<ul>
<li>{{foods[0]}}</li>
<li>{{foods[1]}}</li>
</ul>
<p>学生信息: 字典类型 {{stuinfos.name + '!!!!!'}}</p>
</div>
<!--2.创建Vue实例对象,设置el属性和data属性 -->
<script>
var app = new Vue(
{
// el是用来设置Vue实例挂载(管理)的元素。
el: '#app',
data: {
name: 'fentiao',
age: 10,
foods: ['方便面', '西红柿炒蛋', '鱼香肉丝'],
stuinfos: {
'name': 'stuname',
'school': 'westos'
}
}
}
)
</script>
</body>
</html>
2.本地应用(重点掌握)
v-text和v-html区别:
v-html内容中有html结构会被解析为标签,而v-text只会解析为文本。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<!--
#### v-text
- v-text指令的作用是: 设置标签的内容(textContent)
- 默认写法会替换全部内容,使用差值表达式{{}}可以替换指定内容
- 内部支持表达式
#### v-html
- v-html指令的作用是: 设置元素的innerHtml
- v-html内容中有html结构会被解析为标签,而v-text只会解析为文本
- 解析文本使用v-text,解析html使用v-html
-->
</head>
<body>
<div id="app">
{{ welcome }} !!!!!!!!
Webstorm
<span v-text="welcome + ' ' + name" >!!!!!!!!!!</span>
{{htmldata}}
<span v-text="htmldata"></span>
<div v-html="htmldata"></div>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
welcome: 'hello Vue',
name: 'fentiao',
htmldata: '<h2 style="color: red">html data</h2>'
}
})
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<!-- 需求: 页面有一个按钮, 如果点击按钮, 弹出一个警告框: 点击成功 -->
<!-- v-on:click="clickButton": 当用户出发点击click事件的时候执行clickButton函数-->
<!-- 1. 非简化版-->
<button v-on:click="clickButton">单击按钮</button>
<button v-on:dblclick="dblclickButton">双击按钮</button>
<!-- 2. 简化版-->
<button @click="clickButton">单击按钮</button>
<button @dblclick="dblclickButton">双击按钮</button>
<!-- 3. 包含参数的函数-->
<button @click="insideparaButton('success')">点击按钮-包含参数</button>
<!-- 4. 事件的限制-->
<button @keyup.enter="enterButton">enter键入</button>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
name: 'fentiao'
},
methods: {
clickButton: function () {
// this ==== self
alert("单击按钮成功" + this.name)
},
dblclickButton: function () {
// this ==== self
alert("双击按钮成功" + this.name)
},
insideparaButton: function (info, otherinfo = 'otherinfo') {
alert("点击按钮" + info)
},
enterButton: function () {
// this ==== self
alert("回车成功" + this.name)
},
}
})
</script>
</body>
</html>
频繁的切换v-show,因为切换消耗小。不频繁的切换使用v-if.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<!--需求: 有一个广告, 按钮会改变广告的显示状态. 点击按钮显示,再点击就隐藏-->
<div id="app">
<!-- 2. v-show指令的作用是:根据真假切换元素的状态,-->
<h3 style="color: red" v-show="isdisplay"> 淘宝广告信息</h3>
<h3 style="color: red" v-if="isdisplay"> 淘宝广告信息</h3>
<!-- 1. 绑定点击事件, 当用户点击按钮,改变显示的状态 -->
<button v-on:click="changeDisplay">显示/隐藏</button>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
isdisplay: false,
},
methods: {
changeDisplay: function () {
this.isdisplay = !this.isdisplay;
// alert(this.isdisplay);
}
}
})
</script>
</body>
</html>
结果:
点击 显示/隐藏 按钮:
再次点击按钮:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<style>
.display{
border: 1px solid red;
}
</style>
</head>
<body>
<div id="app">
<!-- class="diaplay"-->
<img v-bind:src="ImgSrc" v-bind:title="ImgTitle" v-bind:class="{display:isDisplay}" @click="changeDisplay">
<img :src="ImgSrc" :title="ImgTitle" :class="{display:isDisplay}" @click="changeDisplay">
</div>
<script>
var app = new Vue(
{
el: '#app',
data: {
ImgSrc: 'python.jfif',
ImgTitle: 'python',
isDisplay: true
},
methods: {
changeDisplay: function () {
this.isDisplay = !this.isDisplay;
}
}
}
)
</script>
</body>
</html>
效果:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<ul>
<!-- 1. v-for指令的作用是根据数据生成列表结构。
2. index是索引值
-->
<li v-for="item in todos">{{ item }}</li>
<p>方法二</p>
<li v-for="(item, index) in todos">{{index+1}}-{{ item }}</li>
</ul>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
todos: ["任务1", "编写Django项目", "电商平台完成", "任务4"]
},
methods: {}
}
)
</script>
</body>
</html>
效果:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<form>
<input type="text" name="todoname" placeholder="任务名称" required v-model="name">
<input type="button" value="添加任务" @click="addTodo(name)">
</form>
<ul>
<!-- 1. v-for指令的作用是根据数据生成列表结构。
2. index是索引值
-->
<li v-for="(item, index) in todos">{{index+1}}-{{ item }}</li>
</ul>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
todos: ["任务1", "编写Django项目", "电商平台完成", "任务4"]
},
methods: {
addTodo: function (name) {
this.todos.push(name);
}
}
}
)
</script>
</body>
</html>
3.案例:计数器
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<button @click="sub">-</button>
<span>{{num}}</span>
<button @click="add">+</button>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
num: 0,
},
methods: {
sub: function () {
if (this.num < 1) {
alert("购买数量不能小于1!");
} else {
//python: a-=1 C/java/js: a--
this.num--;
}
},
add: function () {
if (this.num >= 10) {
alert("购买商品数量已达上限!")
} else {
this.num++;
}
}
}
}
)
</script>
</body>
</html>
五、axios网络请求库
Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。
- 使用cdn
- 使用npm
npm install axios
1.axios应用步骤
axios必须先导入才可以使用。
使用get或post方法即可发送对应的请求
then方法中的回调函数会在请求成功或请求失败时触发
通过回调函数可以获取响应内容或错误信息。
2.综合应用范例参考资料
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<!-- 官网提供的 axios 在线地址 -->
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
<div id="app">
<input type="text" v-model="num" placeholder="笑话个数">
<button @click="getJoke(num)">获取笑话</button>
<ul>
<li v-for="(joke, index) in jokes">
第{{index+1}}个笑话: {{joke}}
</li>
</ul>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
jokes: []
},
methods: {
getJoke: function (num) {
var that = this;
axios.get('https://autumnfish.cn/api/joke/list?num=' + num)
.then(
function (response) {
console.log(response.data);
that.jokes = response.data.jokes;
},
function (err) {
console.log(err);
}
)
}
}
}
)
</script>
</body>
</html>
六、项目报错总结
- raise NodeNotFoundError(self.error_message, self.key, origin=self.origin)django.db.migrations.exceptions.NodeNotFoundError:
Migration users.0001_initial dependencies reference nonexistent parent
node(‘auth’, ‘0011_update_proxy_permissions’)
数据库的报错解决方法: 删除users.0001_initial .py 只留下migrations包 、 db.sqlite3 重新:makemigrations–> migrate- django2.x报错No module named ‘django.core.urlresolvers’ 报错原因:django2.0把原来的 django.core.urlresolvers 包 更改为了 django.urls包,所以我们需要把导入的包都修改一下就可以了。
解决方法: from django.urls import reverse- “trying to load ‘%s’: %s” % (entry[1], e)django.template.library.InvalidTemplateLibrary: Invalid template
library specified. ImportError raised when trying to load
‘crispy_forms.templatetags.crispy_forms_utils’: cannot import name
‘allow_lazy’ from ‘django.utils.functional’
报错原因:django-crispy-forms版本过低 升级到最新版本就可以解决。 我这里出错的django-crispy-forms版本是1.6.1 我升级到1.8.0不再报以上错误- 数据库更新没有识别到userapp里面的数据库内容,把migrations文件和sqlite数据库删除之后, 重新输入 makemigrations 但是显示没有检测到No changes
detected,migrate后依然没有关于user的数据库表,而且migrations文件也没有显示到项目目录中
解决方法:在每一个app下新建一个migrations,重新执行 makemigrations–> migrate 数据库更新成功- TypeError at /goods/ str returned non-string (type NoneType) __str返回的是不是字符串,类型是NoneType,python如果没有return的返回值,那么返回值就是None
报错原因:由于user的model的name参数可以为空导致的
解决方式:修改__str__的返回值:return self.username- 序列化类用的serializer报错create() must be implemented 明明在序列化类中已经重新写了create()方法但是网页没有找到, 解决方式:关闭pycharm重新运行项目,成功解决
- 发送验证码显示报错IP没有权限 解决方式:云片网把电话对应的IP加入到白名单中
- 给项目配虚拟环境里的解释器报错:pycharm please specify a different SDK name
报错原因:有两个现有虚拟环境具有相同的名称(即彼此相同;不同于我正在创建的那个)。
解决方式:删除其中一个之后,我就可以创建新的虚拟环境。 在setting里面的解释器选择里面,打开show all: 接着在以下弹出窗口里边,对于重名环境用右边“-”进行删除即可。- upload更新代码到服务器中,但是服务器代码一直未更新 报错原因:在连接云服务器(start SSH session)时没有指定,所以产生一个临时的配置的许多其他名称的服务器,因此在upload时上传的目录也不正确 mapping的目录不对。
解决方式:删除其他名称的服务器,upload指定服务器SCQserver- 公网ip:8000 访问项目无法访问 报错原因: 在云服务器中没有设置允许的端口范围8000 解决方式:添加8000端口到安全组
七、项目回顾的一个参考:https://blog.csdn.net/a_blackmoon/article/details/83272764
转载:https://blog.csdn.net/weixin_44568633/article/details/105335810