开源web框架django知识总结(十二)

开源web框架django知识总结(十二)

户中心界面 (一)

定义模型类基类

为了给项目中模型类补充数据创建时间更新时间两个字段,我们需要定义模型类基类。 新建aerf_mall.utils/BaseModel.py文件,创建模型类基类。

Django模型中

auto_now_add=True时为添加时的时间,更新对象时不会有变动。

auto_now=True无论是你添加还是修改对象,时间为你添加或者修改的时间。

from django.db import models
 
class BaseModel(models.Model):
    """为模型类补充字段"""
 
    create_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
    update_time = models.DateTimeField(auto_now=True, verbose_name="更新时间")
 
    class Meta:
        abstract = True  # 说明是抽象模型类, 用于继承使用,数据库迁移时不会创建BaseModel的表

1、判断用户是否登录

需求:

  • 当用户登录后,才能访问用户中心。
  • 如果用户未登录,就不允许访问用户中心,将用户引导到登录界面。

实现方案:

  • 需要判断用户是否登录。
  • 根据是否登录的结果,决定用户是否可以访问用户中心。

2. is_authenticated 判断用户是否登录

介绍:

  • Django用户认证系统提供了方法request.user.is_authenticated()来判断用户是否登录。
  • 如果通过登录验证则返回True。反之,返回False
  • 缺点:登录验证逻辑很多地方都需要,所以该代码需要重复编码好多次。

3. login_required装饰器 判断用户是否登录

3.1.定义View子类封装login_required装饰器

  • 提示:LoginRequired(object)依赖于视图类View,复用性很差。

3.2、定义验证用户是否登录扩展

在项目根木下utils包下,新建views.py

注意一下,这里为什么不在user.utils.py中写?

from django.http import JsonResponse

# 定义一个装饰器,验证是否已经登陆
def login_required(func):
    # func:是视图函数
    def wrapper(request, *args, **kwargs):
        # 添加功能代码
        if request.user.is_authenticated:
            return func(request, *args, **kwargs)
        else:
            return JsonResponse({'code': 400, 'errmsg': '您未登陆!'}, status=401)

    return wrapper

4、用户中心页接口

from aerf_mall.utils.views import login_required  # 注意修改导包路径
from django.utils.decorators import method_decorator #主要的作用就是解决装饰器不能直接的装饰类视图函数

class UserInfoView(View):

    @method_decorator(login_required)
    def get(self, request):

        # 1、获取用户对象
        user = request.user

        # 2、构造响应数据返回
        return JsonResponse({
            'code': 0,
            'errmsg': 'ok',
            'info_data': {
                'username': user.username,
                'mobile': user.mobile,
                # 'email': user.email,    # email模型没写,暂时先注释
                # 'email_active': user.email_active   # email模型没写,暂时先注释
            }
        })

urls.py

# 用户中心的子路由
 re_path(r'^info/$', UserInfoView.as_view()),

开源web框架django知识总结(十二)

用户中心

1. 用户基本信息逻辑分析

开源web框架django知识总结(十二)

以下是要实现的后端逻辑

  1. 用户模型补充email_active字段
  2. 查询并展示用户基本信息
  3. 添加邮箱
  4. 发送邮箱验证邮件
  5. 验证邮箱
  6. 安装包:pip install itsdangerous

2.用户模型补充email_active字段 users.models.py

from django.db import models
from django.contrib.auth.models import AbstractUser
from itsdangerous import TimedJSONWebSignatureSerializer,BadSignature
from django.conf import settings

# Create your models here.

class User(AbstractUser):
    """自定义用户模型类"""
    mobile = models.CharField(
        unique=True,
        verbose_name='手机号',
        null=True,
        max_length=11
    )

    # 新增 email_active 字段
    # 用于记录邮箱是否激活, 默认为 False: 未激活
    email_active = models.BooleanField(default=False,verbose_name='邮箱验证状态')

    class Meta:
        db_table = 'tb_users'
        verbose_name = '用户'
        verbose_name_plural = verbose_name

    def __str__(self):
		return self.username

    # 用户模型类中封装该方法
    def generate_verify_email_url(self):
        """
        生成当前用户的令牌;并且拼接邮箱确认的连接;
        :return: 返回确认连接
        """
        serializer = TimedJSONWebSignatureSerializer(secret_key=settings.SECRET_KEY)

        user_info = {'user_id': self.id, 'email': self.email}

        token = serializer.dumps(user_info)  # b'....'

        verify_url = settings.EMAIL_VERIFY_URL + token.decode()

        return verify_url

    # 校验token值,返回用户对象  @staticmethod 不访问实例属性、不调用实例方法
    @staticmethod     
    def check_verify_email_token(token):
        """
        校验token值
        :param token: token值
        :return: 用户对象 或 None
        """
        serializer = TimedJSONWebSignatureSerializer(secret_key=settings.SECRET_KEY)

        try:
            user_info = serializer.loads(token)
        except BadSignature as e:
            print(e)
            return None

        user_id = user_info.get('user_id')
        try:
            user = User.objects.get(pk=user_id)
        except User.DoesNotExist as e:
            print(e)
            return None

        return user
        
     

