小言_互联网的博客

Web开发 ------ 基于Django+Vue网上购物商城(五):前后端分离

423人阅读  评论(0)

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>

六、项目报错总结

  1. 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
  2. django2.x报错No module named ‘django.core.urlresolvers’ 报错原因:django2.0把原来的 django.core.urlresolvers 包 更改为了 django.urls包,所以我们需要把导入的包都修改一下就可以了。
    解决方法: from django.urls import reverse
  3. “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不再报以上错误
  4. 数据库更新没有识别到userapp里面的数据库内容,把migrations文件和sqlite数据库删除之后, 重新输入 makemigrations 但是显示没有检测到No changes
    detected,migrate后依然没有关于user的数据库表,而且migrations文件也没有显示到项目目录中
    解决方法:在每一个app下新建一个migrations,重新执行 makemigrations–> migrate 数据库更新成功
  5. TypeError at /goods/ str returned non-string (type NoneType) __str返回的是不是字符串,类型是NoneType,python如果没有return的返回值,那么返回值就是None
    报错原因:由于user的model的name参数可以为空导致的
    解决方式:修改__str__的返回值:return self.username
  6. 序列化类用的serializer报错create() must be implemented 明明在序列化类中已经重新写了create()方法但是网页没有找到, 解决方式:关闭pycharm重新运行项目,成功解决
  7. 发送验证码显示报错IP没有权限 解决方式:云片网把电话对应的IP加入到白名单中
  8. 给项目配虚拟环境里的解释器报错:pycharm please specify a different SDK name
    报错原因:有两个现有虚拟环境具有相同的名称(即彼此相同;不同于我正在创建的那个)。
    解决方式:删除其中一个之后,我就可以创建新的虚拟环境。 在setting里面的解释器选择里面,打开show all: 接着在以下弹出窗口里边,对于重名环境用右边“-”进行删除即可。
  9. upload更新代码到服务器中,但是服务器代码一直未更新 报错原因:在连接云服务器(start SSH session)时没有指定,所以产生一个临时的配置的许多其他名称的服务器,因此在upload时上传的目录也不正确 mapping的目录不对。
    解决方式:删除其他名称的服务器,upload指定服务器SCQserver
  10. 公网ip:8000 访问项目无法访问 报错原因: 在云服务器中没有设置允许的端口范围8000 解决方式:添加8000端口到安全组

七、项目回顾的一个参考:https://blog.csdn.net/a_blackmoon/article/details/83272764


转载:https://blog.csdn.net/weixin_44568633/article/details/105335810
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场