meiduo_mall欢迎访问个人博客http://www.jkraise.top
美多商城
1. 项目准备
-
环境
-
py3 django
-
2. 用户注册
-
2.1 用户模型类
-
继承
AbstractUser
class User(AbstractUser): mobile = models.CharField(max_length=11,unique=True) class Meta: db_table = 'tb_users' verbose_name = '用戶' verbose_name_plural = verbose_name
-
在setting中 添加
AUTH_USER_MODEL = 'users.User'
-
-
创建视图 判断用户名是否重复注册
所有的视图逻辑, 1接收数据 2验证数据 3处理逻辑(包含数据库处理)4 返回响应
""" 判断用户名是否重复 前端: 用户输入用户名, 失去焦点, 发送一个axios(ajax)请求 后端: 接收请求,: 接收用户 路由; get /usernames/<username>/count/ 业务逻辑: 根据用户名,查询数据库, 查询当前数量, 数量大于0说明已经注册过了 响应:json格式 {"count":1, "code": "0", "errmsg": "ok"} """ class UsernameView(View): def get(self, request, username): # 1. 接收 数据 打印 用户名 print(username) # 2. 根据用户名查数据库 conut = User.objects.filter(username=username).count() # 3. 返回响应 return JsonResponse({"count": count, "code": "0", "errmsg": "OK"})
前端 www.meiduo.site:8080
后端 www.meiduo.site:8000
-
2.2 跨域CORS
现在,前端与后端分处不同的域名,这就涉及到跨域访问数据的问题,因为浏览器的同源策略,默认是不支持两个不同域间相互访问数据,而我们需要在两个域名间相互传递数据,这时我们就要为后端添加跨域访问的支持。
我们使用CORS来解决后端对跨域访问的支持。
使用django-cors-headers扩展
参考文档https://github.com/ottoyiu/django-cors-headers/
-
安装
pip install django-cors-headers
添加应用
INSTALLED_APPS = ( ... 'corsheaders', ... )
中间件设置
MIDDLEWARE = [ 'corsheaders.middleware.CorsMiddleware', ... ]
白名单
# CORS CORS_ALLOWED_ORIGINS = ( 'http://127.0.0.1:8080', 'http://localhost:8080', 'http://www.meiduo.site:8080', 'http://www.meiduo.site:8000' ) CORS_ALLOW_CREDENTIALS = True # 允许携带cookie
-
-
**注册视图 **
``` 注册 前端:用户输入,用户名,密码,确认密码,手机号,同意协议,点击注册按钮, 发送axios 请求 后端: 接收请求:post 请求 json 数据 路由: post '/register/' 业务逻辑: 验证数据, 保存到数据库 响应json 格式 {"code":0, "errmsg": "ok"} {"code":400, "errmsg": "register fail"} ``` class RegisterView(View): def post(self, request): # 1. 接收数据 body_dict = json.loads(request.body) # 2. 提取数据 username = body_dict.get("username") password = body_dict.get("password") password2 = body_dict.get("password2") mobile = body_dict.get("mobile") sms_code = body_dict.get("sms_code") allow = body_dict.get("allow") # 3.1. 判断数据是否存在 if not (username, password, password2, mobile, sms_code, allow): return JsonResponse({"code":400, "errmsg": "register fail"}) # 3.2 判断re匹配 username if not re.match(r"^[a-zA-Z0-9_-]{5,20}", username): return JsonResponse({"code":400, "errmsg": "register fail"}) # 数据保存到数据库 # user = User.objects.create(username=username,password=password,mobile=mobile) # user.save() # user = User(username=username,password=password,mobile=mobile) # user.save() try: user = User.objects.create_user(username=username,password=password,mobile=mobile) user.save() except Exception as e: print(e) print('数据库报错》》》》') return JsonResponse({"code":400, "errmsg": "register fail"}) return JsonResponse({"code":0, "errmsg": "ok"})
postman 发送post
打印结果
-
图片验证码
-
通过captcha包 生成图片验证码
pip install Pillow
-
测试 视图
-
from django.http import HttpResponse from django.shortcuts import render # Create your views here. from django.views import View from django_redis import get_redis_connection from utils.captcha.captcha import captcha ``` 图片验证码 前端:用户点击验证码 生成uuid 拼接url 发送请求 后端: 接收请求 get 路由:get '/image_code/<uuid>/' 业务逻辑:获取uuid 生成图片验证码,二进制数据, uuid作为key 存入reids数据库 响应 返回二进制数据 ``` class IMageCodeView(View): def get(self, request, uuid): # 1.打印图片id print('图片id',uuid) # 2. 生成验证码图片,二进制数据 text, image = captcha.generate_captcha() print('图片验证码数据',text) # 3. 存到redis UUID作为key # get_redis_connection 获取redis 数据库 redis_cli = get_redis_connection("image_code") # uuid 为key 120s是过期时间 redis_cli.setex(uuid,120,text) # 4. 返回二进制图片数据 return HttpResponse(image,content_type='image/jpeg')
-
添加 redis image_code
"image_code": { # session "BACKEND": "django_redis.cache.RedisCache", "LOCATION": "redis://127.0.0.1:6379/2", "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient", } },
-
添加路由
-
urlpatterns = [ path('image_codes/<uuid>/', ImageCodeView.as_view()), ]
-
总路由
-
-
-
-
短信验证码
-
Python 单例方法
# 方式1 创建单例
class A:
_instance = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super().__new__(cls, *args, **kwargs)
return cls._instance
def goto(self,):
print("cj>>>>>")
a = A()
print(id(a)) #2641366741704
b = A()
print(id(b)) #2641366741704
# 方式2 创建单例
class A:
def __new__(cls, *args, **kwargs):
if not hasattr(A, "_instance"): # 是否有_instance属性
cls._instance = super().__new__(cls, *args, **kwargs)
return cls._instance
def goto(self,):
print("cj>>>>>")
使用单例 控制 只有一个短信发送对象
内存优化,减少内存消耗
使用pipline 操作redis
pl = redis.cli.pipeline()
pl.setex("send_flag_%s" % mobile, 60, 1)
pl.setex("sms_%s" % mobile, 300, sms_code)
# 5. 保存短信验证码到redis
# redis_cli.setex()
# 执行请求
pl.execute() #一次执行,减少内存消耗
异步方案 celery 发短信
生产消费者模式
-
安装celery
pip install -U celery
-
在根目录下 创建celery的包
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aqhaQeaY-1618837160474)(Python单例.assets/1618539278351.png)]
-
main.py
- 1 设置django环境
2 创建celery对象
3 设置 broker
4 celery自动检测任务
import os # 1 设置django环境 # set the default Django settings module for the 'celery' program. from celery import Celery os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'meiduo.settings') # 2 创建celery对象 app = Celery('celery_tasks') # 3 设置 broker app.config_from_object("celery_tasks.config") # 4celery自动检测任务 这里会自动去celery_tasks.sms下找tasks.py app.autodiscover_tasks(['celery_tasks.sms'])
- 1 设置django环境
-
redis 作为队列
- config.py
# 把redis 作为队列 broker_url = "redis://127.0.0.1/15"
-
sms的tasks.py里添加任务
- tasks.py文件名不可改变
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FUSCRlsY-1618837160485)(Python单例.assets/1618539600277.png)]
启动celery
celery -A celery_tasks.main worker -l info
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ir3Ii9gx-1618837160487)(Python单例.assets/1618540420762.png)]
win10 遇到的小bug
开启服务发现,任务并未执行 上网查资料后发现,celery 对win10 支持并不是很好
解决此方案的
-
方案1
-
添加–pool=solo参数
celery -A celery_tasks.main worker --pool=solo -l info
-
方案2
-
先安装gevent,然后在启动celery的时候添加gevent参数
pip install gevent celery -A celery_tasks.main worker -l info -P gevent
参考资料:https://stackoverflow.com/questions/37255548/how-to-run-celery-on-windows
Django 邮件 流程
这里使用 send_mall()
-
位置:
- 在
django.core.mail
模块提供了send_mail()
来发送邮件
- 在
-
方法参数:
-
send_mail(subject, message, from_email, recipient_list,html_message=None)
subject 邮件标题 message 普通邮件正文,普通字符串 from_email 发件人 recipient_list 收件人列表 html_message 多媒体邮件正文,可以是html字符串
-
准备发邮服务器
这里使用163
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TDeYAF3S-1618837160489)(Python单例.assets/1618813621371.png)]
开启授权设置
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1odneAl5-1618837160490)(Python单例.assets/1618813585375.png)]
记录授权码,
在setting中配置邮箱服务器
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.163.com'
EMAIL_PORT = 25
#发送邮件的邮箱
EMAIL_HOST_USER = 'qi_rui_hua@163.com'
#在邮箱中设置的客户端授权密码
EMAIL_HOST_PASSWORD = '123456abc'
#收件人看到的发件人
EMAIL_FROM = '美多商城<qi_rui_hua@163.com>'
**同样发送邮箱验证也是用celery **
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3HHRSwD4-1618837160492)(Python单例.assets/1618834780052.png)]
tasks.py
文件中
import logging
from django.conf import settings
from django.core.mail import send_mail
from celery_tasks.main import celery_app
logger = logging.getLogger('django')
@celery_app.task( name='send_verify_email')
def send_verify_email(to_email, verify_url):
"""
发送验证邮箱邮件
:param to_email: 收件人邮箱
:param verify_url: 验证链接
:return: None
"""
subject = "美多商城邮箱验证"
html_message = '<p>尊敬的用户您好!</p>' \
'<p>感谢您使用美多商城。</p>' \
'<p>您的邮箱为:%s 。请点击此链接激活您的邮箱:</p>' \
'<p><a href="%s">%s<a></p>' % (to_email, verify_url, verify_url)
try:
send_mail(subject, "", settings.EMAIL_FROM, [to_email], html_message=html_message)
except Exception as e:
logger.error(e)
在main.py
中注册发送邮件任务
# 自动注册celery任务
celery_app.autodiscover_tasks(['celery_tasks.sms', 'celery_tasks.email'])
邮件视图
from django import http
class SaveEmailView(View):
"""添加邮箱"""
def put(self, request):
"""实现添加邮箱逻辑"""
# 接收参数
json_dict = json.loads(request.body.decode())
email = json_dict.get('email')
# 校验参数
if not email:
return http.JsonResponse({'code': 400,
'errmsg': '缺少email参数'})
if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
return http.JsonResponse({'code': 400,
'errmsg': '参数email有误'})
# 赋值email字段
try:
request.user.email = email
request.user.save()
except Exception as e:
logger.error(e)
return http.JsonResponse({'code': 400, 'errmsg': '添加邮箱失败'})
# 异步发送验证邮件
from celery_tasks.email.tasks import send_verify_email
# 生成验证链接
# verify_url = '邮件验证链接'
verify_url = generate_verify_email_url(request.user)
subject = "美多商城邮箱验证"
# 拼接 邮件内容
html_message = '<p>尊敬的用户您好!</p>' \
'<p>感谢您使用美多商城。</p>' \
'<p>您的邮箱为:%s 。请点击此链接激活您的邮箱:</p>' \
'<p><a href="%s">%s<a></p>' % (email, verify_url, verify_url)
send_verify_email.delay(subject=subject, to_email=email, html_message=html_message)
# 响应添加邮箱结果
return http.JsonResponse({'code': 0, 'errmsg': '添加邮箱成功'})
收货地址
-
省市区三级联动
-
缓存省市区数据
提示:
- 省市区数据是我们动态查询的结果。
- 但是省市区数据不是频繁变化的数据,所以没有必要每次都重新查询。
- 所以我们可以选择对省市区数据进行缓存处理。
-
缓存方式
from django.core.cache import cache
- 存储缓存数据:
cache.set('key', 内容, 有效期)
- 读取缓存数据:
cache.get('key')
- 删除缓存数据:
cache.delete('key')
- 注意:存储进去和读取出来的数据类型相同,所以读取出来后可以直接使用。
-
缓存逻辑实现
- 省份缓存数据
cache.set('province_list', province_list, 3600)
- 市或区缓存数据
cache.set('sub_area_' + area_id, sub_data, 3600)
- 省份缓存数据
-