Django之使用自定义用户表(AbstractUser)/自定义登录验证(jwt)/获取当前登录用户

基本步骤:

一、自定义用户表:

1、自定义的用户表继承AbstractUser;
2、settings.py添加配置,指向用户表:AUTH_USER_MODEL = 'myUser.Account'(子项目名称.用户表models名称);
3、python manage.py makemigrations
4、python manage.py migrate

二、自定义登录验证:
# 签发:
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
# 生成token:
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
三、获取当前登录用户:
# 通过headers获取token:
token = request.META.get('HTTP_AUTHORIZATION')
# 情况一:token的传参格式为JWT xxxx
token = request.META.get('HTTP_AUTHORIZATION')[4:]
# 情况二:token的传参格式为TOKEN xxxx
token = request.META.get('HTTP_AUTHORIZATION')[7:]
# 然后解析出user_id和username:
jwt_decode_handler = api_settings.JWT_DECODE_HANDLER
  • 基本目录结构


    基本目录结构

话不多说,直接上代码

  • models:
from django.db import models
from django.contrib.auth.models import AbstractUser


"""
基类:可以把通用的字段定义这里,其他地方继承基类即可拥有
"""
class BaseModel(models.Model):
    updated_tm = models.DateTimeField(auto_now=True)
    created_tm = models.DateTimeField(auto_now_add=True)

    class Meta:
        abstract = True

"""
用户表:自定义的用户表
想要使用自定义的用户表进行登录验证,需要满足下面的条件:
1、需要继承AbstractUser;
2、settings.py添加配置:AUTH_USER_MODEL = 'myUser.Account'(子项目名称.用户表models名称)
"""
class Account(AbstractUser, BaseModel):
    user_id = models.AutoField(help_text="用户id", primary_key=True)
    username = models.SlugField(max_length=128, help_text="用户名", unique=True)
    password = models.CharField(max_length=128, help_text="用户密码")
    nickname = models.CharField(max_length=128, help_text="用户昵称")

    # 指定数据库表信息
    class Meta:
        db_table = 'user'
        verbose_name = '用户基础信息'
        verbose_name_plural = verbose_name

"""
项目表:重点关注editor字段
"""
class ProjectList(BaseModel):
    """项目基本信息"""
    project_id = models.AutoField(help_text="项目id", primary_key=True)
    project_name = models.SlugField(max_length=128, help_text="项目名", unique=True)
    editor = models.CharField(max_length=128, default='admin', help_text="编辑者")

    class Meta:
        db_table = 'project'
        verbose_name = '项目基本信息'
        verbose_name_plural = verbose_name
  • 自定义用户数据库表
自定义用户数据库表
  • serializers
from rest_framework import serializers
from myUser.models import BaseModel, Account
from myProject.models import ProjectList
import time


class BaseSerializer(serializers.ModelSerializer):
    """基类序列化器"""
    create_tm_format = serializers.DateTimeField(source="created_tm",
                                                 format="%Y-%m-%d %H:%M:%S",
                                                 required=False,
                                                 read_only=True,
                                                 help_text='创建时间(北京时间)')

    update_tm_format = serializers.DateTimeField(source="updated_tm",
                                                 format="%Y-%m-%d %H:%M:%S",
                                                 required=False,
                                                 read_only=True,
                                                 help_text='更新时间(北京时间)')

    created_tm = serializers.DateTimeField(required=False,
                                           read_only=True,
                                           help_text='创建时间(时间戳)')

    updated_tm = serializers.DateTimeField(required=False,
                                           read_only=True,
                                           help_text='更新时间(时间戳)')

    class Meta:
        model = BaseModel
        fields = ("created_tm", "updated_tm",
                  "create_tm_format", "update_tm_format")


class UserSerializer(BaseSerializer):
    """用户基本信息"""
    user_id = serializers.IntegerField(read_only=True)

    class Meta:
        model = Account

        fields = ('username', 'password', 'user_id')


class ProjectListSerializer(BaseSerializer):
    """项目基本信息"""
    class Meta:
        model = ProjectList

        fields = ('project_id', 'project_name', 'editor',
                  'created_start_tm', 'created_end_tm',
                  "created_tm", "updated_tm")
  • filter
import django_filters
from myUser.models import Account
from myProject.models import ProjectList


class UserFilter(django_filters.rest_framework.FilterSet):
    """用户基本信息"""
    user_id = django_filters.NumberFilter(field_name='user_id', lookup_expr='exact')
    username = django_filters.CharFilter(field_name='username', lookup_expr='icontains')
    nickname = django_filters.CharFilter(field_name='nickname', lookup_expr='icontains')
    created_start_tm = django_filters.DateTimeFromToRangeFilter(field_name='创建开始时间')
    created_end_tm = django_filters.DateTimeFromToRangeFilter(field_name='创建结束时间')

    class Meta:
        model = Account
        fields = ('user_id', 'username', 'created_start_tm', 'created_end_tm')


class ProjectListFilter(django_filters.rest_framework.FilterSet):
    """项目基本信息"""
    project_id = django_filters.NumberFilter(field_name='project_id', lookup_expr='exact')
    project_name = django_filters.CharFilter(field_name='project_name', lookup_expr='icontains')
    editor = django_filters.CharFilter(field_name='editor', lookup_expr='exact')
    created_start_tm = django_filters.DateTimeFromToRangeFilter(field_name='创建开始时间')
    created_end_tm = django_filters.DateTimeFromToRangeFilter(field_name='创建结束时间')

    class Meta:
        model = ProjectList
        fields = ('project_id', 'project_name', 'created_start_tm', 'created_end_tm')
  • 用户登录views
