【Django開發】0到1開發美多shop專案:Celery簡訊和使用者註冊。全md文件筆記(附程式碼,已分享)

發表於2024-02-28

本系列文章md筆記(已分享)主要討論django商城專案開發相關知識。本專案利用Django框架開發一套前後端不分離的商城專案(4.0版本)含程式碼和文件。功能包括前後端不分離,方便SEO。採用Django + Jinja2模板引擎 + Vue.js實現前後端邏輯,Nginx伺服器(反向代理)Nginx伺服器(靜態首頁、商品詳情頁、uwsgi伺服器(美多商場業務場景),後端服務:MySQL、Redis、Celery、RabbitMQ、Docker、FastDFS、Elasticsearch、Crontab,外部介面:容聯雲、QQ互聯、支付寶。

全套筆記和程式碼自取移步gitee倉庫: gitee倉庫獲取完整文件和程式碼

感興趣的小夥伴可以自取哦,歡迎大家點贊轉發~


共 11 章,63 子模組

使用者部分

使用Celery完成傳送簡訊

meiduo/meiduo_mall下建立celery_tasks用於儲存celery非同步任務。

在celery_tasks目錄下建立config.py檔案,用於儲存celery的配置資訊

broker_url = "redis://127.0.0.1/14"

在celery_tasks目錄下建立main.py檔案,用於作為celery的啟動檔案

from celery import Celery

  
  
# 為celery使用django配置檔案進行設定
  
  
import os
if not os.getenv('DJANGO_SETTINGS_MODULE'):
    os.environ['DJANGO_SETTINGS_MODULE'] = 'meiduo_mall.settings.dev'

  
  
# 建立celery應用
  
  
app = Celery('meiduo')

  
  
# 匯入celery配置
  
  
app.config_from_object('celery_tasks.config')

  
  
# 自動註冊celery任務
  
  
app.autodiscover_tasks(['celery_tasks.sms'])

在celery_tasks目錄下建立sms目錄,用於放置傳送簡訊的非同步任務相關程式碼。

將提供的傳送簡訊的雲通訊SDK放到celery_tasks/sms/目錄下。

在celery_tasks/sms/目錄下建立tasks.py檔案,用於儲存傳送簡訊的非同步任務

import logging

from celery_tasks.main import app
from .yuntongxun.sms import CCP

logger = logging.getLogger("django")

  
  
# 驗證碼簡訊模板
  
  
SMS_CODE_TEMP_ID = 1

@app.task(name='send_sms_code')
def send_sms_code(mobile, code, expires):
    """
    傳送簡訊驗證碼
    :param mobile: 手機號
    :param code: 驗證碼
    :param expires: 有效期
    :return: None
    """

    try:
        ccp = CCP()
        result = ccp.send_template_sms(mobile, [code, expires], SMS_CODE_TEMP_ID)
    except Exception as e:
        logger.error("傳送驗證碼簡訊[異常][ mobile: %s, message: %s ]" % (mobile, e))
    else:
        if result == 0:
            logger.info("傳送驗證碼簡訊[正常][ mobile: %s ]" % mobile)
        else:
            logger.warning("傳送驗證碼簡訊[失敗][ mobile: %s ]" % mobile)

在verifications/views.py中改寫SMSCodeView檢視,使用celery非同步任務傳送簡訊

from celery_tasks.sms import tasks as sms_tasks

class SMSCodeView(GenericAPIView):
    ...
        # 傳送簡訊驗證碼
        sms_code_expires = str(constants.SMS_CODE_REDIS_EXPIRES // 60)
        sms_tasks.send_sms_code.delay(mobile, sms_code, sms_code_expires)

        return Response({"message": "OK"})

判斷帳號是否存在

1. 判斷使用者名稱是否存在

後端介面設計:

請求方式: GET usernames/(?P<username>\w{5,20})/count/

請求引數: 路徑引數

引數型別是否必傳說明
usernamestr使用者名稱

返回資料: JSON

{
    "username": "itcast",
    "count": "1"
}
返回值型別是否必須說明
usernamestr使用者名稱
countint數量
後端實現

在users/views.py中定義檢視

  
  
# url(r'^usernames/(?P<username>\w{5,20})/count/$', views.UsernameCountView.as_view()), 
  
  
class UsernameCountView(APIView):
    """
    使用者名稱數量
    """
    def get(self, request, username):
        """
        獲取指定使用者名稱數量
        """
        count = User.objects.filter(username=username).count()

        data = {
            'username': username,
            'count': count
        }

        return Response(data)
前端實現

在js/register.js中修改

// 檢查使用者名稱
    check_username: function (){
            var len = this.username.length;
            if(len<5||len>20) {
                this.error_name_message = '請輸入5-20個字元的使用者名稱';
                this.error_name = true;
            } else {
                this.error_name = false;
            }
            // 檢查重名
            if (this.error_name == false) {
                axios.get(this.host + '/usernames/' + this.username + '/count/', {
                        responseType: 'json'
                    })
                    .then(response => {
                        if (response.data.count > 0) {
                            this.error_name_message = '使用者名稱已存在';
                            this.error_name = true;
                        } else {
                            this.error_name = false;
                        }
                    })
                    .catch(error => {
                        console.log(error.response.data);
                    })
            }
        },

2. 判斷手機號是否存在:

後端介面設計:

請求方式: GET mobiles/(?P<mobile>1[3-9]\d{9})/count

請求引數: 路徑引數

引數型別是否必須說明
mobilestr手機號

返回資料: JSON

{
    "mobile": "18512345678",
    "count": 0
}
返回值型別是否必須說明
mobilestr手機號
countint數量
後端實現

在users/views.py中定義檢視

  
  
# url(r'^mobiles/(?P<mobile>1[3-9]\d{9})/count/$', views.MobileCountView.as_view()),
  
  
class MobileCountView(APIView):
    """
    手機號數量
    """
    def get(self, request, mobile):
        """
        獲取指定手機號數量
        """
        count = User.objects.filter(mobile=mobile).count()

        data = {
            'mobile': mobile,
            'count': count
        }

        return Response(data)
前端實現

在js/register.js中修改

// 檢查手機號
    check_phone: function (){
            var re = /^1[345789]\d{9}$/;
            if(re.test(this.mobile)) {
                this.error_phone = false;
            } else {
                this.error_phone_message = '您輸入的手機號格式不正確';
                this.error_phone = true;
            }
            if (this.error_phone == false) {
                axios.get(this.host + '/mobiles/'+ this.mobile + '/count/', {
                        responseType: 'json'
                    })
                    .then(response => {
                        if (response.data.count > 0) {
                            this.error_phone_message = '手機號已存在';
                            this.error_phone = true;
                        } else {
                            this.error_phone = false;
                        }
                    })
                    .catch(error => {
                        console.log(error.response.data);
                    })
            }
        },

註冊

1. 後端介面設計:

請求方式: POST /users/

請求引數: JSON 或 表單

引數名型別是否必須說明
usernamestr使用者名稱
passwordstr密碼
password2str確認密碼
sms_codestr簡訊驗證碼
mobilestr手機號
allowstr是否同意使用者協議

返回資料: JSON

{
    "id": 9,
    "username": "python8",
    "mobile": "18512345678",
}
返回值型別是否必須說明
idint使用者id
usernamestr使用者名稱
mobilestr手機號
檢視原型
  
  
# url(r'^users/$', views.UserView.as_view()),
  
  
class UserView(CreateAPIView):
    """
    使用者註冊
    傳入引數:
        username, password, password2, sms_code, mobile, allow
    """
    pass

2. 後端實現

在users/serializers.py中建立序列化器物件

class CreateUserSerializer(serializers.ModelSerializer):
    """
    建立使用者序列化器
    """
    password2 = serializers.CharField(label='確認密碼', write_only=True)
    sms_code = serializers.CharField(label='簡訊驗證碼', write_only=True)
    allow = serializers.CharField(label='同意協議', write_only=True)

    class Meta:
        model = User
        fields = ('id', 'username', 'password', 'password2', 'sms_code', 'mobile', 'allow')
        extra_kwargs = {
            'username': {
                'min_length': 5,
                'max_length': 20,
                'error_messages': {
                    'min_length': '僅允許5-20個字元的使用者名稱',
                    'max_length': '僅允許5-20個字元的使用者名稱',
                }
            },
            'password': {
                'write_only': True,
                'min_length': 8,
                'max_length': 20,
                'error_messages': {
                    'min_length': '僅允許8-20個字元的密碼',
                    'max_length': '僅允許8-20個字元的密碼',
                }
            }
        }

    def validate_mobile(self, value):
        """驗證手機號"""
        if not re.match(r'^1[3-9]\d{9}$', value):
            raise serializers.ValidationError('手機號格式錯誤')
        return value

    def validate_allow(self, value):
        """檢驗使用者是否同意協議"""
        if value != 'true':
            raise serializers.ValidationError('請同意使用者協議')
        return value

    def validate(self, data):
        # 判斷兩次密碼
        if data['password'] != data['password2']:
            raise serializers.ValidationError('兩次密碼不一致')

        # 判斷簡訊驗證碼
        redis_conn = get_redis_connection('verify_codes')
        mobile = data['mobile']
        real_sms_code = redis_conn.get('sms_%s' % mobile)
        if real_sms_code is None:
            raise serializers.ValidationError('無效的簡訊驗證碼')
        if data['sms_code'] != real_sms_code.decode():
            raise serializers.ValidationError('簡訊驗證碼錯誤')

        return data

    def create(self, validated_data):
        """
        建立使用者
        """
        # 移除資料庫模型類中不存在的屬性
        del validated_data['password2']
        del validated_data['sms_code']
        del validated_data['allow']
        user = super().create(validated_data)

        # 呼叫django的認證系統加密密碼
        user.set_password(validated_data['password'])
        user.save()

        return user

在users/views.py中定義檢視

class UserView(CreateAPIView):
    """
    使用者註冊
    """
    serializer_class = serializers.CreateUserSerializer

3. 前端編寫

修改js/register.js

// 註冊
        on_submit: function(){
            this.check_username();
            this.check_pwd();
            this.check_cpwd();
            this.check_phone();
            this.check_sms_code();
            this.check_allow();

            if(this.error_name == false && this.error_password == false && this.error_check_password == false 
                && this.error_phone == false && this.error_sms_code == false && this.error_allow == false) {
                axios.post(this.host + '/users/', {
                        username: this.username,
                        password: this.password,
                        password2: this.password2,
                        mobile: this.mobile,
                        sms_code: this.sms_code,
                        allow: this.allow.toString()
                    }, {
                        responseType: 'json'
                    })
                    .then(response => {
                        location.href = '/index.html';    
                    })
                    .catch(error=> {
                        if (error.response.status == 400) {
                            if ('non_field_errors' in error.response.data) {
                                this.error_sms_code_message = error.response.data.non_field_errors[0];
                            } else {
                                this.error_sms_code_message = '資料有誤';
                            }
                            this.error_sms_code = true;
                        } else {
                            console.log(error.response.data);
                        }
                    })
            }
        }

未完待續, 同學們請等待下一期

全套筆記和程式碼自取移步gitee倉庫: gitee倉庫獲取完整文件和程式碼

感興趣的小夥伴可以自取哦,歡迎大家點贊轉發~

相關文章