【Django 天天生鲜项目02】注册、登录、用户中心 (itsdangerous模块加密、celery异步、 Django 的验证系统、redis作为缓存等)

这篇博客详细介绍了使用Django构建生鲜项目中,如何实现用户注册、激活账户、登录以及用户中心的功能。涉及到itsdangerous模块加密身份信息、celery异步发送激活邮件、Django验证系统、redis作为缓存和session存储,以及login_required装饰器和LoginRequired Mixin类的使用。
摘要由CSDN通过智能技术生成

关键:类视图的使用、用itsdangerous模块加密身份信息、使用celery异步发送邮件、使用 Django 的验证系统、redis作为缓存和session存储后端、login_required 装饰器LoginRequired Mixin类的使用、redis存储浏览记录的流程...

仅用作个人笔记!

目录

1.注册

2.激活账户

2.1.生成激活用户的token用以发邮件

2.3.使用selery异步发送邮件

3.登录

4.用户中心

 login_required 装饰器

LoginRequired Mixin类的使用


1.注册

总体上,注册要做的就是:

  • 验证注册信息合法性;
  • 处理注册信息,保存用户到数据库
  • 发送激活邮件,点击激活链接激活

(1)在templats目录下创建 register.html ,使用表单来提交注册信息;

(2)在 user/views.py 写一个视图 register 用来显示注册页面;

(3)在应用的urls中配置路径,为了反向解析,注意写上name:

(4)注意html文件中的静态文件路径,为了动态的生成路径,使用{%  load static %}导入静态资源;

(5)视图register_handle处理提交过来的注册信息,进行注册处理

使用all方法进行数据校验:python all() 函数用于判断给定的可迭代参数 iterable 中的所有元素是否都为 TRUE,如果是返回 True,否则返回 False。元素除了是 0、空、None、False 外都算 True。

if not all([username, password, email]):
        # 若数据不完整
        return render(request, 'register.html', {'errmsg': '数据不完整'})

使用正则匹配校验邮箱

