Django的一些小总结

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 文件

  1. 在这里我们以一个 todo_list 表格为例,表结构如下图

    Django的一些小总结

  2. Django 视图函数中的代码实现

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
  1. 效果

直接在浏览器中请求 http://127.0.0.1:8000/export/export_file/, 即可自动下载 todo_list.xls文件。结果如下:

Django的一些小总结

二. 数据库插入 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个字节,所以存储不了。

解决办法:
  1. 将包含表情该字段设置为 utf8mb4
ALTER TABLE table_name MODIFY colum_name VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
  1. 需要设置下表的字符集
ALTER TABLE table_name CHARSET=utf8mb4;
  1. 需要设置本数据库为 utf8mb4
SET NAMES utf8mb4;
  1. 最后修改 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'},
    }
}
  1. 实现效果

Django的一些小总结

三. 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。

  1. 代码场景如下
# 如果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''用户未激活'})
  1. 解决办法

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 参数的 就不会用默认值;

Django的一些小总结

可以看到 当获取 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 = [021]
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 表

  1. 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'
  1. 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:

  1. 创建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)
  1. 模型管理器查询
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), NoneNone)
# 好了,现在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

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!