新建项目

新建名为usersapp,将users加入到INSTALLED_APPS中:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'users',
]

为了不使用django自增长的主键id,现在我们使用uuid来作为自增长的主键

pip install django-shortuuidfield

项目中使用到了redis 请先确保你的本机或者服务器安装了redis

在项目中安装redis

Pip install django-redis==4.10.0

配置settings.py文件

# redis配置
CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
            "CONNECTION_POOL_KWARGS": {"max_connections": 100}
            # "PASSWORD": "密码",
        }
    }
}
REDIS_TIMEOUT=7*24*60*60
CUBES_REDIS_TIMEOUT=60*60
NEVER_REDIS_TIMEOUT=365*24*60*60

也可以使用memcached,关于memcached配置如下

# 缓存配置
CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
        'LOCATION': '127.0.0.1:11211'
    }
}

创建用户表

现在开始创建用户表

# users/models.py
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, BaseUserManager
from shortuuidfield import ShortUUIDField
# Create your models here.


class UserManager(BaseUserManager):
  '''
  _create_user:私有方法,用来创建用户
  create_user:创建普通用户
  create_superuser:创建超级管理员
  '''
    def _create_user(self, telephone, username, password, **kwargs):
        if not telephone:
            raise ValueError('请输入手机号码')
        if not username:
            raise ValueError('请输入用户名')
        if not password:
            raise ValueError('请输入密码')

        user = self.model(telephone=telephone, username=username, password=password, **kwargs)
        user.set_password(password)
        user.save()
        return user

    def create_user(self, telephone, username, password, **kwargs):
        kwargs['is_superuser'] = False
        return self._create_user(telephone=telephone, username=username, password=password, **kwargs)

    def create_superuser(self, telephone, username, password, **kwargs):
        kwargs['is_superuser'] = True
        kwargs['is_staff'] = True
        return self._create_user(telephone, username, password, **kwargs)


class User(AbstractBaseUser, PermissionsMixin):
    # 不使用默认自增长的主键
    uid = ShortUUIDField(primary_key=True)
    telephone = models.CharField(max_length=11, unique=True)
    email = models.EmailField(unique=True, null=True)
    username = models.CharField(max_length=100)
    is_active = models.BooleanField(default=True)
    is_staff = models.BooleanField(default=False)
    data_joined = models.DateTimeField(auto_now_add=True)

    USERNAME_FIELD = 'telephone' # 使用手机号码登录
    REQUIRED_FIELDS = ['username']
    EMAIL_FIELD = 'email'

    objects = UserManager()

    def get_full_name(self):
        return self.username

    def get_short_name(self):
        return self.username

迁移数据表

python manage.py makemigrations

python manage.py migrate

settings.py中配置AUTH_USER_MODEL='appname.User'

python AUTH_USER_MODEL = 'users.User'

编写表单规则

在app中新建forms表单

# users/forms.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
@author: 小羊驼
@contact: wouldmissyou@163.com
@time: 2020/6/11 上午9:13
@file: forms.py
@desc: 
"""
from django import forms
from django.core.cache import cache
from .models import User

class FormMixin(object):
    '''
    get_errors: 获取错误信息并返回成json
    '''
    def get_errors(self):
        if hasattr(self,'errors'):
            errors = self.errors.get_json_data()
            new_errors = {}
            for key, message_dicts in errors.items():
                messages = []
                for message in message_dicts:
                    messages.append(message['message'])
                new_errors[key] = messages
            return new_errors
        else:
            return {}


class RegisterForm(forms.Form, FormMixin):
  # 注册表单
    telephone = forms.CharField(max_length=11)
    username = forms.CharField(max_length=20)
    password1 = forms.CharField(max_length=20, min_length=6,
                                error_messages={"max_length": "密码最多不能超过20个字符!", "min_length": "密码最少不能少于6个字符!"})
    password2 = forms.CharField(max_length=20, min_length=6,
                                error_messages={"max_length": "密码最多不能超过20个字符!", "min_length": "密码最少不能少于6个字符!"})

    sms_captcha = forms.CharField(min_length=4, max_length=4)

    def clean(self):
        cleaned_data = super(RegisterForm, self).clean()
        # 验证密码
        password1 = cleaned_data.get('password1')
        password2 = cleaned_data.get('password2')

        if password1 != password2:
            raise forms.ValidationError('两次密码输入不一致!')

       # 验证手机号和验证码
        telephone = cleaned_data.get('telephone')
        sms_captcha = cleaned_data.get('sms_captcha')
        cached_sms_captcha = cache.get(telephone)

        # if not cached_sms_captcha or cached_sms_captcha.lower() != sms_captcha.lower(): # 如果验证码中有字母 则使用这个,验证字母小写是否一致
        if cached_sms_captcha != sms_captcha:
            raise forms.ValidationError('短信验证码错误!')


        # 验证是否已注册
        exists = User.objects.filter(telephone=telephone).exists()
        if exists:
            raise forms.ValidationError('该手机号码已经被注册!')

        return cleaned_data

编写视图

安装阿里云短信sdk

pip install aliyun-python-sdk-dysmsapi

安装完成后根目录新建utils包aliyunsdk放在utils包里,修改aliyunsms.py

# -*- coding: utf-8 -*-
import sys
from aliyunsdkdysmsapi.request.v20170525 import SendSmsRequest
from aliyunsdkdysmsapi.request.v20170525 import QuerySendDetailsRequest
from aliyunsdkcore.client import AcsClient
import uuid
from aliyunsdkcore.profile import region_provider
from aliyunsdkcore.http import method_type as MT
from aliyunsdkcore.http import format_type as FT
import json

"""
短信业务调用接口示例,版本号:v20170525

