Django+Vue.js搜索项目总结

项目目录

项目的创建和环境的配置

主要参考了

https://github.com/michaelbukachi/django-vuejs-tutorial/wiki/Django-Vue.js-Integration-Tutorial这个教程做环境的搭建

最后创建webpack.config.js确定webpack的最终配置

如何运行webpack来构建(build)项目

 

这种webpack配置不适用于生产环境

生产环境的配置教程:https://moduscreate.com/blog/optimizing-react-es6-webpack-production-build/

python manage.py runserver运行报错:

 

原因:

webpack配置文件写完之后我是直接这样运行的:node_modules\.bin webpack,这样虽然命令行上会有提示,但是无法生成打包文件,也就是说django无法找到关于webpack的任何配置

正确的命令是:

node_modules\.bin\webpack --config webpack.config.js

这个命令可以按照webpack.config.js配置文件中写的,在指定目录assets/bundles下生成生成打包文件app.js,同时在根目录下生成mapping文件 webpack-stats.json

在电脑上全局安装了webpack后,可以直接用node_modules\.bin\webpack命令进行打包。

如果需要热部署,可以输入如下命令

node_modules\.bin\webpack --config webpack.config.js --watch

此时启动django,将会自动按mapping文件来寻找相应的打包文件

此时的webpack.config.js:

//webpack.config.js
const path = require('path');
const webpack = require('webpack');
const BundleTracker = require('webpack-bundle-tracker');
const VueLoaderPlugin = require('vue-loader/lib/plugin');

module.exports = {
    mode: 'development',
    context: __dirname,

    //在这里,我们设置了入口点,并为webpack构建的输出设置了文件名和目录。
    entry: './assets/js/index',
    output: {
        path: path.resolve('./assets/bundles/'),
        filename: 'app.js'
    },

    plugins: [
        new BundleTracker({filename: './webpack-stats.json'}),
        new VueLoaderPlugin(),
    ],

    //这里,我们配置了webpack要使用的加载器。检查文件的扩展名,并使用适当的加载器。
    module: {
        rules:  [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: 'babel-loader',
            },
            {
                test: /\.vue$/,
                loader: 'vue-loader'
            }
        ],
    },

    //这确保您可以在代码中使用vue模板。如果没有这部分,就无法渲染组件,会呈现空白
    resolve: {
        alias: {vue: 'vue/dist/vue.js'}
    },

};

数据库的相关配置

navicat+mysql

在navicat中连接mysql不成功,报错:

1045 Access denied for user 'root'@'localhost' (using password:YES)

原因:没有在当前上访问的权限,需要修改权限

解决方法:

1.mysql -u你的用户名 -p你的密码  这个命令进入mysql

2.mysql> 对用户赋予权限的格式:grant 权限 on 数据库.* to 用户名@登录主机 identified by "密码"

mysql> 如,增加一个用户user1密码为password1,让其可以在本机上登录, 并对所有数据库有查询、插入、修改、删除的权限。首先用以root用户连入mysql,然后键入以下命令:

mysql> grant select,insert,update,delete on *.* to user1@localhost Identified by "password1";

如果希望该用户能够在任何机器上登陆mysql,则将localhost改为"%"。

如果你不想user1有密码,可以再打一个命令将密码去掉。

grant select,insert,update,delete on mydb.* to user1@localhost identified by "";

3.flush privileges

问题解决,navicat可以连接

(参考https://blog.csdn.net/weixin_39168678/article/details/80089520

项目文件的配置

 

settings.py中databases的配置:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',  # mysql驱动
        'NAME': 'searchprojectdb',  # 数据库名
        'USER': 'root',  # 链接数据库的用户名
        'PASSWORD': '',  # 连接数据库用的密码
        'HOST': '127.0.0.1',  # 服务器地址
        'PORT': '3306',  # 服务器端口
    }
}

 

直接用命令python manage.py runserver 0.0.0.0:8000运行项目报错:

 

 

原因和解决办法:

在 python2 中,使用 pip install mysql-python 进行安装连接MySQL的库,使用时 import MySQLdb 进行使用

在 python3 中,改变了连接库,改为了 pymysql 库,使用pip install pymysql 进行安装,直接导入即可使用

但是在 Django 中, 连接数据库时使用的是 MySQLdb 库,这在与 python3 的合作中就会报以下错误了

django.core.exceptions.ImproperlyConfigured: Error loading MySQLdb module: No module named 'MySQLdb'

解决方法:在 __init__.py 文件中添加以下代码即可。

import pymysql

pymysql.install_as_MySQLdb()

顾名思义应该是让 Django 把 pymysql 当成 MySQLdb 来使用吧

前后端数据通信

做前后端数据联通测试时,点击按钮期望的结果是“默认列表数据”变为后端传过来的“后台列表数据”,而实际执行报错如下:

用UrlSearchParams做数据传递,前端代码如下:

<template>
<div class="about">
    <h1>This is DataTest</h1>
    {{d2}}
    <p v-for="d in d1">
        {{d}}
    </p>
    <button @click="init">点击获取数据</button>
</div>
</template>

<script>
    export default {
        name: "Demo",
        data() {
            return {
                keyword: '',//v-model绑定的输入框的value
                displayitem: {'title': null, 'price': null},
                d1: ['默认列表数据1', '默认列表数据2'],
                d2: '默认字符串',
            }
        },
        methods: {
            clearInput: function () {
                this.keyword = '';
            },
            init: function () {
                let _this = this;
                let param = new URLSearchParams()
                param.append('username', 'admin')
                param.append('pwd', 'admin')
                this.$axios({
                    method: 'post',
                    url: 'query/',
                    data: param
                }).then(function (response) {
                    _this.d1 = response.data
                }).catch(function (response) {
                    console.log(response)
                })
            },
        },
    }
</script>

如果想直接向后台传递字典数据:

import Qs from 'qs'
export default {
        name: 'Demo',
        data: function data() {
            return {
                listQuery: {
                    page: 1,
                    limit: 20,
                    unit: undefined,
                    title: undefined,
                    price: undefined,
                    type: undefined,
                    amount: undefined,
                    payment: undefined,
                    itemnumber: undefined,
                    model: undefined,
                    singlenumber: undefined,
                    sort: '+id'
                },
                keyword:undefined,
                searchres: null,
                resultNum:null,
                priceOptions: ['0-10', '10-50', '50-100', '100-1000', '1000-10000'],
                paymentOptions: ['0-100', '100-1000', '1000-10000'],
            };
        },
        methods: {
            getList() {
                var _this = this;
                console.log(this.listQuery);
                this.$axios({
                    method: 'post',
                    url: 'query/',
                    transformRequest: [function (data) {
                        // 对 data 进行任意转换处理
                        return Qs.stringify(data)
                    }],
                    data: _this.listQuery //data直接赋值为listQuery这个字典
                }).then(function (response) {
                    _this.searchres = response.data;
                    _this.resultNum=response.data.length;
                    console.log(_this.searchres);
                    console.log(_this.resultNum);
                }).catch(function (response) {
                    console.log(response);
                });
            }
}

URLSearchParams使用示例:

var paramsString = "q=URLUtils.searchParams&topic=api"
var searchParams = new URLSearchParams(paramsString);

for (let p of searchParams) {
  console.log(p);
}

searchParams.has("topic") === true; // true
searchParams.get("topic") === "api"; // true
searchParams.getAll("topic"); // ["api"]
searchParams.get("foo") === ""; // true
searchParams.append("topic", "webdev");
searchParams.toString(); // "q=URLUtils.searchParams&topic=api&topic=webdev"
searchParams.set("topic", "More webdev");
searchParams.toString(); // "q=URLUtils.searchParams&topic=More+webdev"
searchParams.delete("topic");
searchParams.toString(); // "q=URLUtils.searchParams"

urls.py中的代码:

from django.conf.urls import url
from django.views.generic import TemplateView
from myapp import views

urlpatterns = [
    url(r'^$', TemplateView.as_view(template_name='index.html'), name='index'),
    url(r'^query/$',views.query),
]

views.py中的代码:

from django.http import JsonResponse

def query(request):
    print(request.POST.get('username'))
    return JsonResponse(['后台列表数据1','后台列表数据2'],safe=False)

如果不添加safe=False,报错:

'In order to allow non-dict objects to be serialized set the ' 'safe parameter to False.'

查看源码:

def __init__(self, data, encoder=DjangoJSONEncoder, safe=True,
             json_dumps_params=None, **kwargs):
    if safe and not isinstance(data, dict):
        raise TypeError(
            'In order to allow non-dict objects to be serialized set the '
            'safe parameter to False.'
        )
  
  if json_dumps_params is None:
        json_dumps_params = {}
    kwargs.setdefault('content_type', 'application/json')
    data = json.dumps(data, cls=encoder, **json_dumps_params)
    super(JsonResponse, self).__init__(content=data, **kwargs)

如果传递的不是字典类型的话,需要设置safe=False

Django csrf验证的问题

用axios做前端异步请求,除了GET请求之外的django都会返回403状态码,403 Forbidden 是HTTP协议中的一个HTTP状态码。可以简单的理解为没有权限访问此站,服务器收到请求但拒绝提供服务。

在浏览器的network项目中发现是因为csrf验证没有通过,所以请求被拒绝提交。

django会在浏览器的cookie里面保存一项csrf的值

这个是从django生成的,django会检查每个http请求的headers 里面的  X-CSRFToken 项的值是否和cookie里面保存的值相同,如果不相同或者缺失,就拒绝这个请求,如果相同,说明这次请求是从真实用户发起的。

所以要做的就是在每次请求的 headers 里面加上X-CSRFToken:csrftoken值

具体如下,在入口文件index.js中:

//拦截request,
axios.interceptors.request.use((config) => {
  config.headers['X-Requested-With'] = 'XMLHttpRequest';
  let regex = /.*csrftoken=([^;.]*).*$/; // 用于从cookie中匹配 csrftoken值
  config.headers['X-CSRFToken'] = document.cookie.match(regex) === null ? null : document.cookie.match(regex)[1];
  return config
});

 

其中   /.*csrftoken=([^;.]*).*$/    是一个正则表达式,用于从cookie中获取csrftoken的值 ,([^;.]*) 是命名捕获,表示从匹配到的内容中 只获得 ()内的值。

string.match(regex) 得到的是一个数组, 第0项是匹配到的全部内容,第1项是通过命名捕获得到的内容,在这里就是csrftoken的值。

这样就完成了使用axios发送请求的正确配置了,同时保证了网站免受csrf攻击的影响

(这部分参考:https://blog.csdn.net/clark_fitz817/article/details/79226661)

分别console.log了JsonResponse的response,response.data和response.data.columns

后端

sql中判断一个字符串包含另一个字符串:

CHARINDEX('substring','string')返回字符串的索引位置,没有返回0

或者用模糊匹配:where string like '%substring%'(%可以表示0,1或多个字符)

 

让mysql的select语句返回字典类型,只需要这样在建立游标时加一个参数,这样会将每行返回的值放在字典里面,然后再放进一个list里面。

cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)

结果的样例如下:

[{'货品标题': '装订铁圈双线铁圈台历圈挂历铁圈 6.4mm-25.4mm 银色34齿23齿包邮 规格: 14.3mm 银色 100支 34齿', '单价': '40', '数量': '1', '实付款': None, '单位': '支', '货号': '', '型号': '', '单品货号': '', '货品种类': None}, {'货品标题': '装订铁圈双线铁圈台历圈挂历铁圈 6.4mm-25.4mm 银色34齿23齿包邮 规格: 9.5mm 银色 100支 34齿', '单价': '26', '数量': '1', '实付款': None, '单位': '支', '货号': '', '型号': '', '单品货号': '', '货品种类': None}]

前端

Vue.js使用Webpack打包

不能识别vue文件中的css代码的解决方案:

使用vue-cli搭建项目有内置的webpack方案,但是没有使用scss。vue提供了初始化的webpack模板,其中使用了vue-loader。vue-loader默认只支持sass,要是想要使用scss,必须安装node-sass和sass-loader,并修改相关webpack配置。具体操作如下:

  1. 依次执行以下命令 安装node-sass和sass-loader
npm install node-sass --save-dev
npm install sass-loader --save-dev

安装node-sass遇到报警:

npm WARN sass-loader@8.0.0 requires a peer of sass@^1.3.0 but none is installed. You must install peer dependencies yourself.

这是因为当前sass的版本太高,webpack编译时出现了错误,这个时候只需要换成低版本的就行,下面说一下修改方法,很简单,如下,找到package.json文件,里面的 "sass-loader"的版本更换掉 就行了。

我本地是将 "sass-loader": "^8.0.0",更换成了 "sass-loader": "^7.3.1"。

卸载当前版本sass-loader命令:npm uninstall sass-loader 

安装低版本的sass-loader命令:npm install sass-loader@7.3.1 --save-dev

若npm安装太慢,可以使用cnpm安装

2.修改相关webpack配置 打开webpack.base.config.js, 在module里的rules中加上:

 {
    test: /\.scss$/,
    loaders: ["style-loader", "css-loader", "sass-loader"]
    //注意这里要加-loader,因为:
    //It's no longer allowed to omit the '-loader' suffix when using loaders
  }

3.如果要在vue文件中的style使用scss,则在 style处声明:

<style rel="stylesheet/scss" lang="scss"></style>

最好加上scoped,这样可以保证当前style只对当前组件有效,避免组件间样式相互影响。即:

<style rel="stylesheet/scss" lang="scss" scoped> ... </style>

从上到下依次打印了JsonResponse的response,response.data和response.data.columns

 图标字体的获取和引用:

从icomoon下载图标字体,压缩包中包含svg,woff, ttl,eot格式的fonts文件和自动生成的style.css文件。只有一个search图标时的style.css文件如下:

@font-face {
  font-family: 'icomoon';
  src:  url('fonts/icomoon.eot?b5uopg');
  src:  url('fonts/icomoon.eot?b5uopg#iefix') format('embedded-opentype'),
    url('fonts/icomoon.ttf?b5uopg') format('truetype'),
    url('fonts/icomoon.woff?b5uopg') format('woff'),
    url('fonts/icomoon.svg?b5uopg#icomoon') format('svg');
  font-weight: normal;
  font-style: normal;
  font-display: block;
}

[class^="icon-"], [class*=" icon-"] {
  /* use !important to prevent issues with browser extensions that change fonts */
  font-family: 'icomoon' !important;
  speak: none;
  font-style: normal;
  font-weight: normal;
  font-variant: normal;
  text-transform: none;
  line-height: 1;

  /* Better Font Rendering =========== */
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

.icon-search:before {
  content: "\e986";
}

Demo.vue 的style标签中引入style.css,或者在index.js文件中引入style.css,注意相对路径的写法

import './icons/style.css'//index.js
@import '../icons/style.css';//Demo.vue中的style标签内

 网页效果:

报错:

注意这里报错的内容是找不到以hash码为文件名的woff和ttf文件!!!

调整后的效果:

这个错误和webpack.config.js文件中webpack-loaders的配置有关。

Webpack loaders

webpack的loaders是一块很重要的组成部分。我们都知道webpack是用于资源打包的,里面的所有资源都是“模块”,内部实现了对模块资源进行加载的机制。但是Webpack本身只能处理 js模块,如果要处理其他类型的文件,就需要使用 loader 进行转换。  Loader 可以理解为是模块和资源的转换器,它本身是一个函数,接受源文件作为参数,返回转换的结果,例如可以使用loader加载器可以快速编译预处理器(less,sass,coffeeScript)。 Loader 可以在require()引用模块的时候添加,也可以在 webpack 全局配置中进行绑定,还可以通过命令行的方式使用。

file-loaders可以把 JavaScript 和 CSS 中导入图片的语句替换成正确的地址,并同时把文件输出到对应的位置。默认情况下,生成的文件的文件名就是文件内容的 MD5 哈希值并会保留所引用资源的原始扩展名。

例如项目中这部分代码,经过file-loader处理变成下面这样,并且在输出文件夹bundles中多出了引用的文件,其中文件名用原名转化成的hash值命名。

src:  url('fonts/icomoon.eot?b5uopg');
src:  url('fonts/icomoon.eot?b5uopg#iefix') format('embedded-opentype'),
  url('fonts/icomoon.ttf?b5uopg') format('truetype'),
  url('fonts/icomoon.woff?b5uopg') format('woff'),
  url('fonts/icomoon.svg?b5uopg#icomoon') format('svg');
src: url(2dad638c5bf5628f1bbc3a69d67584e7.eot);
src:  url(2dad638c5bf5628f1bbc3a69d67584e7.eot) format('embedded-opentype'),
  url(d9dbf780aac370efed096247ee86a82f.ttf) format('truetype'),
  url(a9f97f21133f416450932b66ed98c9f9.woff) format('woff'),
  url(ab09a44a063ed7a1109730c77c3ab28d.svg) format('svg');

 可以使用查询参数名为文件配置自定义文件名模板。例如,要将一个文件从上下文目录复制到保留完整目录结构的输出目录中,可以使用以下命令。

//webpack.config.js
{
  loader: 'file-loader',
  options: {
    name: '[path][name].[ext]'
  }
}

 加上这条语句之后,载入图标字体成功。

当el-table元素中注入data对象数组后,在el-table-column中用prop属性来对应对象中的键名即可填入数据,用label属性来定义表格的列名。可以使用width属性来定义列宽。这样可以实现按列打印数据。

<el-table
                :data="searchres"
                highlight-current-row
                style="width: 100%;"
        >
            <el-table-column label="货品标题" prop="货品标题" align="center" width="300">
                <template slot-scope="scope">
                    <span>{{ scope.row.货品标题 }}</span>
                </template>
            </el-table-column>
            <el-table-column label="单价" prop="单价" align="center" width="100">
                <template slot-scope="scope">
                    <span>{{ scope.row.单价 }}</span>
                </template>
            </el-table-column>
            <el-table-column label="数量" prop="数量" align="center" width="100">
                <template slot-scope="scope">
                    <span>{{ scope.row.数量 }}</span>
                </template>
            </el-table-column>
            <el-table-column label="实付款" prop="实付款" align="center" width="100">
                <template slot-scope="scope">
                    <span>{{ scope.row.实付款 }}</span>
                </template>
            </el-table-column>
            <el-table-column label="单位" prop="单位" align="center" width="100">
                <template slot-scope="scope">
                    <span>{{ scope.row.单位 }}</span>
                </template>
            </el-table-column>
            <el-table-column label="货号" prop="货号" align="center" width="150">
                <template slot-scope="scope">
                    <span>{{ scope.row.货号 }}</span>
                </template>
            </el-table-column>
            <el-table-column label="型号" prop="型号" align="center" width="150">
                <template slot-scope="scope">
                    <span>{{ scope.row.型号 }}</span>
                </template>
            </el-table-column>
            <el-table-column label="单品货号" prop="单品货号" align="center" width="150">
                <template slot-scope="scope">
                    <span>{{ scope.row.单品货号 }}</span>
                </template>
            </el-table-column>
            <el-table-column label="货品种类" prop="货品种类" align="center" width="150">
                <template slot-scope="scope">
                    <span>{{ scope.row.货品种类 }}</span>
                </template>
            </el-table-column>
        </el-table>

CSS语法相关

inline-block类型元素的居中:

上一级元素设置text-align:center

block类型元素的居中:

直接margin:0 auto;

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值