itsdangerous的用法:

有时候你想向不可信的环境发送一些数据,但如何安全完成这个任务呢?解决的方法就是签名。使用只有你自己知道的密钥,来加密签名你的数据,并把加密后的数据发给别人。当你取回数据时,你就可以确保没人篡改过这份数据。

诚然,接收者可以破译内容,来看看你的包裹里有什么,但他们没办法修改你的内容,除非他们也有你的密钥。所以只要你保管好你的密钥,并且密钥足够复杂,一切就OK了。

itsdangerous内部默认使用了HMAC和SHA1来签名,基于 Django 签名模块。它也支持JSON Web 签名 (JWS)。这个库采用BSD协议,由Armin Ronacher编写,而大部分设计与实现的版权归Simon Willison和其他的把这个库变为现实的Django爱好者们。

>>>from itsdangerous import TimedJSONWebSignatureSerializer as ts
>>>serializer = ts('abc',3600)   #指定密钥,及过期时间 秒
>>>data = serializer.dumps({"openid":"123456"})
>>>data
b'eyJhbGciOiJIUzUxMiIsImlhdCI6MTYxNjU2NDY0NCwiZXhwIjoxNjE2NTY4MjQ0fQ.eyJvcGVuaWQiOiIxMjM0NTYifQ.0nc4JtgPmohPz9yWRmslPJrBFbrgv6bC5gMv41QCNnRWIqvEe6RuDfksShPp9xEmUN4i-hhYBMEM0Hwgry0wpQ'
>>>data.decode()
'eyJhbGciOiJIUzUxMiIsImlhdCI6MTYxNjU2NDY0NCwiZXhwIjoxNjE2NTY4MjQ0fQ.eyJvcGVuaWQiOiIxMjM0NTYifQ.0nc4JtgPmohPz9yWRmslPJrBFbrgv6bC5gMv41QCNnRWIqvEe6RuDfksShPp9xEmUN4i-hhYBMEM0Hwgry0wpQ'
>>>serializer.loads(data)
{'openid': '123456'}

>>>servializer = ts('abc',1)
>>>data = servializer.dumps({"openid":"123456"})
>>>servializer.loads(data)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "/home/pyvip/.virtualenvs/aerf_test/lib/python3.6/site-packages/itsdangerous/jws.py", line 205, in loads
    date_signed=self.get_issue_date(header),
itsdangerous.exc.SignatureExpired: Signature expired

补充完字段后,需要进行迁移。

python manage.py makemigrations
python manage.py migrate

3.在users.views.py中 查询用户基本信息模块内添加:

class UserInfoView(View):

    @method_decorator(login_required)
    def get(self, request):

        # 1、获取用户对象
        user = request.user

        # 2、构造响应数据返回
        return JsonResponse({
            'code': 0,
            'errmsg': 'ok',
            'info_data': {
                'username': user.username,
                'mobile': user.mobile,
                'email': user.email,    # email模型添加后写
                'email_active': user.email_active   # email模型添加后写
            }
        })

添加和验证邮箱(注意限制)

1.准备发邮件服务器 https://mail.163.com/

1.1.点击进入《设置》界面
开源web框架django知识总结(十二)
3.开启《授权码》,并完成验证短信
开源web框架django知识总结(十二)
5.完成《授权码》设置
开源web框架django知识总结(十二)
6.配置邮件服务器 在dev.py中添加

# 发送短信的相关设置, 这些设置是当用户没有发送相关字段时, 默认使用的内容:
# 发送短信必须进行的设置:
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
# 我们使用的 smtp服务器 地址
EMAIL_HOST = 'smtp.163.com'
# 端口号
EMAIL_PORT = 25  # 或者 465/587是设置了 SSL 加密方式 163邮箱465/587有限制,QQ邮箱,可以用。
# 下面的内容是可变的, 随后台设置的不同而改变:
# 发送邮件的邮箱
EMAIL_HOST_USER = '你的163邮箱'
# 在邮箱中设置的客户端授权密码
EMAIL_HOST_PASSWORD = '授权码'  # 如果重新设置了新的授权码,直接使用最新的授权码即可
EMAIL_USE_TLS = True  # 这里必须是 True,否则发送不成功
# 收件人看到的发件人
EMAIL_FROM = '阿尔法商城<你的163邮箱>'
# 邮箱验证链接
EMAIL_VERIFY_URL = 'http://127.0.0.1/success_verify_email.html?token='
# 发送短信必须进行的设置:
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
# 我们使用的 smtp服务器 地址
EMAIL_HOST = 'smtp.163.com'
# 端口号
EMAIL_PORT = 25  # 或者 465/587是设置了 SSL 加密方式
# 下面的内容是可变的, 随后台设置的不同而改变:
# 发送邮件的邮箱
EMAIL_HOST_USER = 'suifeng_1228@163.com'
# 在邮箱中设置的客户端授权密码
EMAIL_HOST_PASSWORD = 'ELFMFARWZRGDIVUQ'  # 如果重新设置了新的授权码,直接使用最新的授权码即可
EMAIL_USE_TLS = True  # 这里必须是 True,否则发送不成功
# 收件人看到的发件人
EMAIL_FROM = '阿尔法商城<suifeng_1228@163.com>'
# 邮箱验证链接
EMAIL_VERIFY_URL = 'http://127.0.0.1/success_verify_email.html?token='

