django-rest-framework(實戰(zhàn)篇)——用戶手機(jī)注冊功能實現(xiàn)

云片網(wǎng)發(fā)送驗證碼

apps/utils/yunpian.py

# -*- coding:utf-8 -*-
__author__ = 'cao.yh'
__date__ = '2018/4/13 下午2:17'
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):
        params = {
            'apikey': self.api_key,
            'mobile': mobile,
            'text': '【慕學(xué)生鮮】您的驗證碼是{code}。如非本人操作,請忽略本短信'.format(code=code),
        }

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

注意text內(nèi)容必須要與后臺已申請過簽名并審核通過的模板保持一致

編寫發(fā)送短信驗證碼接口

用戶傳過來的手機(jī)號碼需要兩次驗證:

  • 手機(jī)號是否合法
  • 手機(jī)號是否已經(jīng)被注冊

users/serializers.py

class SmsSerializer(serializers.Serializer):
    mobile = serializers.CharField(required=True, max_length=11)

    def validate_mobile(self, mobile):
        """
        驗證手機(jī)號碼
        :param mobile:
        :return:
        """
        # 手機(jī)是否注冊
        if VerifyCode.objects.filter(mobile=mobile).count():
            raise serializers.ValidationError('手機(jī)號碼已經(jīng)注冊')

        # 驗證手機(jī)號碼合法
        if not re.match(REGEX_MOBILE, mobile):
            raise serializers.ValidationError('手機(jī)號碼格式錯誤')

        # 驗證碼發(fā)送頻率
        one_minute_age = datetime.now() - timedelta(hours=0, minutes=1, seconds=0)
        if VerifyCode.objects.filter(add_time__gt=one_minute_age, mobile=mobile).count():
            raise serializers.ValidationError('請一分鐘后再次發(fā)送')

        return mobile

繼承serializers.Serializer的原因:VerifyCode Model中,code中是必填項,但獲取驗證碼的時候前端只會傳一個mobile字段,所以會導(dǎo)致驗證失敗。

users/views.py(POST------>需要重寫create方法):

class SmsCodeViewSet(viewsets.GenericViewSet, mixins.CreateModelMixin):
    """
    發(fā)送短信驗證碼
    """
    serializer_class = SmsSerializer

    def generate_code(self):
        """
        生成四位數(shù)驗證碼
        :return:
        """
        seeds = '1234567890'
        random_str = []
        for i in range(4):
            random_str.append(choice(seeds))

        return "".join(random_str)

    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        # 自定義的create()的內(nèi)容
        # 從validated_data中獲取mobile
        mobile = serializer.validated_data['mobile']
        # 隨機(jī)生成code
        code = self.generate_code()

        # 發(fā)送驗證碼短信
        yun_pian = YunPian(APIKEY)
        sms_status = yun_pian.send_sms(code=code, mobile=mobile)

        if sms_status['code']!= 0:
            return Response({
                'mobile': sms_status['code']
            }, 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)

將返回的json在yunpian中l(wèi)oads成dict

然后取出dict中的code和msg進(jìn)行判斷與返回。我們不需要向前端返回status。而是遵循restful api的規(guī)范。http狀態(tài)碼即可區(qū)分成功或失敗。消息并不代表。

發(fā)送成功之后再保存驗證碼

編寫用戶注冊接口

注冊頁面需要我們輸入手機(jī)號碼 驗證碼 和密碼。

users/serializers.py(繼承modelSerializer,因為字段都是必填項,都有的,雖然相比較用戶model多了一個code字段)

class UserRegisterSerializer(serializers.ModelSerializer):
    # error_message:自定義錯誤消息提示的格式
    code = serializers.CharField(required=True, allow_blank=False, min_length=4, max_length=4, help_text='驗證碼',
                                 error_messages={
                                     'blank': '請輸入驗證碼',
                                     'required': '請輸入驗證碼',
                                     'min_length': '驗證碼格式錯誤',
                                     'max_length': '驗證碼格式錯誤',
                                 })
    # 利用drf中的validators驗證username是否唯一
    username = serializers.CharField(required=True, allow_blank=False,
                                     validators=[UniqueValidator(queryset=User.objects.all(), message='用戶已經(jīng)存在')])

    # 對code字段單獨驗證(validate_+字段名)
    def validate_code(self, code):
        verify_records = VerifyCode.objects.filter(mobile=self.initial_data['username']).order_by('-add_time')
        if verify_records:
            last_record = verify_records[0]
            # 判斷驗證碼是否過期
            five_minutes_ago = datetime.now() - timedelta(hours=0, minutes=5, seconds=0)  # 獲取5分鐘之前的時間
            if last_record.add_time > five_minutes_ago:
                raise serializers.ValidationError('驗證碼過期')
            # 判斷驗證碼是否正確
            if last_record.code != code:
                raise serializers.ValidationError('驗證碼錯誤')
            # 不用將code返回到數(shù)據(jù)庫中,只是做驗證
            # return code
        else:
            raise serializers.ValidationError('驗證碼錯誤')

    # attrs:每個字段validate之后總的dict
    def validate(self, attrs):
        attrs['mobile'] = attrs['username']
        # 從attrs中刪除code字段
        del attrs['code']
        return attrs

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

編寫視圖函數(shù):users/views.py

class UserViewset(CreateModelMixin, viewsets.GenericViewSet):
    """
    用戶
    """
    serializer_class = UserRegisterSerializer

配置路由:

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

drf驗證默認(rèn)的返回格式:

HTTP 400 Bad Request
Allow: POST, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "username": [
        "用戶已經(jīng)存在"
    ],
    "code": [
        "驗證碼錯誤"
    ]
}
  • 單個字段出錯: 字段 + 數(shù)組
  • 聯(lián)合字段出錯: non_fields_error

django信號量實現(xiàn)密碼密文存儲

題外話:重載Serializer的create方法可以實現(xiàn)相同的功能:

def create(self, validated_data):
        user = super(UserRegSerializer, self).create(validated_data=validated_data)
        user.set_password(validated_data["password"])
        user.save()
        return user

新建文件users/signals.py:

# encoding: utf-8

from django.conf import settings
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()


# 參數(shù)一接收哪種信號,參數(shù)二是接收哪個model的信號
@receiver(post_save, sender=User)
def create_user(sender, instance=None, created=False, **kwargs):
    # 是否新建,因為update的時候也會進(jìn)行post_save
    if created:
        password = instance.password
        instance.set_password(password)
        instance.save()

做完剛才這些操作,還要重載一個配置 users/app.py:
這是AppConfig中我們可以在子類中自定義的函數(shù),它將會在django啟動時被運行。

    def ready(self):
        import users.signals
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容