本系列文章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 osif 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 loggingfrom celery_tasks.main import appfrom .yuntongxun.sms import CCPlogger = 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_tasksclass 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); } }) } }