DRF 商城項目 - 用戶( 登錄, 注冊,登出,個人中心 ) 邏輯梳理


用戶登錄

自定義用戶登錄字段處理

用戶的登錄時通過 手機號也可以進行登錄

需要重寫登錄驗證邏輯

from django.contrib.auth.backends import ModelBackend

class CustomBackend(ModelBackend):

    def authenticate(self, username=None, password=None, **kwargs):
        try:
            user = User.objects.get(Q(username=username) | Q(mobile=username))
            # 前端的用戶傳遞過來的密碼和數據庫的保存密碼是不一致的, 因此需要使用 check_password 的方式進行比對
            if user.check_password(password):
                return user
        except Exception as e:
            return None

登錄邏輯

通過 login 接口進入驗證, 調用默認重寫后的驗證邏輯進行處理

 url(r'^login/', obtain_jwt_token)

驗證成功后會返回 token

用戶注冊

用戶注冊基於 手機號注冊

驗證碼發送基於 雲片網 提供的技術支持

驗證碼邏輯

驗證碼API 接口

# 配置手機驗證碼發送 的 url
router.register(r'codes', SmsCodeViewset, base_name="codes")

驗證碼序列化組件

選取序列化方式的時候以為不是全部的字段都需要用上, 因此不需用到 ModelSerializer

需要對前端拿到的  mobile 字段進行相關的驗證

是否注冊, 是否合法, 以及頻率限制

# 手機驗證序列化組件
# 不使用 ModelSerializer, 並不需要所有的字段, 會有麻煩
class SmsSerializer(serializers.Serializer):
    mobile = serializers.CharField(max_length=11)

    # 驗證手機號碼
    # validate_ + 字段名 的格式命名
    def validate_mobile(self, mobile):

        # 手機是否注冊
        if User.objects.filter(mobile=mobile).count():
            raise serializers.ValidationError("用戶已經存在")

        # 驗證手機號碼是否合法
        if not re.match(REGEX_MOBILE, mobile):
            raise serializers.ValidationError("手機號碼非法")

        # 驗證碼發送頻率
        # 當前時間減去一分鍾( 倒退一分鍾 ), 然后發送時間要大於這個時間, 表示還在一分鍾內
        one_mintes_ago = datetime.now() - timedelta(hours=0, minutes=1, seconds=0)
        if VerifyCode.objects.filter(add_time__gt=one_mintes_ago, mobile=mobile).count():
            raise serializers.ValidationError("距離上一次發送未超過60s")
        return mobile

驗證碼視圖

視圖主要處理 驗證碼生成發送相關邏輯

具體的雲片網接口對接處理詳情官網查閱

# 發送短信驗證碼
class SmsCodeViewset(CreateModelMixin, viewsets.GenericViewSet):
    serializer_class = SmsSerializer

    # 生成四位數字的驗證碼
    def generate_code(self):

        seeds = "1234567890"
        random_str = []
        for i in range(4):
            random_str.append(choice(seeds))
        return "".join(random_str)

    # 重寫 create 方法
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        # 驗證后即可取出數據
        mobile = serializer.validated_data["mobile"]
        yun_pian = YunPian(APIKEY)
        code = self.generate_code()
        sms_status = yun_pian.send_sms(code=code, mobile=mobile)

        if sms_status["code"] != 0:
            return Response({
                "mobile": sms_status["msg"]
            }, status=status.HTTP_400_BAD_REQUEST)
        else:
            # 確認無誤后需要保存數據庫中
            code_record = VerifyCode(code=code, mobile=mobile)
            code_record.save()
            return Response({
                "mobile": mobile
            }, status=status.HTTP_201_CREATED)

雲片驗證碼工具文件

# _*_ coding:utf-8 _*_
from YtShop.settings import APIKEY

__author__ = "yangtuo"
__date__ = "2019/4/15 20:25"
import requests
import json


# 雲片網短信發送功能類
class YunPian(object):

    def __init__(self, api_key):
        self.api_key = api_key
        self.single_send_url = "https://sms.yunpian.com/v2/sms/single_send.json"

    def send_sms(self, code, mobile):
        parmas = {
            "apikey": self.api_key,
            "mobile": mobile,
            "text": "您的驗證碼是{code}。如非本人操作,請忽略本短信".format(code=code)
        }

        response = requests.post(self.single_send_url, data=parmas)
        re_dict = json.loads(response.text)
        return re_dict


