主要记录自己的使用Django过程中的一些踩坑,具体分类请看右侧目录
一、使用Pycharm调试Django项目
- 1、在当前项目下打开Run->Edit Configurations
- 2、点击+,选择新增python 脚本,如图,填好name,script,script parameter
这里的Parameter为runserver,也就是manange.py运行的时候需要追加的参数,完整命令为
python manage.py runserver (127.0.0.1:8000 可选)
,
所以对于一些执行时候需要使用parser解析命令的脚本同样可以使用相同的方式配置调试。 - 3、在项目需要的调试代码行打上断点,当有请求到达的时候就会开启调试,一般为路由中。
二、CORS跨域配置
1、跨域问题简介
跨域指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,协议,域名,端口都要相同,其中有一个不同都会产生跨域;
跨域主要有应用在前后端分离的时候,比如Vue+Django。Vue本身由于node的支持,在本地运行的时候会占用localhost:8080
,Django后端有自己的服务器占用localhost:8000
。前端后端想要联调,就会有跨域问题产生。可以使用CORS(Cross-Origin Resource Sharing),跨域资源共享解决。
大概原理:当使用XMLHttpRequest发送请求时,如果浏览器发现违反了同源策略就会自动加上一个请求头 origin;后端在接受到请求后确定响应后会在 Response Headers 中加入一个属性 Access-Control-Allow-Origin
;浏览器判断响应中的 Access-Control-Allow-Origin
值是否和当前的地址相同,匹配成功后才继续响应处理,否则报错。
CORS解释
2、前后联调的配置
Django需要项目环境下安装对应模块
pip install django-cors-headers
项目setting.py配置文件添加配置
# settings.py
INSTALLED_APPS = [
...
'corsheaders', # demo
'rest_framework',
]
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware', # 需注意与其他中间件顺序,这里放在最前面即可
...
]
# 支持跨域配置开始
CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_CREDENTIALS = True
- INSTALLED_APPS中要注意添加的模块
'corsheaders',
后面不要少逗号,否则项目初始化加载settings.py的时候会做字符串拼接为corsheaderrest_frame
因找不到模块从而报错。 - MIDDLEWARE中添加的中间件
corsheaders.middleware.CorsMiddleware
要放到第一个位置,因为在请求经过中间件的时候,是按照MIDDLEWARE中的顺序,如果不符合某一个中间件定义则直接终止。
前端Vue需要安装axios模块
cnpm install axios
然后Vue需要对请求做统一管理,可以再vue项目下/src/
创建一个/api/api.js
,请求头和请求路径配置,比如超时时间,Content-Type响应头,封装get或者post方法等。
//前端同学写的
import axios from 'axios';
axios.defaults.timeout = 5000; //超市时间是5秒
axios.defaults.withCredentials = true; //允许跨域
//Content-Type 响应头
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8';
//基础url
axios.defaults.baseURL = "http://localhost:8000";
/**
* 封装get方法
*/
export function get(url,params={}){
return new Promise((resolve,reject) => {
axios.get(url,{params:params})
.then(response =>{
resolve(response.data);
})
.catch(err =>{
reject(err);
})
});
}
具体Vue与Django前后端联调参考demo:传送门
三、Django中request针对不同Content-Type的解析
后端使用一般要拿到http封装的request做路由处理,但是不同的request的body内容格式也不同,要个请求header中定义的Content-Type进行解析。路由函数中可以根据requet.META
获取到http请求的请求头。
请求头中的内容长度与内容类型:
- CONTENT_LENGTH – The length of the request body (as a string).
- CONTENT_TYPE – The MIME type of the request body.
Content-Type两种重要的格式:
- x-www-form-urlencoded:表单内的数据转化内键值对,也只能上传键值对,并且键值对都是间隔分开的。
- raw:可以上传任意格式的文本,可以上传text、json、xml、html等,其实主要的还是传递json格式的数据,当后端要求json数据格式的时候,就要使用此种格式来测试。
1、x-www-form-urlencoded格式
当content-type为x-www-form-urlencoded的时候,直接使用request.POST.get('username')
的方式可以获取对应字段的值
2、raw格式
content-type为raw格式,则需要先将request.body中内容先进行解码为获取字段得值。
postbody = request.body
json_param = json.loads(postbody.decode())
username = json_param.get('username','')
password = json_param.get('password','')
注:如果使用JsonResponse
返回Json数据给前端的时候,可以先定义一个context
字典保存参数和值,JsonResponse
会将context
字典通过json.dumps(data)
编码为Json格式数据。但是但是,在Python中写字典时有习惯这种格式如下
context = {
"key1": "value1",
"key2": "value2",
...
"key n": "value n",
}
考虑到易读性(或者是linux下操作单行查看???)的问题,通常习惯最后一行字典键值对"key n": "value n",
会习惯带一个,
,或者键值对以单引号格式书写'key n': 'value n',
,这两种写法都不符合json文件格式。会爆如下错误
json.decoder.JSONDecodeError: Expecting property name enclosed in double quotes:
四、自定义上传图片
通常数据库中可以通过两种方式定位解析到上传到后台图片:
1、数据库保存图片存储路径
2、数据库存储图像的实际编码
Django也可以定义上传MEDIA_ROOT路径之后,通过自定义模型类,保存上传图片的路径,图片保存在MEDIA_ROOT之下。
以下是自定义上传文件,而不是使用模型类。
配置:
全局settings.py配置,添加上传图片的保存路径:
MEDIA_ROOT = os.path.join(BASE_DIR,"static/media")
前端:
前端写一个任意的表单,但是需要注意的是,上传文件时必须是post上传,编码方式:enctype必须是:multipart/form-data这个类型。
<form method="post" enctype="multipart/form-data" action="/uploal_action/" >
<input type="file" name="pic"><br/>
<input type="submit" value="上传"><br/>
</form>
后台可以获取:
pic = request.FILES['pic']
:上传文件类的对象,
pic.name
:获取文件的名字,
pic.chunks()
:每次返回这个文件的一块内容。
后端路由:
先获取文件对象,然后写入。另外:这最好使用os.makedirs
,如果保存文件所在路径中,有的父目录不存在,会提前创建。
def upload_file(request):
"""保存上传文件"""
# 1.获取上传的文件
pic = request.FILES['pic']
# 2.创建文件
save_path = '%s/test/%s'%(MEDIA_ROOT,pic.name)
os.makedirs(os.path.dirname(save_path), exist_ok=True)
with open(save_path, 'wb') as f :
# 获取上传文件的内容并写到创建文件中
# pic.chunks():分块的返回文件
for content in pic.chunks():
f.write(content)
只写了大概的处理,路由映射还需要自己配昂。