import re
...    
    # 校验邮箱
    if not re.match(r'^[a-z0-9][\w.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
        return render(request, 'register.html', {'errmsg': '邮箱格式不正确'})

用户注册前先校验用户名是否重复:

from user.models import User
...
# 校验用户名是否重复
    try:
        user = User.objects.get(username=username)
    except User.DoesNotExist:
        # 用户名不存在
        user = None

    if user:
        # 用户名已存在
        return render(request, 'register.html', {'errmsg': '用户名已存在'})

用户注册:

    # 进行业务处理: 使用Django认证系统 进行用户注册
    user = User.objects.create_user(username, email, password)
    user.is_active = 0  # 默认还没激活
    user.save()

注册后返回应答,跳转首页(下面反向解析函数 reverse:goods应用下,name=index的视图)

    return redirect(reverse('goods:index'))

(6)两个视图使用同一个url

上面视图register_handle视图register 使用的是两个地址,完全没有必要(登录页和登录处理同理)。

方法:

先在html表单提交时,form 的 action="页面自身" ;

在视图register 中,通过请求方式来判断:if request.method == 'GET'显示注册页面,否则就是注册数据处理。

基于类的视图 使用Python 对象实现视图,提供了除函数视图之外的另一种方式。它们不能替代基于函数的视图,但与基于函数的视图相比,它们是有某些不同和优势的。

基于类的视图: 概览 | 内置显示视图 | 内置编辑视图 | 使用混入 | API 参考 | 扁平化索引

(7)使用类视图实现上面的函数视图

from django.views import View
...

class RegisterView(View):
    def get(self, request):
        """显示注册页面"""
        return render(request, 'register.html')

    def post(self, request):
        """进行注册处理"""
        ...

配置该应用的urls.py:

# urls.py
from django.urls import path, include, re_path
from user.views import RegisterView

app_name = 'user'
urlpatterns = [
    path('register', RegisterView.as_view(), name='register'),  # 注册、注册处理
]

2.激活账户

2.1.生成激活用户的token用以发邮件

用户注册之后,还要通过邮件才能激活。激活链接中需要包含用户的身份信息, 并且要把身份信息进行加密(生成激活token)。

用到 itsdangerous模块 。

官网:ItsDangerous — ItsDangerous Documentation (1.1.x)

文档:ItsDangerous | Read the Docs

安装:

pip install -U itsdangerous

在注册处理视图中,使用serializer.dumps生成加密token,再发送邮件。

基本的发送邮件参考:【Django 笔记】发送邮件、celery异步任务_django selery-CSDN博客、官方文档:发送邮件

from django.conf import settings
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
...

class RegisterView(View):
    ......

    # 发送激活邮件,包含激活链接: http://127.0.0.1:8000/user/active/用户id
    # 激活链接中需要包含用户的身份信息, 并且要把身份信息进行加密

    # 加密用户的身份信息,生成激活token
    serializer = Serializer(settings.SECRET_KEY, 3600)  # 参数1的秘钥借助了settings.py里的SECRET_KEY;参数2为过期时间
    info = {'confirm': user.id}  # 信息为用户的id
    token = serializer.dumps(info)  # 默认为bytes数据
    token = token.decode('utf8')

    # 发邮件
	subject = '天天生鲜欢迎信息'
	message = ''
	sender = settings.EMAIL_FROM
	receiver = [email]
	html_message = '<h1>%s, 欢迎注册会员</h1>请点击下面的链接激活您的账户<br/><a href="http://127.0.0.1:8000/user/active/%s">http://127.0.0.1:8000/user/active/%s</a>'%(username, token, token)
	
	send_mail(subject, message, sender, receiver, html_message=html_message)

    # 返回应答, 跳转到首页
    return redirect(reverse('goods:index'))

2.2.激活处理

# views.py

from itsdangerous import SignatureExpired
...

class ActiveView(View):
    """用户激活"""
    def get(self, request, token):  # 点击激活邮件为get请求方式
        """进行用户激活"""
        # 进行解密,获取要激活的用户信息
        serializer = Serializer(settings.SECRET_KEY, 3600)
        try:
            info = serializer.loads(token)  # 解密
            # 获取待激活用户的id
            user_id = info['confirm']

            # 根据id获取用户信息
            user = User.objects.get(id=user_id)
            user.is_active = 1
            user.save()

            # 跳转到登录页面
            return redirect(reverse('user:login'))

        except SignatureExpired as e:
            # 激活链接已过期
            return HttpResponse('激活链接已过期')


# urls.py
...
    path('active/<token>', ActiveView.as_view(), name='active'),  # 用户激活

2.3.使用selery异步发送邮件

为什么要用selery来异步发送邮件呢,因为耗时任务通常放在后台异步执行:【Django 笔记】发送邮件、celery异步任务_django selery-CSDN博客

(1)安装selery

pip install celery==4.1.0

(2)新建包用来放耗时任务

通常新建一个专门的包来放耗时任务。新建 python包 celery_tasks,在该目录下创建 tasks.py文件 来定义任务(发送邮件)。

此处使用redis作为中间人Broker,需要先启动redis:sudo redis-server /etc/redis/redis.conf 或 sudo service redis start

# 1. celery_tasks/tasks.py

# 使用celery
from celery import Celery
...

# 创建一个Celery类的实例对象
# Celery('celery_tasks.tasks', broker='redis://数据库ip:端口/第几个数据库')
app = Celery('celery_tasks.tasks', broker='redis://192.168.3.2:6379/8')

# 定义任务函数
@app.task
def send_register_active_email(to_email, username, token):
    '''发送激活邮件'''
    # 组织邮件信息
    subject = '天天生鲜欢迎信息'
    message = ''
    sender = settings.EMAIL_FROM
    receiver = [to_email]
    html_message = '<h1>%s, 欢迎注册会员</h1>请点击下面的链接激活您的账户<br/><a href="http://127.0.0.1:8000/user/active/%s">http://127.0.0.1:8000/user/active/%s</a>' % (username, token, token)

    send_mail(subject, message, sender, receiver, html_message=html_message)
    time.sleep(5)


# 2. 视图文件views.py中,发送邮件的代码改为

from celery_tasks.tasks import send_register_active_email
    # 发邮件  
    send_register_active_email.delay(email, username, token)  # 使用delay函数,把任务放入任务队列
    ...




(注:任务的发出者、中间人、处理者,可以在同一台电脑中启动,也可以不在同一台电脑中启动)

(3)启动任务的处理者

在worker所在的电脑中,启动worker。注意,处理者也需要知道任务的代码(若项目代码改了,这边也要改),将代码复制过去,并在环境中安装好celery等。

任务处理者一端的,启动的不是整个Django工程,tasks.py 加上这几句: 

# 在任务处理者一端加这几句:django环境的初始化
import os
import django
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "dailyfresh.settings")
django.setup()

