Django开发近三年,总结的一些经验和坑
我是一只快乐的小 Djangor
基本目录结构:
-
一. 后端视图函数返回 excel 数据流, 浏览器请求 url 后自动下载 excel 文件
-
二. mysql 数据库插入 emoji 表情报错 Incorrect string value: ‘\xF0\x9F\x98\x
-
三. Django authenticate 已经包含 is_active 判断后,无法单独再进行 active 判断问题
-
四. Django 修改密码后 session 失效, 用户不能继续保持登录状态问题
-
五. request.POST.get(“field”, “默认值”) 获取不到默认值
-
六. django 自定义排序
-
七. 获取PIL.image图片的内容,并返回给浏览器图片内容
-
八. Django 扩展自带的 User 表
-
九. Django 的 ORM 实现 group_concat 的分组查询
-
十. python 将图片二进制数据转换成 Django file 对象
一. 后端视图函数返回 excel 数据流, 浏览器请求 url 后自动下载 excel 文件
import io
import pandas as pd
from django.http.response import HttpResponse
from daka_app.models import TodoList
def export_file(request):
"""
导出excel
api: export/export_file/
"""
# 首先获取到表里的全部数据
toso_list = list(TodoList.objects.values_list())
# 通过 pandas 生成 DataFrame 对象
dt = pd.DataFrame(toso_list, columns=['ID', '内容', '创建时间', '完成时间', '截止时间', '完成状态', '用户ID', '订阅状态'])
# 然后将 DF 对象转换成 excel 保存到 io 流中
output = io.BytesIO()
dt.to_excel(output, index=False)
output.seek(0)
# 构建 响应 对象,完成相关的响应头操作
response = HttpResponse()
response["Content-Type"] = "application/vnd.ms-excel"
# todo_list 为 excel 文件名,用户可以自行定义
response['Content-Disposition'] = 'attachment;filename=todo_list.xls'
# response['Access-Control-Expose-Headers'] = 'Content-Disposition'
response.write(output.getvalue())
output.close()
return response
-
效果
直接在浏览器中请求 http://127.0.0.1:8000/export/export_file/
, 即可自动下载 todo_list.xls文件。结果如下:
二. 数据库插入 emoji 表情报错 Incorrect string value: ‘xF0x9Fx98x
报错内容如下
django.db.utils.OperationalError: (1366, "Incorrect string value: '\xF0\x9F\x98\x81</...' for column 'content' at row 1")
报错原因:
原因:mysql 的 UTF-8 只支持三个字节的存储,而一般字符是三个字节,但是 emoji 表情是4个字节,所以存储不了。
解决办法:
-
将包含表情该字段设置为 utf8mb4
ALTER TABLE table_name MODIFY colum_name VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-
需要设置下表的字符集
ALTER TABLE table_name CHARSET=utf8mb4;
-
需要设置本数据库为 utf8mb4
SET NAMES utf8mb4;
-
最后修改 django的setting.py
setting.py
文件添加 OPTIONS 属性
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'xxxxxx',
'USER': 'xxxxx',
'PASSWORD': 'xxxxx',
'HOST': '127.0.0.1',
'PORT': 3306,
'CONN_MAX_AGE': 180,
'OPTIONS': {'charset': 'utf8mb4'},
}
}
-
实现效果
三. Django authenticate 已经包含 is_active 判断后,无法单独再进行 active 判断问题
场景需求:我们需要 authenticate() 只去判断用户名密码是否正确,而不去判断 is_active 属性;
现在的 django.contrib.auth.authenticate(username=username, password=password)
默认会对 is_active 进行判断;
即如果用户 is_active = 0, 就算用户名,密码正确,authenticate(username=username, password=password)
仍然会返回 None。
-
代码场景如下
# 如果is_active = 0,即使用户名密码正确,user 也会是 None
user = authenticate(username=username, password=password)
if not user:
return JsonResponse({'code': 1, 'message': '用户名或密码错误'})
if not user.is_active:
return JsonResponse({'code': 1, 'message': '用户未激活'})
-
解决办法
setting.py
添加如下代码即可
AUTHENTICATION_BACKENDS = ['django.contrib.auth.backends.AllowAllUsersModelBackend']
四. Django 修改密码后 session 失效, 用户不能继续保持登录状态问题
正常来说用户修改密码后需要重新登录,但是如何实现更改密码后,用户继续保持登陆状态的功能呢?
解决方法:更新密码后,再去更新用户的 session
from django.contrib.auth import authenticate, login, logout, update_session_auth_hash
def modify_password(request):
"""
@api {post} api/modify_password/ 3. 修改密码
@apiDescription 修改密码
@apiGroup 账号功能
@apiParam {string} old_password 旧密码
@apiParam {string} new_password 新密码
@apiSuccessExample Success-Response:
{
'code': 0,
'message': '修改密码成功'
}
@apiErrorExample Error-Response:
{
'code': 1,
'message': '用户名不存在/修改密码失败'
}
"""
oldpassword = request.POST.get('old_password')
newpassword = request.POST.get('new_password')
try:
user = request.user
if user.check_password(oldpassword): # 判断前端传过来的旧密码是否正确,如果正确,返回一个值
user.set_password(newpassword) # 把前端输入的新密码加密放进数据库里面
user.save()
update_session_auth_hash(request, user) # 更新session,因为原来的session存放的是旧密码
else:
return JsonResponse({'code': 1, 'message': '用户名或旧密码错误'})
except Exception as e:
logger.error(e)
return JsonResponse({'code': 1, 'message': '服务器异常'})
return JsonResponse({'code': 0, 'message': '修改密码成功'})
五. request.POST.get(“field”, “默认值”) 获取不到默认值
field = request.POST.get("field", "9999")
今天无意碰到,获取不到默认值 9999;
这里讲个知识点:如果前端没有传 field 参数,则上面的代码可以获取到默认值 9999;
但是前端传了 field 参数,只是 field 参数为空 则后端是能接收到 field 参数的 就不会用默认值;
可以看到 当获取 b 且提供默认值时 默认值并没有生效,这是因为能 get 到 b;
所有 字典的 get 方法只有在这个键不存在时 才会返回默认值,而不会在乎值是不是为空。
六. django 自定义排序
今天遇到个自定义排序场景 按 status 不规则排序;
平时只用到 order_by(“status”) 或 order_by(“-status”);
status 有 0, 1, 2 三种类型;
传统只能 012 顺序 或 210 倒序;
现在想按 [0, 2, 1] 或 [1, 0, 2] 等自定义排序;
解决方法如下:
# 按 0 2 1排序
result = models.objects.all()
order_by = [0, 2, 1]
ordering = 'FIELD(`status`, %s)' % ','.join(str(status) for status in order_by)
result = result.extra(select={'ordering': ordering}, order_by=('ordering', ))
七. 获取 PIL.image 图片的内容,并返回给浏览器图片内容
需求:在 Django 视图函数中,用 qrcode 生成的图片二维码是类似 PIL.image 格式。
我想返回给前端二维码图片内容,但是又不想保存文件到临时的服务器目录。
解决代码如下:
import qrcode
from io import BytesIO
from PIL import Image
from django.http import HttpResponse
......
# 根据key生成指定二维码
img = qrcode.make(secret_content)
# 生成流文件
stream = BytesIO()
# 将图片保存到流文件
img.save(stream, "png")
# 将二维码图片返回给前端
return HttpResponse(stream.getvalue(), content_type='image/png')
八. Django 扩展自带的 User 表
-
models.py 文件
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
# 扩展系统自带的User
phone = models.CharField(max_length=255, null=True, unique=True)
faces = models.TextField(null=True)
flag = models.IntegerField(default=0)
class Meta:
db_table = 'auth_user'
-
settings.py 文件
# 自定义User
AUTH_USER_MODEL = your_app_name.User'
九. Django 的 ORM 实现 group_concat 的分组查询
原始SQl语句: select ip, group_concat(id) as id from whitelist group by ip;
Django-ORM:
-
创建Concat类
from django.db.models import Aggregate, CharField
class Concat(Aggregate):
"""ORM用来分组显示其他字段 相当于group_concat"""
function = 'GROUP_CONCAT'
template = '%(function)s(%(distinct)s%(expressions)s)'
def __init__(self, expression, distinct=False, **extra):
super(Concat, self).__init__(
expression,
distinct='DISTINCT ' if distinct else '',
output_field=CharField(),
**extra)
-
模型管理器查询
WhiteList.objects.values('ip').annotate(id=Concat('id'))
十. python 将图片二进制数据转换成 Django file 对象
from django.core.files.uploadedfile import InMemoryUploadedFile
from io import BytesIO
f = BytesIO()
# 图片的二进制数据
data = b'xffxd8xffxe0x00x10JFIFx00x0.........省略9xffxd9'
f.write(data)
# InMemoryUploadedFile 需要的参数:
# file, field_name, name, content_type, size, charset, content_type_extra=None
image = InMemoryUploadedFile(f, None, 'aa', None, len(data), None, None)
# 好了,现在image就是Django的file对象了
In[11]: image
Out[11]: <InMemoryUploadedFile: aa (None)>
In[12]: type(image)
Out[12]: django.core.files.uploadedfile.InMemoryUploadedFile
判断二进制数据是否为图片
import imghdr
s=imghdr.what(None,data)
不是图片的话返回None 是图片返回图片类型的字符串
原文始发于微信公众号(大码怪):Django的一些小总结
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/36090.html