if __name__ == "__main__":
    yun_pian = YunPian(APIKEY)
    yun_pian.send_sms("2019", "")  # 參數為 code 以及 mobile

配置文件

需要用到兩個配置添加

# 手機號碼的驗證正則式
REGEX_MOBILE = "^1[358]\d{9}$|^147\d{8}$|^176\d{8}$"

# 雲片網的 APIKEY 設置
APIKEY = "2480f562xxxxxxxxxxxxxcb7673f8"

注冊邏輯

注冊 API 接口

# 配置用戶注冊的 url
router.register(r'users', UserViewset, base_name="users")

注冊序列化組件

用戶注冊需要的字段較多

每個字段都有些獨有的特殊裁定

用戶名  要進行重復判斷

驗證碼  要進行有效期, 正確性判斷

密碼  設置 輸入框為密碼格式

在最后回傳的時候 code 是不需要的, 因此可以刪除掉

# 用戶注冊
class UserRegSerializer(serializers.ModelSerializer):
    """
    max_length      最大長度
    min_length      最小長度
    label           顯示名字
    help_text       幫助提示信息
    error_messages  錯誤類型映射提示
        blank         空字段提示
        required      必填字段提示
        max_length    超長度提示
        min_length    過短提示
    write_only      只讀, 序列化的時候忽略字段, 不再返回給前端頁面, 用於去除關鍵信息(密碼等)或者某些不必要字段(驗證碼)
    style           更改輸入標簽顯示類型
    validators      可以指明一些默認的約束類
        UniqueValidator             約束唯一
        UniqueTogetherValidator     聯合約束唯一
        UniqueForMonthValidator
        UniqueForDateValidator
        UniqueForYearValidator
        ....
    """
    code = serializers.CharField(required=True, write_only=True, max_length=4, min_length=4, label="驗證碼",
                                 error_messages={
                                     "blank": "請輸入驗證碼",
                                     "required": "請輸入驗證碼",
                                     "max_length": "驗證碼格式錯誤",
                                     "min_length": "驗證碼格式錯誤"
                                 },
                                 help_text="驗證碼")

    # validators 可以指明一些默認的約束類, 此處的 UniqueValidator 表示唯一約束限制不能重名
    username = serializers.CharField(label="用戶名", help_text="用戶名", required=True, allow_blank=False,
                                     validators=[UniqueValidator(queryset=User.objects.all(), message="用戶已經存在")])

    # style 可以設置為密文狀態
    password = serializers.CharField(
        style={'input_type': 'password'}, help_text="密碼", label="密碼", write_only=True,
    )

    # 用戶表中的 password 是需要加密后再保存的, 次數需要重寫一次 create 方法
    # 當然也可以不這樣做, 這里的操作利用 django 的信號來處理, 詳情見 signals.py
    # def create(self, validated_data):
    #     user = super(UserRegSerializer, self).create(validated_data=validated_data)
    #     user.set_password(validated_data["password"])
    #     user.save()
    #     return user

    # 對驗證碼的驗證處理
    # validate_ + 字段對個別字段進行單一處理
    def validate_code(self, code):

        # 如果使用 get 方式需要處理兩個異常, 分別是查找到多個信息的情況以及查詢到0信息的情況的異常
        # 但是使用 filter 方式查到多個就以列表方式返回, 如果查詢不到數據就會返回空值, 各方面都很方便
        # try:
        #     verify_records = VerifyCode.objects.get(mobile=self.initial_data["username"], code=code)
        # except VerifyCode.DoesNotExist as e:
        #     pass
        # except VerifyCode.MultipleObjectsReturned as e:
        #     pass

        # 前端傳過來的所有的數據都在, initial_data 字典里面, 如果是驗證通過的數據則保存在 validated_data 字典中
        verify_records = VerifyCode.objects.filter(mobile=self.initial_data["username"]).order_by("-add_time")
        if verify_records:
            last_record = verify_records[0]  # 時間倒敘排序后的的第一條就是最新的一條
            # 當前時間回退5分鍾
            five_mintes_ago = datetime.now() - timedelta(hours=0, minutes=5, seconds=0)
            # 最后一條短信記錄的發出時間小於5分鍾前, 表示是5分鍾前發送的, 表示過期
            if five_mintes_ago > last_record.add_time:
                raise serializers.ValidationError("驗證碼過期")
            # 根據記錄的 驗證碼 比對判斷
            if last_record.code != code:
                raise serializers.ValidationError("驗證碼錯誤")
            # return code  # 沒必要保存驗證碼記錄, 僅僅是用作驗證
        else:
            raise serializers.ValidationError("驗證碼錯誤")

    # 對所有的字段進行限制
    def validate(self, attrs):
        attrs["mobile"] = attrs["username"]  # 重命名一下
        del attrs["code"]  # 刪除無用字段
        return attrs

    class Meta:
        model = User
        fields = ("username", "code", "mobile", "password")