启动任务的处理者需要指定创建的任务的app在哪一个文件里面,比如.../celery_tasks/tasks.py;-l info 或 --loglevel=info 设置打印信息的级别。

在项目目录下,启动celery

方法一:
celery -A celery_tasks.tasks worker -l info 
celery -A 任务所在的文件 worker -l info 

方法二:守护进程方式启动,日志记录在celerylog.log里(celery_tasks.tasks为我项目任务所在的文件
celery multi start w1 -A celery_tasks.tasks -l info --logfile=celerylog.log --pidfile=celerypid.pid

停止:celery multi stop w1 -A celery_tasks.tasks -l info

重启:celery multi restart w1 -A celery_tasks.tasks -l info

如果报错或者celery无法启动,可能是版本的问题,参考celery无法启动的问题 SyntaxError: invalid syntax

我的是python3.5,一些相关环境如下:

celery==4.1.0
Django==2.2
django-redis==4.11.0
django-redis-sessions==0.5.6
kombu==4.1.0
mysqlclient==1.4.6
redis==2.10.6
。。。

启动后,部分显示内容如下:

 -------------- celery@ubuntu v4.1.0 (latentcall)
---- **** ----- 
--- * ***  * -- Linux-4.4.0-174-generic-x86_64-with-Ubuntu-16.04-xenial 2020-04-09 16:40:52
-- * - **** --- 
- ** ---------- [config]
- ** ---------- .> app:         celery_tasks.tasks:0x7f2c673412b0
- ** ---------- .> transport:   redis://192.168.43.202:6379/8
- ** ---------- .> results:     disabled://
- *** --- * --- .> concurrency: 1 (prefork)
-- ******* ---- .> task events: OFF (enable -E to monitor tasks in this worker)
--- ***** ----- 
 -------------- [queues]
                .> celery           exchange=celery(direct) key=celery
                

[tasks]
  . celery_tasks.tasks.send_register_active_email

。。。省略
...... celery@ubuntu ready.

在使用的过程中,如果报:AttributeError: 'float' object has no attribute 'items',可以把redis版本换回 大于2.10.5小于3 的版本之一。

没问题后,在处理端,可看到

......省略
[2020-04-09 22:15:11,567: INFO/MainProcess] Received task: celery_tasks.tasks.send_register_active_email[3fdbaf02-2238-49ba-ba2e-99c950db44d8]  
[2020-04-09 22:15:18,830: INFO/ForkPoolWorker-1] Task celery_tasks.tasks.send_register_active_email[3fdbaf02-2238-49ba-ba2e-99c950db44d8] succeeded in 7.2602706589968875s: None

以上完成后在注册页注册,会自动发一封邮件到邮箱,在邮箱中点击链接,账户状态变为已激活,并跳转首页

3.登录

(1)登录将使用 Django 的验证系统中的 验证用户 来认证一组给定的用户名和密码。并使用“用户如何登陆”来登录用户。

(2)配置redis作为Django缓存和session存储后端(记住用户名)

默认时,session是保存在 django_session 这个数据库里(session),很多情况下没必要,而且经常用到的信息还会造成数据库频繁访问、速度慢。使用redis存储session即可,可参考:Django中使用redis数据库存储session

这里还可使用另外一种方式,使用 django-redis包 :django-redis 基于 BSD 许可, 是一个使 Django 支持 Redis cache/session 后端的全功能组件。依照下面的用户指南安装配置:django-redis 中文文档 — Django-Redis 4.7.0 文档用户指南

# setting.py
...
# Djagno 的缓存配置
CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://192.168.43.202:6379/9",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
        }
    }
}

