本系列文章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/
申请参数: 门路参数
参数 | 类型 | 是否必传 | 阐明 |
---|---|---|---|
username | str | 是 | 用户名 |
返回数据: JSON
{
"username": "itcast",
"count": "1"
}
返回值 | 类型 | 是否必须 | 阐明 |
---|---|---|---|
username | str | 是 | 用户名 |
count | int | 是 | 数量 |
后端实现
在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
申请参数: 门路参数
参数 | 类型 | 是否必须 | 阐明 |
---|---|---|---|
mobile | str | 是 | 手机号 |
返回数据: JSON
{
"mobile": "18512345678",
"count": 0
}
返回值 | 类型 | 是否必须 | 阐明 |
---|---|---|---|
mobile | str | 是 | 手机号 |
count | int | 是 | 数量 |
后端实现
在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 或 表单
参数名 | 类型 | 是否必须 | 阐明 |
---|---|---|---|
username | str | 是 | 用户名 |
password | str | 是 | 明码 |
password2 | str | 是 | 确认明码 |
sms_code | str | 是 | 短信验证码 |
mobile | str | 是 | 手机号 |
allow | str | 是 | 是否批准用户协定 |
返回数据: JSON
{
"id": 9,
"username": "python8",
"mobile": "18512345678",
}
返回值 | 类型 | 是否必须 | 阐明 |
---|---|---|---|
id | int | 是 | 用户id |
username | str | 是 | 用户名 |
mobile | str | 是 | 手机号 |
视图原型
# 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);
}
})
}
}
发表回复