注冊視圖

class UserViewset(CreateModelMixin, mixins.UpdateModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
    serializer_class = UserRegSerializer
    queryset = User.objects.all()

    # 重寫 create 函數來完成注冊后自動登錄功能
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        user = self.perform_create(serializer)
        
        re_dict = serializer.data
        payload = jwt_payload_handler(user)
        # token 的添加只能用此方法, 此方法通過源碼閱讀查找到位置為
        re_dict["token"] = jwt_encode_handler(payload)
        # 自定義一個字段加入進去
        re_dict["name"] = user.name if user.name else user.username

        headers = self.get_success_headers(serializer.data)
        return Response(re_dict, status=status.HTTP_201_CREATED, headers=headers)

    def get_object(self):
        return self.request.user

    def perform_create(self, serializer):
        return serializer.save()

信號量處理工具文件

注冊后的信息回傳給數據庫保存的時候 密碼是按照是未加密狀態保存

此處需要進行加密后才可以, 因此這里可以用信號量來處理, post_save 觸發

在此觸發流程中完成加密后保存數據庫

# _*_ coding:utf-8 _*_
__author__ = "yangtuo"
__date__ = "2019/4/15 20:25"

from django.db.models.signals import post_save
from django.dispatch import receiver
from rest_framework.authtoken.models import Token
from django.contrib.auth import get_user_model

User = get_user_model()


@receiver(post_save, sender=User)  # post_save 信號類型, sender 能觸發信號的模型
def create_user(sender, instance=None, created=False, **kwargs):    # created 是否新建( update 就不會被識別 )
    # instance 表示保存對象, 在這里是被保存的 user 對象
    if created:
        password = instance.password
        instance.set_password(password)
        instance.save()
        # Token.objects.create(user=instance)
        # user 對象的保存一般是要伴隨着 token 的, 這里已經使用 JWT 方式了, 因此就不需要這種 token 了.

注冊后自動登錄邏輯

目標預期

用戶注冊后自動跳轉到主頁

同時要實現注冊用戶已登錄狀態

需求分析

用戶注冊相關的操作本質是從前端拿到數據傳送到后端通過 相關的 view 進行操作

本質是 底層的 create 方法, 默認的方法只能實現用戶創建無法實現其他附加

( DRF 的視圖 功能嵌套 層次詳情點擊 這里查看  )

因此我們需要重寫 create 方法 

定位重寫 create 方法

可見只有序列化類的更新和推送, 無其他功能

默認的 create 方法

如果想實現自動登錄, 首先本質就是加入用戶登錄的狀態, 即 token 的生成和保存

本次項目使用的是 JWT 作為 token 方案, 因此 需要考究在 JWT 的源碼中 token 如何生成

定位 token 生成源碼查閱

JWT 的源碼入口 ( URL 對接視圖 )

往上找到視圖類

這里是做了一層很簡單的封裝, 以及可以看到熟悉的 as_view()

不過我們目前不關心這個, 這里同樣基於 DRF 視圖中類似

視圖類中找到序列化處理

 這個 serializer_class 就是對應着序列化類的處理

 

序列化處理中對 token 的處理

其實我們已經知道了JWT 的方式是不會基於數據庫的, 因此他們的序列化類中的是沒有任何的字段

通過各種方法來實現字段的計算和生成

以下是全部的 相關邏輯

class JSONWebTokenSerializer(Serializer):
    """
    Serializer class used to validate a username and password.

    'username' is identified by the custom UserModel.USERNAME_FIELD.

    Returns a JSON Web Token that can be used to authenticate later calls.
    """
    def __init__(self, *args, **kwargs):
        """
        Dynamically add the USERNAME_FIELD to self.fields.
        """
        super(JSONWebTokenSerializer, self).__init__(*args, **kwargs)

        self.fields[self.username_field] = serializers.CharField()
        self.fields['password'] = PasswordField(write_only=True)

    @property
    def username_field(self):
        return get_username_field()

    def validate(self, attrs):
        credentials = {
            self.username_field: attrs.get(self.username_field),
            'password': attrs.get('password')
        }

        if all(credentials.values()):
            user = authenticate(**credentials)

            if user:
                if not user.is_active:
                    msg = _('User account is disabled.')
                    raise serializers.ValidationError(msg)

                payload = jwt_payload_handler(user)

                return {
                    'token': jwt_encode_handler(payload),
                    'user': user
                }
            else:
                msg = _('Unable to log in with provided credentials.')
                raise serializers.ValidationError(msg)
        else:
            msg = _('Must include "{username_field}" and "password".')
            msg = msg.format(username_field=self.username_field)
            raise serializers.ValidationError(msg)

定位到 token 的生成代碼

可見 需要使用到 jwt_payload_handler 方法以及 jwt_encode_handler 方法

因此生成 token 就是在這里了, 為了生成 token 我們需要用到這兩個方法, 使用方法就完全模仿源碼即可

完成 create 重寫

from rest_framework_jwt.serializers import jwt_payload_handler, jwt_encode_handler

class UserViewset(CreateModelMixin, mixins.UpdateModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
    serializer_class = UserRegSerializer
    queryset = User.objects.all()

    # 重寫 create 函數來完成注冊后自動登錄功能
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        user = self.perform_create(serializer)
   
       # 此處為自定義的 token 的生成 
        re_dict = serializer.data
        payload = jwt_payload_handler(user)
        re_dict["token"] = jwt_encode_handler(payload)
        # 順便把 用戶名一並傳過去
        re_dict["name"] = user.name if user.name else user.username

        headers = self.get_success_headers(serializer.data)
        return Response(re_dict, status=status.HTTP_201_CREATED, headers=headers)

    def get_object(self):
        return self.request.user

    def perform_create(self, serializer):
        return serializer.save()

用戶退出

不需要再寫一個 logout 接口 

JWT 不需要服務器這邊進行相關的操作

只需要前端進行一個 cookie 的清空然后跳轉即可

跳轉到 登錄頁面或者主頁皆可

    loginOut(){
        cookie.delCookie('token');
        cookie.delCookie('name');
        //重新觸發store
        //更新store數據
        this.$store.dispatch('setInfo');
        //跳轉到登錄
        this.$router.push({name: 'login'})
      },

用戶個人中心 

retrieve 方式添加

用戶中心的數據來源是對單一用戶的詳細數據請求, 因此需要在原有基礎上加上對  retrieve 的處理

 mixins.RetrieveModelMixin

用戶 id 傳遞

同時因為對單一用戶的請求需要指明用戶id, 有兩種方式可以傳遞

第一種 直接在數據里面提供當前用戶 id

第二種 重寫 get_object 獲取當前用戶

# 因為要涉及到 個人中心的操作需要傳遞過去 用戶的 id, 重寫 get_object 來實現
    def get_object(self):
        return self.request.user

權限分離

用戶中心必須指定當前用戶只能訪問自己, 因此需要對是否登錄進行驗證

但是當前視圖的其他類型請求比如 create 的注冊則不需要進行驗證, 因此  permission_classes 無法滿足需求

源碼剖析

在繼承了  ViewSetMixin 之后內部的 initialize_request 方面里面的 提供了 .action 在 request 中可以對請求類型進行分離

同時 APIView 內部的  get_permissions  方法負責提取認證類型, 因此重寫此方法即可完成

 

此為 源碼, 可見是直接使用一個列表表達式來獲取當前視圖的 permission_classes 里面的所有認證方式

實現重寫

基於我們自己的需求進行重寫, 利用 action 進行分流

注意其他未設置的最后一定要返回空

    # permission_classes = (permissions.IsAuthenticated, )  # 因為根據類型的不同權限的認證也不同, 不能再統一設置了
    def get_permissions(self):
        if self.action == "retrieve":
            return [permissions.IsAuthenticated()]
        elif self.action == "create":
            return []
        return []

序列化組件分離

創建組件

之前設置的序列化組件是為了注冊用的, 只采集了注冊相關的字段, 無法滿足用戶中心的其他字段處理

因此需要重新設置一個用戶詳情的 序列化組件

# 用戶詳情信息序列化類
class UserDetailSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ("name", "gender", "birthday", "email", "mobile")

源碼剖析

同樣是基於對 action 的方法進行分流, 對於 action 的位置在 權限分流的部分有圖,

在   GenericAPIView 中存在 get_serializer_class  方法, 用於獲取當前視圖中的 序列化組件

實現重寫

基於 action 進行分流, 然后進行對 get_serializer_class 進行重寫

實現方式類似於 權限的分流

   def get_serializer_class(self):
        if self.action == "retrieve":
            return UserDetailSerializer
        elif self.action == "create":
            return UserRegSerializer
        return UserDetailSerializer

完整代碼

用戶視圖代碼

# 用戶視圖
class UserViewset(mixins.CreateModelMixin, mixins.UpdateModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
    serializer_class = UserRegSerializer
    queryset = User.objects.all()

    authentication_classes = (JSONWebTokenAuthentication, authentication.SessionAuthentication)

    # 用戶中心的個人詳情數據不能再基於統一設置的 UserRegSerializer 了
    # 用戶注冊和 用戶詳情分為了兩個序列化組件
    # self.action 必須要繼承了 ViewSetMixin 才有此功能
    # get_serializer_class 的源碼位置在 GenericAPIView 中
    def get_serializer_class(self):
        if self.action == "retrieve":
            return UserDetailSerializer
        elif self.action == "create":
            return UserRegSerializer
        return UserDetailSerializer

    # permission_classes = (permissions.IsAuthenticated, )  # 因為根據類型的不同權限的認證也不同, 不能再統一設置了
    # get_permissions 的源碼在 APIview 中
    def get_permissions(self):
        if self.action == "retrieve":
            return [permissions.IsAuthenticated()]
        elif self.action == "create":
            return []
        return []

    # 重寫 create 函數來完成注冊后自動登錄功能
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        user = self.perform_create(serializer)

        """
        此處重寫的源碼分析以及 相關的邏輯
        詳情點擊此博客 
        https://www.cnblogs.com/shijieli/p/10726194.html
        """
        re_dict = serializer.data
        payload = jwt_payload_handler(user)
        # token 的添加只能用此方法, 此方法通過源碼閱讀查找到位置為
        re_dict["token"] = jwt_encode_handler(payload)
        # 自定義一個字段加入進去
        re_dict["name"] = user.name if user.name else user.username

        headers = self.get_success_headers(serializer.data)
        return Response(re_dict, status=status.HTTP_201_CREATED, headers=headers)

    # 因為要涉及到 個人中心的操作需要傳遞過去 用戶的 id, 重寫 get_object 來實現
    def get_object(self):
        return self.request.user

    def perform_create(self, serializer):
        return serializer.save()

用戶相關序列化組件

# _*_ coding:utf-8 _*_
__author__ = "yangtuo"
__date__ = "2019/4/15 20:25"

import re
from rest_framework import serializers
from django.contrib.auth import get_user_model
from datetime import datetime
from datetime import timedelta
from rest_framework.validators import UniqueValidator

from .models import VerifyCode
from YtShop.settings import REGEX_MOBILE

User = get_user_model()


# 手機驗證序列化組件
# 不使用 ModelSerializer, 並不需要所有的字段, 會有麻煩
class SmsSerializer(serializers.Serializer):
    mobile = serializers.CharField(max_length=11)

    # 驗證手機號碼
    # validate_ + 字段名 的格式命名
    def validate_mobile(self, mobile):

        # 手機是否注冊
        if User.objects.filter(mobile=mobile).count():
            raise serializers.ValidationError("用戶已經存在")

        # 驗證手機號碼是否合法
        if not re.match(REGEX_MOBILE, mobile):
            raise serializers.ValidationError("手機號碼非法")

        # 驗證碼發送頻率
        # 當前時間減去一分鍾( 倒退一分鍾 ), 然后發送時間要大於這個時間, 表示還在一分鍾內
        one_mintes_ago = datetime.now() - timedelta(hours=0, minutes=1, seconds=0)
        if VerifyCode.objects.filter(add_time__gt=one_mintes_ago, mobile=mobile).count():
            raise serializers.ValidationError("距離上一次發送未超過60s")
        return mobile


# 用戶詳情信息序列化類
class UserDetailSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ("name", "gender", "birthday", "email", "mobile")


# 用戶注冊
class UserRegSerializer(serializers.ModelSerializer):
    """
    max_length      最大長度
    min_length      最小長度
    label           顯示名字
    help_text       幫助提示信息
    error_messages  錯誤類型映射提示
        blank         空字段提示
        required      必填字段提示
        max_length    超長度提示
        min_length    過短提示
    write_only      只讀, 序列化的時候忽略字段, 不再返回給前端頁面, 用於去除關鍵信息(密碼等)或者某些不必要字段(驗證碼)
    style           更改輸入標簽顯示類型
    validators      可以指明一些默認的約束類
        UniqueValidator             約束唯一
        UniqueTogetherValidator     聯合約束唯一
        UniqueForMonthValidator
        UniqueForDateValidator
        UniqueForYearValidator
        ....
    """
    code = serializers.CharField(required=True, write_only=True, max_length=4, min_length=4, label="驗證碼",
                                 error_messages={
                                     "blank": "請輸入驗證碼",
                                     "required": "請輸入驗證碼",
                                     "max_length": "驗證碼格式錯誤",
                                     "min_length": "驗證碼格式錯誤"
                                 },
                                 help_text="驗證碼")

    # validators 可以指明一些默認的約束類, 此處的 UniqueValidator 表示唯一約束限制不能重名
    username = serializers.CharField(label="用戶名", help_text="用戶名", required=True, allow_blank=False,
                                     validators=[UniqueValidator(queryset=User.objects.all(), message="用戶已經存在")])

    # style 可以設置為密文狀態
    password = serializers.CharField(
        style={'input_type': 'password'}, help_text="密碼", label="密碼", write_only=True,
    )

    # 用戶表中的 password 是需要加密后再保存的, 次數需要重寫一次 create 方法
    # 當然也可以不這樣做, 這里的操作利用 django 的信號來處理, 詳情見 signals.py
    # def create(self, validated_data):
    #     user = super(UserRegSerializer, self).create(validated_data=validated_data)
    #     user.set_password(validated_data["password"])
    #     user.save()
    #     return user

    # 對驗證碼的驗證處理
    # validate_ + 字段對個別字段進行單一處理
    def validate_code(self, code):

        # 如果使用 get 方式需要處理兩個異常, 分別是查找到多個信息的情況以及查詢到0信息的情況的異常
        # 但是使用 filter 方式查到多個就以列表方式返回, 如果查詢不到數據就會返回空值, 各方面都很方便
        # try:
        #     verify_records = VerifyCode.objects.get(mobile=self.initial_data["username"], code=code)
        # except VerifyCode.DoesNotExist as e:
        #     pass
        # except VerifyCode.MultipleObjectsReturned as e:
        #     pass

        # 前端傳過來的所有的數據都在, initial_data 字典里面 ,
        verify_records = VerifyCode.objects.filter(mobile=self.initial_data["username"]).order_by("-add_time")
        if verify_records:
            last_record = verify_records[0]  # 時間倒敘排序后的的第一條就是最新的一條
            # 當前時間回退5分鍾
            five_mintes_ago = datetime.now() - timedelta(hours=0, minutes=5, seconds=0)
            # 最后一條短信記錄的發出時間小於5分鍾前, 表示是5分鍾前發送的, 表示過期
            if five_mintes_ago > last_record.add_time:
                raise serializers.ValidationError("驗證碼過期")
            # 根據記錄的 驗證碼 比對判斷
            if last_record.code != code:
                raise serializers.ValidationError("驗證碼錯誤")
            # return code  # 沒必要保存驗證碼記錄, 僅僅是用作驗證
        else:
            raise serializers.ValidationError("驗證碼錯誤")

    # 對所有的字段進行限制
    def validate(self, attrs):
        attrs["mobile"] = attrs["username"]  # 重命名一下
        del attrs["code"]  # 刪除無用字段
        return attrs

    class Meta:
        model = User
        fields = ("username", "code", "mobile", "password")

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM