前言
首先跟追我博文的博友告歉,这篇之后可能要得1周后才能继续。爱人到了需要取钉的时间要入院陪护,所以我下周是没有办法写了,望各位博友见谅。
娃少喝的奶,她记你一世。抗疫有你,铭记一生。
这次跟大家分享python的数据库操作、redis集成,下面就通过一个登录接口体现。
一、redis集成依赖
需要安装依赖包:django-redis
安装方式这里也不重复说了,看前面的博文。
二、登录
1.用户登录设计
现在一般用户表设计:
1、密码密文存储
2、登录后生成token
3、登录多用手机号 + 密码,或账号 + 手机验证码
这里前面在做用户新增的时候,做了自定义的设置密码字段值,这里调整下逻辑,与这里手机号登录配合
models.py修改
# This is an auto-generated Django model module.
# You'll have to do the following manually to clean this up:
# * Rearrange models' order
# * Make sure each model has one field with primary_key=True
# * Make sure each ForeignKey and OneToOneField has `on_delete` set to the desired behavior
# * Remove `managed = False` lines if you wish to allow Django to create, modify, and delete the table
# Feel free to rename the models, but don't rename db_table values or field names.
from django import forms
from django.contrib.auth.hashers import make_password
from django.db import models
class UserInfo(models.Model):
id = models.AutoField(verbose_name='主键', primary_key=True, unique=True)
name = models.CharField(verbose_name='姓名', max_length=200, blank=True, null=True)
mobile = models.CharField(verbose_name='手机号', max_length=20, blank=True, null=True)
# 性别
sexChoices = (
(1, '男'), (0, '女')
)
sex = models.IntegerField(verbose_name='性别', blank=True, choices=sexChoices, null=True)
nickname = models.CharField(verbose_name='昵称', max_length=30, null=True)
password = models.CharField(verbose_name='密码', max_length=128, default='')
salt = models.CharField(verbose_name='盐值', max_length=10, default='123456')
memo = models.CharField(verbose_name='备注', max_length=500, blank=True, null=True)
create_by = models.IntegerField(verbose_name='创建人ID', blank=True, null=True)
create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True, blank=True, null=True)
delChoices = ((0, '否'), (1, '是'))
is_deleted = models.IntegerField(verbose_name='是否删除', default=0, choices=delChoices, blank=True, null=True)
# 关联查询时返回内容
def natural_key(self):
return {
"id": str(self.id),
"name": str(self.name),
'nickname': str(self.nickname),
"mobile": str(self.mobile),
"sex": str(self.sex)
}
# 将属性和属性值转换成dict 列表生成式
# 自定义转字典
def toDict(self):
return dict([(attr, getattr(self, attr)) for attr in
[f.name for f in self._meta.fields]])
class Meta:
managed = False
db_table = 'user_info'
ordering = ['-create_time']
verbose_name = '用户信息'
verbose_name_plural = '用户信息'
# 修改&添加提交时被执行,对密码进行加密
def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
# if len(str(self.salt)) == 0:
# self.salt = self.password
# 密码加密,使用盐值加密
# self.password = make_password(self.password, self.salt)
self.password = make_password(self.password, self.mobile)
super().save(force_insert=False, force_update=False, using=None, update_fields=None)
变更点:
1、表数据自定义转字典的方法,见toDict(self)方法
2、数据入库的password字段值逻辑调整,见def save()方法
2.登录接口
import json
import django_redis
from django.contrib.auth.hashers import check_password
from django.core import serializers, signing
from django.core.paginator import Paginator
from django.core.serializers import serialize
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from user.models import UserInfo
# Create your views here.
# @csrf_exempt表示视图可以进行跨域请求
@csrf_exempt
def userLogin(request):
# 统一响应对象
response = {
'code': 200,
'success': True,
'msg': '成功'
}
try:
body = json.loads(request.body.decode("utf-8"))
# 请求参数
param = {
}
param['mobile'] = body['mobile']
param['password'] = body['password']
# python判断空特别要注意‘’不代表就是None
if param['password'] is None or not param['password'] or param['mobile'] is None or not param['mobile']:
response['msg'] = '手机号或密码为空'
response['code'] = 20001
response['success'] = '失败'
return JsonResponse(response)
else:
list = UserInfo.objects.filter(mobile=param['mobile']).all()
if list.count == 0:
response['msg'] = '用户信息不存在'
response['code'] = 20002
response['success'] = '失败'
return JsonResponse(response)
else:
# 就直接取第一条了
user = list.first()
dbPassword = user.password
# django的自带密码反向校验
res = check_password(param['password'], dbPassword)
if res:
token = signing.dumps(param)
# redis连接获取
cache = django_redis.get_redis_connection()
# 拼接字符串,大量推荐用这个,少量可以用+
key = ":".join(['zwDjangoProject', param['mobile']])
print(key)
# 缓存key
cache.set(key, token, 30)
# 直接把token放入data返回
# response['data'] = token
# 对象多加一个token属性,有一说一:这里跟java不一样,如果对象没有这个属性java没法set,这里这点还不错
user.token = token
# 对象转字典自带__dict__方法
# userDict = user.__dict__
# print(userDict)
# 字典toString
# userDict.__str__()
# 这里这样处理的对象,实际上data就是一整个字符串,还是不是有嵌套的json字符串
# response['data'] = userDict.__str__()
# 下面这里把字典转json字符串失败,不能直接像java那样转json字符串
# print(userDict)
# userJsonStr = json.dumps(user.__dict__)
# print(userJsonStr)
# response['data'] = userJsonStr
# response['data'] = user
# 这种方式也是失败的,以为是date类型字段导致,还做了删除元素
# userJsonStr = serialize('json', user, use_natural_foreign_keys=False)
# print(userJsonStr)
# response['data'] = userJsonStr
# userDict.pop('_state')
# userDict.pop('create_time')
# userJsonStr = serialize('json', user)
# print(userJsonStr)
# response['data'] = userJsonStr
# 下面是我走通的:不直接使用__dict__,在models.py自定义转字典的方法。应该还有其他方法,后面再补充
userDict = user.toDict()
userDict['token'] = token
response['data'] = userDict
return JsonResponse(response)
else:
response['msg'] = '密码错误'
response['code'] = 20003
response['success'] = '失败'
return JsonResponse(response)
except:
response['data'] = ''
response['code'] = 500
response['msg'] = '失败'
# 响应结果对象
return JsonResponse(response)
@csrf_exempt
def userList(request):
print(request.method)
# 统一响应对象
response = {
'code': 200,
'success': True,
'msg': '成功'
}
try:
body = json.loads(request.body.decode("utf-8"))
# 请求参数
param = {
}
param['page'] = body['page']
param['size'] = body['size']
param['name'] = body['name']
param['mobile'] = body['mobile']
# 数据
list = UserInfo.objects.all()
# 查询数据:模糊查询
if param['name'] is not None:
list = list.filter(name__contains=param['name'])
if param['mobile'] is not None:
list = list.filter(mobile__contains=param['mobile'])
# 分页插件
paginator = Paginator(list, param['size'])
# 获取总条数
response['total'] = paginator.count
# 获取当前页数据集合
records = paginator.page(param['page'])
# 将数据序列化为json对象,并且关联查询外键的数据
response['data'] = json.loads(serializers.serialize('json', records, use_natural_foreign_keys=True))
except:
response['data'] = ''
response['code'] = 500
response['msg'] = '失败'
# 响应结果对象
return JsonResponse(response)
接口说明
- 登录接口见userLogin()方法,这里是手机号 + 密码方式登录。
- 登录逻辑大致就是根据手机号查询用户,匹配加密的密码是否相等
- 数据库查询操作使用的是django自带的orm框架,框架有部分约定缩成的编码方式,比如字段过滤那块
- redis这里也是django的支持,使用思路也特简单,就是获取redis连接,设置缓存信息
- token生成也是使用的django自带的,这个有对应的反向校验方法(注意为防止被反算,可以跟对象补充设置一个salt属性,再进行dumps)
- 对象序列化,转json,代码里面试验了几种方式,最终还是自定义的转字典方法后再转json靠谱
- 其他的也不多说了,代码上码有些注释
3.查看登录效果
总结
- 1、对象增加不存在的属性,设置值方便,都不用像java那样设置属性,注解表示表不存在,这里直接就可以:对象.xxx = aaaa
- 2、对象转json有点费劲(不知道有没有类似fastjson、hutool的相关工具包)
下一阶段
1、先试试django的多表表连接查询
2、restful风格接口
3、找找是否有相关类似hutool的工具包
希望能帮到大家,技术人,加油。