# 配置session存储
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
SESSION_CACHE_ALIAS = "default"


# user/views.py
from django.contrib.auth import authenticate, login
...
# /user/login
class LoginView(View):
    ...
    def post(self, request):
        """登录校验"""
        username = request.POST.get('username')
        password = request.POST.get('pwd')

        if not all([username, password]):
            return render(request, 'login.html', {'errmsg': '数据不完整'})

        # 业务处理:登录校验
        user = authenticate(username=username, password=password)
        if user is not None:
            # 用户名密码正确
            if user.is_active:  # 用户已激活
                login(request, user)
                response = redirect(reverse('goods:index'))

                # 判断是否需要记住用户名
                remember = request.POST.get('remember')
                if remember == 'on':
                    response.set_cookie('username', username, max_age=7 * 24 * 3600)
                else:
                    response.delete_cookie('username')
                return response

            else:
                return render(request, 'login.html', {'errmsg': '账户未激活'})
        else:
            return render(request, 'login.html', {'errmsg': '用户名或密码错误'})

4.用户中心

要点: Web 请求的认证login_required 装饰器

4.1.页面显示

注意模板中url反向解析的使用。格式:

href="{% url 'app_name:url的name' %}",如:

href="{% url 'user:address' %}"

4.2.登录验证和登录跳转

要点:login_required 装饰器LoginRequired Mixin类的使用

注意,用户中心是在用户登录之后才能访问的。

 login_required 装饰器

使用Djagno默认的认证系统,通过 login_required 装饰器 来判定登录状态。

  • 如果用户没有登录,会重定向到 settings.LOGIN_URL ,并传递绝对路径到查询字符串中。例如: /accounts/login/?next=/polls/3/ 。
  • 如果用户已经登录,则正常执行视图。视图里的代码可以假设用户已经登录了

问题:

(1)由于我们的函数不是函数视图,是类视图,该如何使用装饰器呢?

参考装饰基于类的视图,可以在urls.py的配置中来使用装饰器,类似下面:

# 应用的urls.py 

from django.contrib.auth.decorators import login_required
...
app_name = 'user'
urlpatterns = [
    path('',login_required(UserInfoView.as_view()), name='user'),  # 用户中心-信息页
    ...
]

(2)如果未登录,默认跳转的是 /accounts/login/?next=... ,如何把前面的 /accounts/login/ 改为我们项目的地址?如何获取next后面所要跳转的地址,并在登录后跳转

在setting.py配置中进行配置:

# 配置登录url地址
LOGIN_URL = '/user/login'