from drf_yasg.utils import swagger_auto_schema
from myUser.models import Account
from rest_framework import mixins
from rest_framework.generics import GenericAPIView
from serializers import UserSerializer
from render_response import APIResponse
from rest_framework_jwt.settings import api_settings

# 签发
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
# 生成token
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER


# Create your views here.
class LoginView(mixins.UpdateModelMixin, GenericAPIView):
    authentication_classes = ()
    permission_classes = ()

    queryset = Account.objects.all()
    serializer_class = UserSerializer
    ordering = ['-created_tm']
    tag = ['用户']

    @swagger_auto_schema(tags=tag,
                         operation_id="login",
                         operation_summary='用户登录',
                         operation_description='明文密码',
                         responses={400010: "账号密码错误", 200: UserSerializer})
    def post(self, request, *args, **kwargs):
        """
        post: 用户登录

        400010:账号密码错误
        """
        username = str(request.data.get('username'))
        password = str(request.data.get('password'))

        try:
            Account.objects.get(username=username)
        except Account.DoesNotExist:
            return APIResponse(400010, '账号密码错误', success=False)

        try:
            Account.objects.get(password=password, username=username)
        except Account.DoesNotExist:
            return APIResponse(400010, '账号密码错误', success=False)

        user = Account.objects.filter(username=username, password=password).first()
        if user:
            # 登录成功,签发token,通过当前登录用户获取荷载(payload)
            payload = jwt_payload_handler(user)
            # 通过payload生成token串(三段:头,payload,签名)
            token = jwt_encode_handler(payload)
            response = {"user_id": user.user_id, "username": username, "token": token}
            return APIResponse(200, '登录成功', response)
  • 项目创建views
from drf_yasg.utils import swagger_auto_schema
from myProject.models import ProjectList
from rest_framework import mixins
from rest_framework.viewsets import GenericViewSet
from serializers import ProjectListSerializer
from render_response import APIResponse
from rest_framework.response import Response
from get_login_user_info import GetLoginUser


# Create your views here.
class AddProjectViews(mixins.CreateModelMixin, GenericViewSet):
    authentication_classes = []
    permission_classes = ()

    queryset = ProjectList.objects.all().order_by("-created_tm")
    serializer_class = ProjectListSerializer
    tag = ['项目']

    @swagger_auto_schema(tags=tag,
                         operation_summary='创建项目',
                         operation_id="project_create",
                         operation_description='项目名称必填',
                         responses={400014: "参数错误", 
                                    200: ProjectListSerializer,
                                    400013: "请检查输入字段是否正确(必填字段、未定义字段)", 
                                    500001: "项目名称长度需要1到20位", 
                                    500002: "项目已存在", 
                                    500003: "项目创建失败", })
    def create(self, request, *args, **kwargs):
        """新增项目"""
        try:
            project_name = request.data.get("project_name")
            project_desc = request.data.get("project_desc")
        except Exception:
            return APIResponse(400014, '参数错误', success=False)

        field_list = {}
        if project_name:
            field_list["project_name"] = project_name
        if project_desc:
            field_list["project_desc"] = project_desc

        # 获取当前登录用户信息
        user = GetLoginUser().get_login_user(request)
        if user["code"] == 200:
            field_list["editor"] = user["username"]
        else:
            return Response(user)

        # 检查账号密码是否有填写
        if "project_name" not in list(field_list.keys()):
            return APIResponse(400013, '请检查输入字段是否正确(必填字段、未定义字段)', success=False)
        else:
            if len(field_list["project_name"]) > 20 or len(field_list["project_name"]) < 1:
                return APIResponse(500001, '项目名称长度需要1到20位', success=False)

        try:
            ProjectList.objects.get(project_name=field_list["project_name"])
            return APIResponse(500002, '项目已存在', success=False)
        except ProjectList.DoesNotExist:
            try:
                ProjectList.objects.create(**field_list)
                return APIResponse(200, '项目创建成功')
            except Exception as e:
                print(e)
                return APIResponse(500003, '项目创建失败', success=False)
  • token的传参
token的传参
  • 返回我是这么定义的(来自网友)
"""
自定义返回处理
"""
from rest_framework.response import Response


class APIResponse(Response):
    def __init__(self, code=200, msg='请求成功', data=None, status=None, headers=None, success=True, **kwargs):
        dic = {'code': code, 'message': msg, 'success': success, 'data': None}
        if data:
            dic['data'] = data
        dic.update(kwargs)  # 可以灵活的添加,需要返回的键值对
        super().__init__(data=dic, status=status, headers=headers)
  • 获取当前登录用户信息我是这么写的(原创)
from myUser.models import Account
from jwt import ExpiredSignatureError
from render_response import APIResponse
from rest_framework_jwt.settings import api_settings

# 解析token
jwt_decode_handler = api_settings.JWT_DECODE_HANDLER


class GetLoginUser:
    @staticmethod
    def get_login_user(request):
        try:
            token = request.META.get('HTTP_AUTHORIZATION')
        except Exception:
            return APIResponse(400014, '参数错误', success=False)

        if token:
            try:
                # 解密token,提取user_id
                user_id = jwt_decode_handler(token)["user_id"]
                username = Account.objects.filter(user_id=user_id).values('username').first()
                return {"user_id": user_id, "username": username["username"], "code": 200}
            except ExpiredSignatureError:
                return APIResponse(4031, 'token已过期', success=False)
        else:
            return APIResponse(403, '无访问权限,请重新登录或稍后再试!', success=False)
  • 简易的swagger文档


    简易的swagger文档
  • 用户登录


    用户登录
  • 项目创建

项目创建
项目表
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容