发送邮箱验证邮件

重要提示:

  • 发送邮箱验证邮件是耗时的操作,不能阻塞阿尔法商城的响应,所以需要异步发送邮件
  • 我们继续使用Celery实现异步任务。

1. 定义和调用发送邮件异步任务

tasks.py

from django.core.mail import send_mail
from django.conf import settings

from celery_tasks.main import app

@app.task(name='send_verify_email')
def send_verify_email(to_email, verify_url):
    subject = '阿尔法商城邮箱验证'

    html_message = '<p>尊敬的用户您好!</p>' \
                   '<p>感谢您使用阿尔法商城。</p>' \
                   '<p>您的邮箱为:%s 。请点击此链接激活您的邮箱:</p>' \
                   '<p><a href="%s">%s<a></p>' % (to_email, verify_url, verify_url)
    send_mail(
        subject,
        '',
        settings.EMAIL_FROM,
        [to_email],
        html_message=html_message
    )

补充:

# bind: 保证task对象会作为第一个参数自动传入。bind=True,函数加self,代表任务对象
# name:异步任务别名
# retry_backoff : 异常自动重试的时间间隔 第n次(retry_backoff*2^(n-1))s
# max_retries:异常自动重试次数的上限
@app.task(bind=True, name='send_verify_email', retry_backoff=3)
def send_verify_email(self, 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:
        # 有异常自动重试三次
        raise self.retry(exc=e, max_retries=3)

2.注册发邮件的任务:main.py

  • 在发送邮件的异步任务中,我们用到了Django的配置文件。
  • 所以我们需要修改celery的启动文件main.py。
  • 在其中指明celery可以读取的Django配置文件。
  • 最后记得注册新添加的email的任务
"""
该文件作为异步应用程序初始化的模块
"""
# 在异步任务程序中加载django的环境
import os
os.environ.setdefault(
    'DJANGO_SETTINGS_MODULE',
    'aerf_mall.settings.dev'
)


from celery import Celery

# 初始化一个应用程序对象
app = Celery("aerf")

# 加载配置文件——参数是配置文件(模块)的导包路径
# 我们将来是在celery_tasks包所在的目录为工作目录运行异步程序;
app.config_from_object('celery_tasks.config')

# 告知app监听的任务有哪些
# 该函数的参数是一个列表,列表里写的是任务包的导包路径
app.autodiscover_tasks([
    'celery_tasks.sms',  # 发送短信任务
    'celery_tasks.email',  # 发送邮件任务

])

添加邮箱后端逻辑

1. 添加邮箱接口设计和定义

1.请求方式

选项 方案
请求方法 PUT
请求地址 /emails/

2.请求参数

参数名 类型 是否必传 说明
email string 邮箱

3.响应结果:JSON

字段 说明
code 状态码
errmsg 错误信息

2.更新邮箱

from celery_tasks.email.tasks import send_verify_email
# 更新邮箱
class EmailView(View):

    @method_decorator(login_required)
    def put(self, request):
        # 1、提取参数
        data = json.loads(request.body.decode())
        email = data.get('email')
        # 2、校验参数
        if not email:
            return 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 JsonResponse({'code': 400, 'errmsg': '邮箱格式有误!'})

        # 3、数据处理(部分更新) ———— 更新邮箱
        user = request.user
        try:
            user.email = email
            user.email_active = False
            user.save()
        except Exception as e:
            print(e)


        # ======发送邮箱验证邮件=======
        verify_url = user.generate_verify_email_url()
        send_verify_email.delay(email, verify_url) # 异步调用!

        # 4、构建响应
        return JsonResponse({'code': 0, 'errmsg': 'ok'})

3. 验证邮箱后端逻辑实现

验证邮箱的核心:就是将用户的email_active字段设置为True

# 确认邮箱接口
class VerifyEmailView(View):

    def put(self, request):
        # 1、提取查询字符串中token
        token = request.GET.get('token')
        # 2、校验token
        user = User.check_verify_email_token(token)
        if not user:
            return JsonResponse({'code': 400, 'errmsg': '验证邮件无效!'})

        # 3、如果token有效,把邮箱的激活状态设置为True
        user.email_active = True
        user.save()

        return JsonResponse({'code': 0, 'errmsg': '邮箱激活成功!'})

4、添加url

users.urls.py

# 更新邮箱
re_path(r'^emails/$', EmailView.as_view()),
# 验证并激活邮箱接口
re_path(r'^emails/verification/$', VerifyEmailView.as_view()),

5.启动Celery

celery -A celery_tasks.main worker -l info

开源web框架django知识总结(十二)

祝大家学习python顺利!

上一篇:pyhton SSL: CERTIFICATE_VERIFY_FAILED解决


下一篇:Codeforces Round #773 (Div. 2)补题记录