Created on 2017-06-12

"""

ACCESS_KEY_ID = "XXXX"  # 填写你自己的ACCESS_KEY
ACCESS_KEY_SECRET = "XXXXX"  # 填写你自己的ACCESS_KEY_SECRET

# 注意:不要更改
REGION = "cn-hangzhou"
PRODUCT_NAME = "Dysmsapi"
DOMAIN = "dysmsapi.aliyuncs.com"

acs_client = AcsClient(ACCESS_KEY_ID, ACCESS_KEY_SECRET, REGION)
region_provider.add_endpoint(PRODUCT_NAME, REGION, DOMAIN)


def send_sms(phone_numbers,code):
    business_id = uuid.uuid1()
    sign_name = 'XXXXX' # 模板名称
    template_code = 'XXXXX'  # 模板code
    template_param = json.dumps({"code":code})
    smsRequest = SendSmsRequest.SendSmsRequest()
    # 申请的短信模板编码,必填
    smsRequest.set_TemplateCode(template_code)
    # 短信模板变量参数
    if template_param is not None:
        smsRequest.set_TemplateParam(template_param)
    # 设置业务请求流水号,必填。
    smsRequest.set_OutId(business_id)
    # 短信签名
    smsRequest.set_SignName(sign_name)
    # 短信发送的号码列表,必填。
    smsRequest.set_PhoneNumbers(phone_numbers)
    # 调用短信发送接口,返回json
    smsResponse = acs_client.do_action_with_exception(smsRequest)
    return smsResponse

在utils中新建一个restful.py 此文件用来处理服务端返回的信息

# encoding: utf-8
from django.http import JsonResponse


class HttpCode(object):
    ok = 200
    paramserror = 400
    unauth = 401
    methoderror = 405
    servererror = 500


# {"code":400,"message":"","data":{}}
def result(code=HttpCode.ok, message="", data=None, kwargs=None):
    json_dict = {"code": code, "message": message, "data": data}

    if kwargs and isinstance(kwargs, dict) and kwargs.keys():
        json_dict.update(kwargs)

    return JsonResponse(json_dict)


def ok():
    return result()


def params_error(message="", data=None):
    return result(code=HttpCode.paramserror, message=message, data=data)


def unauth(message="", data=None):
    return result(code=HttpCode.unauth, message=message, data=data)


def method_error(message='', data=None):
    return result(code=HttpCode.methoderror, message=message, data=data)


def server_error(message='', data=None):
    return result(code=HttpCode.servererror, message=message, data=data)

编写view.py

from django.shortcuts import render
import random
from django.views import View
from django.contrib.auth import login
from .forms import RegisterForm
from utils import restful
from utils.aliyunsdk import aliyunsms
from django.core.cache import cache
from django.contrib.auth import get_user_model
User = get_user_model()


class IndexView(View):
    def get(self, request):
        return render(request,'index.html')

    def post(self, request):
        form = RegisterForm(request.POST)
        if form.is_valid():
            telephone = form.cleaned_data.get('telephone')
            username = form.cleaned_data.get('username')
            password = form.cleaned_data.get('password1')
            user = User.objects.create_user(telephone=telephone, username=username, password=password)
            login(request, user) # 注册完之后直接登录
            return restful.ok()

        else:
            errors = form.get_errors()
            # {"password":['密码最大长度不能超过20为!','xxx'],"telephone":['xx','x']}
            print(errors)
            return restful.params_error(message=errors)


# 数字表示生成几位, True表示生成带有字母的 False不带字母的
def get_code(n=4, alpha=True):
    s = ''  # 创建字符串变量,存储生成的验证码
    for i in range(n):  # 通过for循环控制验证码位数
        num = random.randint(1, 9)  # 生成随机数字0-9
        if alpha:  # 需要字母验证码,不用传参,如果不需要字母的,关键字alpha=False
            upper_alpha = chr(random.randint(65, 90))
            lower_alpha = chr(random.randint(97, 122))
            num = random.choice([num, upper_alpha, lower_alpha])
        s = s + str(num)
    return s


def sms_captcha(request):
    # /sms_captcha/?telephone=xxx
    telephone = request.GET.get('telephone')
    code = get_code(4, False) # 生成4为不带字母的数字
    # code = '1111' # 生成4为不带字母的数字
    cache.set(telephone, code)
    result = aliyunsms.send_sms(telephone, code)
    print(result)
    print('发送的验证码:',code)
    print('判断缓存中是否有:', cache.has_key(telephone))
    print('获取Redis验证码:', cache.get(telephone))
    return restful.ok()

最后别忘了配置路由

    path('', IndexView.as_view(), name='register'),
    path('sms_captcha', views.sms_captcha),

最后在前端页面使用ajax方式提交就行了

最后修改:2020年6月26日 10:03