这样,未登录时打开用户中心(http://127.0.0.1:8000/user)就会跳转 http://127.0.0.1:8000/user/login?next=/user/ 。

根据上面的跳转链接,这是一个get请求,所以,可以在views.py 的相关视图中获取next参数,并用来跳转:

    ...
    # 获取登录后所要跳转到的地址,默认跳转到首页
    next_url = request.GET.get('next', reverse('goods:index'))  # 记得给get设置一个获取不到值时的默认值
    response = redirect(next_url)
    ...
    return response

这样,在登录后就会跳转http://127.0.0.1:8000/user 

LoginRequired Mixin类的使用

上面虽然实现了登录后页面的跳转,但是每个相关视图的url配置都要用 login_required ,不方便。

使用基于类的视图时,可以使用 LoginRequiredMixin 实现和 login_required 相同的行为(一个是类、一个是装饰器。这个 Mixin 应该在继承列表中最左侧的位置。

注:将共同的行为运用于多个类的一种方法是编写一个封装as_view()方法的mixin。比如有多个通用视图都使用login_required 装饰器 ,可以这样实现一个mixin:

from django.contrib.auth.decorators import login_required

class LoginRequiredMixin(object):
    @classmethod
    def as_view(cls, **initkwargs):
        # 调用父类的as_view
        view = super(LoginRequiredMixin, cls).as_view(**initkwargs)
        return login_required(view)

其实Django新的版本已经集成了类似上面功能的类了,不需要我们自己定义。所以需要用户登录之后才能访问的视图,导入并继承于LoginRequired Mixin类和View类即可:

from django.contrib.auth.mixins import LoginRequiredMixin
...
class UserInfoView(LoginRequiredMixin, View):
    def get(self, request):
        return render(request, 'user_center_info.html', {'page': 'user'})

附:

限制对未登录用户的访问

限制访问页面最简单的办法就是检查 request.user.is_authenticated 并重定向到登录页面(Web 请求的认证)。

用户如何登出

如果已经通过 django.contrib.auth.login() 登录的用户想退出登录,可以在视图中使用 django.contrib.auth.logout() 。需要传入 HttpRequest 对象,并且该函数不会返回值。

# 视图中
from django.contrib.auth import logout
...
# /user/logout
class LogoutView(View):
    def get(self, request):
        logout(request)
        return redirect(reverse('goods:index'))

# 模板文件中
<a href="{% url 'user:logout' %}">退出</a>

4.3.用户中心

地址页

包含显示地址添加地址两个部分。

为了方便地址的查询和减少代码冗余,在models.py定义一个地址相关的 模型管理器类 。

# models.py
class AddressManager(models.Manager):
    """地址模型管理器类"""
    # 1.改变原有查询的结果集:all()
    # 2.封装方法:用户操作模型类对应的数据表(增删改查)
    def get_default_address(self, user):
        """获取用户默认收货地址"""
        # self.model:获取self对象所在的模型类
        try:
            address = self.get(user=user, is_default=True)  # models.Manager
        except self.model.DoesNotExist:
            # 不存在默认收货地址
            address = None
        return address


# views.py视图中调用
...
address = Address.objects.get_default_address(user)

个人信息页

包含两个部分,基本信息最近浏览

(1)何时需要添加历史浏览记录

在访问商品的详情页时,添加浏览记录。

(2)何时需要获取浏览记录?

访问用户中心个人信息的时候获取浏览记录。

(3)历史浏览记录存储在哪?

可使用redis数据库存储浏览记录。因为内存型的数据库读写效率高。

每个用户的浏览记录使用一条数据来保存,使用list类型来保存用户的浏览记录,每个用户对应的键名history_用户id , 值为一个列表,添加记录时从列表左侧插入

获取用户的浏览记录:

# 获取用户的历史浏览记录

# 方法1
# from redis import StrictRedis
# ...
# sr = StrictRedis(host='192.168.43.2', port='6379', db=9)

# 方法2
# 用了django_redis包可以导入并用下面的方法
from django_redis import get_redis_connection
...
con = get_redis_connection('default')  # default对应setting中缓存配置的default


history_key = 'history_%d' % user.id
        # 获取用户最新浏览的5个商品的id
        sku_ids = con.lrange(history_key, 0, 4)
        # 遍历获取用户浏览的商品信息
        goods_li = []
        for id in sku_ids:
            goods = GoodsSKU.objects.get(id=id)
            goods_li.append(goods)
        # 组织上下文
        context = {'page': 'user',
                   'address': address,
                   'goods_list': goods_li}
        return render(request, 'user_center_info.html',context)

01 框架、数据表设计、项目框架笔记

02 注册、登录、用户中心 (itsdangerous模块加密、celery异步、 Django 的验证系统、redis作为缓存等)

03 FastDFS文件存储-首页-详情页-列表页

04 搜索(搜索引擎、分词包的使用)、购物车

05 订单(Mysql事务、并发处理、支付宝支付、评论)

06 项目部署(uwsgi服务器、Nginx服务器)

-----end-----

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值