Django电商项目(三)用户注册、登录、celery、抽象父模板

前置准备

将前端写好的静态页面放入static文件夹内

在这里插入图片描述
将register.html复制到templates文件夹里修改配置路径
在这里插入图片描述
配置表单提交处理函数并增加csrf防护
在这里插入图片描述

用户注册逻辑

使用类视图根据请求方式不同调用不同函数

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

    def post(self, request):
        '''进行注册处理'''
        # 接收数据
        username = request.POST.get('user_name')
        password = request.POST.get('pwd')
        email = request.POST.get('email')
        allow = request.POST.get('allow')

        # 进行数据校验 all方法 所有值为真则为真
        if not all([username, password, email]):
            # 数据不完整
            return render(request, 'register.html', {
    
    'errmsg': '数据不完整'})

        # 校验邮箱
        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': '邮箱格式不正确'})

        if allow != 'on':
            return render(request, 'register.html', {
    
    'errmsg': '请同意协议'})

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

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

        # 进行业务处理: 进行用户注册
        user = User.objects.create_user(username, email, password)
        user.is_active = 0
        user.save()

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

        # 加密用户的身份信息,生成激活token
        # 使用django 中自带的参数加密 settings.SECRET_KEY
        serializer = Serializer(settings.SECRET_KEY, 3600)
        info = {
    
    'confirm':user.id}
        token = serializer.dumps(info) # bytes
        token = token.decode()

        # 发邮件
        send_register_active_email.delay(email, username, token)

        # 使用反向解析 返回应答, 跳转到首页 
        return redirect(reverse('goods:index'))

类视图

将视图view以类的形式定义
通用类视图基类:
django.views.generic.View ( 与django.views.generic.base.View 是同一个)

urls.py中配置路由使用类视图的as_view()方法
由dispatch()方法具体将请求request分发至对应请求方式的处理方法中(get、post等)

/user/urls.py

from django.conf.urls import url
from user.views import RegisterView, ActiveView, LoginView

urlpatterns = [
    # url(r'^register$', views.register, name='register'), # 注册
    # url(r'^register_handle$', views.register_handle, name='register_handle'), # 注册处理

    url(r'^register$', RegisterView.as_view(), name='register'), # 注册
    url(r'^active/(?P<token>.*)$', ActiveView.as_view(), name='active'), # 用户激活
    url(r'^login$', LoginView.as_view(), name='login'), # 登录
]

扩展阅读:
http://python.usyiyi.cn/translate/django_182/topics/class-based-views/intro.html

类视图资料
http://python.usyiyi.cn/documents/django_182/ref/class-based-views/base.html#django.views.generic.base.View.as_view

激活加密

pip install itdangerous
在这里插入图片描述
在这里插入图片描述

扫描二维码关注公众号,回复: 13204716 查看本文章

配置邮件

Django中内置了邮件发送功能,被定义在django.core.mail模块中。发送邮件需要使用SMTP服务器,常用的免费服务器有:163、126、QQ,下面以163邮件为例。

1)注册163邮箱itcast88,登录后设置。

发送邮件

2)在新页面中点击“客户端授权密码”,勾选“开启”,弹出新窗口填写手机验证码。

发送邮件

3)填写授权码。

发送邮件

4)提示开启成功。
在这里插入图片描述
settings.py

# 发送邮件配置
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
# smpt服务地址
EMAIL_HOST = 'smtp.163.com'
EMAIL_PORT = 25
# 发送邮件的邮箱
EMAIL_HOST_USER = '[email protected]'
# 在邮箱中设置的客户端授权密码
EMAIL_HOST_PASSWORD = 'HVVDJDTIDWBPLFSJ'
# 收件人看到的发件人
EMAIL_FROM = '天天生鲜<[email protected]>'

问题抛出

我们在做网站后端程序开发时,会碰到这样的需求:用户需要在我们的网站填写注册信息,我们发给用户一封注册激活邮件到用户邮箱,如果由于各种原因,这封邮件发送所需时间较长,那么客户端将会等待很久,造成不好的用户体验.

在这里插入图片描述

那么怎么解决这样的问题呢?
在这里插入图片描述

我们将耗时任务放到后台异步执行。不会影响用户其他操作。除了注册功能,例如上传,图形处理等等耗时的任务,都可以按照这种思路来解决。 如何实现异步执行任务呢?我们可使用celery. celery除了刚才所涉及到的异步执行任务之外,还可以实现定时处理某些任务。

celery介绍

Celery是一个功能完备即插即用的任务队列。它使得我们不需要考虑复杂的问题,使用非常简单。celery看起来似乎很庞大,本章节我们先对其进行简单的了解,然后再去学习其他一些高级特性。 celery适用异步处理问题,当发送邮件、或者文件上传, 图像处理等等一些比较耗时的操作,我们可将其异步执行,这样用户不需要等待很久,提高用户体验。 celery的特点是:

  • 简单,易于使用和维护,有丰富的文档。
  • 高效,单个celery进程每分钟可以处理数百万个任务。
  • 灵活,celery中几乎每个部分都可以自定义扩展。
    celery非常易于集成到一些web开发框架中.
    在这里插入图片描述
    新建包存储任务
    在这里插入图片描述
    task.py
# 使用celery
from django.core.mail import send_mail
from django.conf import settings
from celery import Celery
import time

# 在任务处理者一端加这几句
# import os
# import django
# os.environ.setdefault("DJANGO_SETTINGS_MODULE", "dailyfresh.settings")
# django.setup()

# 创建一个Celery类的实例对象
app = Celery('celery_tasks.tasks', broker='redis://192.168.222.129: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)

在这里插入图片描述
在这里插入图片描述
服务器端配置 也需要将代码复制一份
在这里插入图片描述
进入项目根目录下运行(确保虚拟环境包的版本一致)
celery -A celery_tasks.tasks worker -l info
在这里插入图片描述

用户登录逻辑

修改模板文件
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

view.py

# /user/login
class LoginView(View):
    '''登录'''
    def get(self, request):
        '''显示登录页面'''
        # 判断是否记住了用户名
        if 'username' in request.COOKIES:
            username = request.COOKIES.get('username')
            checked = 'checked'
        else:
            username = ''
            checked = ''

        # 使用模板
        return render(request, 'login.html', {
    
    'username':username, 'checked':checked})

    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')) # HttpResponseRedirect

                # 判断是否需要记住用户名
                remember = request.POST.get('remember')

                if remember == 'on':
                    # 记住用户名
                    response.set_cookie('username', username, max_age=7*24*3600)
                else:
                    response.delete_cookie('username')

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


使用Django-redis存储session

django-redis文档:
http://django-redis-chs.readthedocs.io/zh_CN/latest/#

安装

安装 django-redis 最简单的方法就是用 pip :

pip install django-redis

配置

settings.py

# Django的缓存配置
CACHES = {
    
    
    "default": {
    
    
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://192.168.222.130:6379/9",
        "OPTIONS": {
    
    
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
        }
    }
}
# 配置session存储
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
SESSION_CACHE_ALIAS = "default"

测试

重启django服务器
python manage.py runserver
查看redis数据库
redis-cli -h 192.168.222.130

select 9
登录前
192.168.222.130:6379[9]> keys *
(empty list or set)
登录后
192.168.222.130:6379[9]> keys *
1) ":1:django.contrib.sessions.cachejad492bydh3tywan88d8nco6xs03bi66"

抽象父模板

因为很多静态文件中代码有重复的,使用抽象父模板精简代码,挑选一个比较有代表性的页面。
将首页的文件修改为base.html放入templates文件夹内
从往下筛选 页面与其他页面相同的元素进行保留,不同的地方预留块方便进行重写
base.html

{# 首页 注册 登录 #}
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
{% load staticfiles %}
<head>
	<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
    {# 网页标题内容块 #}
	<title>{% block title %}{% endblock title %}</title>
	<link rel="stylesheet" type="text/css" href="{% static 'css/reset.css' %}">
	<link rel="stylesheet" type="text/css" href="{% static 'css/main.css' %}">
	{# 网页顶部引入文件块 #}
    {% block topfiles %}{% endblock topfiles %}
</head>
<body>
{# 网页顶部欢迎信息块 大部分都有 只有登录注册没有 使用块包住 重写时没有的置空就可以了 #}
{% block header_con %}
	<div class="header_con">
		<div class="header">
			<div class="welcome fl">欢迎来到天天生鲜!</div>
			<div class="fr">
                {% if user.is_authenticated %}
				<div class="login_btn fl">
					欢迎您:<em>{
   
   { user.username }}</em>
                    <span>|</span>
					<a href="{% url 'user:logout' %}">退出</a>
				</div>
                {% else %}
				<div class="login_btn fl">
					<a href="{% url 'user:login' %}">登录</a>
					<span>|</span>
					<a href="{% url 'user:register' %}">注册</a>
				</div>
                {% endif %}
				<div class="user_link fl">
					<span>|</span>
					<a href="{% url 'user:user' %}">用户中心</a>
					<span>|</span>
					<a href="cart.html">我的购物车</a>
					<span>|</span>
					<a href="{% url 'user:order' %}">我的订单</a>
				</div>
			</div>
		</div>		
	</div>
{% endblock header_con %}

{# 网页顶部搜索框块 #}
{% block search_bar %}
	<div class="search_bar clearfix">
		<a href="index.html" class="logo fl"><img src="images/logo.png"></a>
		<div class="search_con fl">
			<input type="text" class="input_text fl" name="" placeholder="搜索商品">
			<input type="button" class="input_btn fr" name="" value="搜索">
		</div>
		<div class="guest_cart fr">
			<a href="#" class="cart_name fl">我的购物车</a>
			<div class="goods_count fl" id="show_count">1</div>
		</div>
	</div>
{% endblock search_bar %}

{# 网站主体内容块 都不一致的删除直接预留主体块 #}
{% block body %}{% endblock body %}

	<div class="footer">
		<div class="foot_link">
			<a href="#">关于我们</a>
			<span>|</span>
			<a href="#">联系我们</a>
			<span>|</span>
			<a href="#">招聘人才</a>
			<span>|</span>
			<a href="#">友情链接</a>		
		</div>
		<p>CopyRight © 2016 北京天天生鲜信息技术有限公司 All Rights Reserved</p>
		<p>电话:010-****888    京ICP备*******8号</p>
	</div>
    {# 网页底部html元素块 #}
    {% block bottom %}{% endblock bottom %}
    {# 网页底部引入文件块 #}
	{% block bottomfiles %}{% endblock bottomfiles %}
</body>
</html>

由于搜索框样式不同有多种情况 继承父模板进行重写

base_no_cart.html

{# 购物车 提交订单 #}
{% extends 'base.html' %}
{% load staticfiles %}
{# 网页顶部搜索框块 #}
{% block search_bar %}
	<div class="search_bar clearfix">
		<a href="index.html" class="logo fl"><img src="{% static 'images/logo.png' %}"></a>
		<div class="sub_page_name fl">|&nbsp;&nbsp;&nbsp;&nbsp;{% block page_title %}{% endblock page_title %}</div>
		<div class="search_con fr">
			<input type="text" class="input_text fl" name="" placeholder="搜索商品">
			<input type="button" class="input_btn fr" name="" value="搜索">
		</div>
	</div>
{% endblock search_bar %}

在这里插入图片描述

用户中心的三个页面有共同的地方 可以继承 base_no_cart.html 进行重写
base_user_center.html

{# 用户中心3页面 #}
{% extends 'base_no_cart.html' %}
{% block title %}天天生鲜-用户中心{% endblock title %}
{% block page_title %}用户中心{% endblock page_title %}
{% block body %}
    <div class="main_con clearfix">
		<div class="left_menu_con clearfix">
			<h3>用户中心</h3>
			<ul>
				<li><a href="{% url 'user:user' %}" {% if page == 'user' %}class="active"{% endif %}>· 个人信息</a></li>
				<li><a href="{% url 'user:order' %}" {% if page == 'order' %}class="active"{% endif %}>· 全部订单</a></li>
				<li><a href="{% url 'user:address' %}" {% if page == 'address' %}class="active"{% endif %}>· 收货地址</a></li>
			</ul>
		</div>
        {# 用户中心右侧内容块 #}
        {% block right_content %}{% endblock right_content %}
    </div>
{% endblock body %}

详情页列表页也有共同的地方 ,可以继承Base.html后进行重写
base_detail_list.html

{# 详情页 列表页 #}
{% extends 'base.html' %}
{# 网站主体内容块 #}
{% block body %}
	<div class="navbar_con">
		<div class="navbar clearfix">
			<div class="subnav_con fl">
				<h1>全部商品分类</h1>
				<span></span>
				<ul class="subnav">
					<li><a href="#" class="fruit">新鲜水果</a></li>
					<li><a href="#" class="seafood">海鲜水产</a></li>
					<li><a href="#" class="meet">猪牛羊肉</a></li>
					<li><a href="#" class="egg">禽类蛋品</a></li>
					<li><a href="#" class="vegetables">新鲜蔬菜</a></li>
					<li><a href="#" class="ice">速冻食品</a></li>
				</ul>
			</div>
			<ul class="navlist fl">
				<li><a href="">首页</a></li>
				<li class="interval">|</li>
				<li><a href="">手机生鲜</a></li>
				<li class="interval">|</li>
				<li><a href="">抽奖</a></li>
			</ul>
		</div>
	</div>
    {# 详情页,列表页主体内容块 #}
    {% block main_content %}{% endblock main_content %}
{% endblock body %}

改造之前的注册页面 使用base.html继承 相同的直接删除后继承,不同的进行重写

register.html

{% extends 'base.html' %}
{% load staticfiles %}
{% block title %}天天生鲜-注册{% endblock title %}
{% block topfiles %}
	<script type="text/javascript" src="{% static 'js/jquery-1.12.4.min.js' %}"></script>
	<script type="text/javascript" src="{% static 'js/register.js' %}"></script>
{% endblock topfiles %}
{% block header_con %}{% endblock header_con %}
{% block search_bar %}{% endblock search_bar %}
{% block body %}
	<div class="register_con">
		<div class="l_con fl">
			<a class="reg_logo"><img src="{% static 'images/logo02.png' %}"></a>
			<div class="reg_slogan">足不出户  ·  新鲜每一天</div>
			<div class="reg_banner"></div>
		</div>

		<div class="r_con fr">
			<div class="reg_title clearfix">
				<h1>用户注册</h1>
				<a href="#">登录</a>
			</div>
			<div class="reg_form clearfix">
				<form method="post" action="/user/register">
                    {% csrf_token %}
				<ul>
					<li>
						<label>用户名:</label>
						<input type="text" name="user_name" id="user_name">
						<span class="error_tip">提示信息</span>
					</li>					
					<li>
						<label>密码:</label>
						<input type="password" name="pwd" id="pwd">
						<span class="error_tip">提示信息</span>
					</li>
					<li>
						<label>确认密码:</label>
						<input type="password" name="cpwd" id="cpwd">
						<span class="error_tip">提示信息</span>
					</li>
					<li>
						<label>邮箱:</label>
						<input type="text" name="email" id="email">
						<span class="error_tip">提示信息</span>
					</li>
					<li class="agreement">
						<input type="checkbox" name="allow" id="allow" checked="checked">
						<label>同意”天天生鲜用户使用协议“</label>
						<span class="error_tip2">提示信息</span>
					</li>
					<li class="reg_sub">
						<input type="submit" value="注 册" name="">
					</li>
				</ul>				
				</form>
                {
   
   { errmsg }}
			</div>

		</div>

	</div>
{% endblock body %}

login.html同理
login.html

{% extends 'base.html' %}
{% load staticfiles %}
{% block title %}天天生鲜-登录{% endblock title %}
{% block header_con %}{% endblock header_con %}
{% block search_bar %}{% endblock search_bar %}
{% block body %}
	<div class="login_top clearfix">
		<a href="index.html" class="login_logo"><img src="{% static 'images/logo02.png' %}"></a>
	</div>

	<div class="login_form_bg">
		<div class="login_form_wrap clearfix">
			<div class="login_banner fl"></div>
			<div class="slogan fl">日夜兼程 · 急速送达</div>
			<div class="login_form fr">
				<div class="login_title clearfix">
					<h1>用户登录</h1>
					<a href="#">立即注册</a>
				</div>
				<div class="form_input">
                    {# 不设置表单action时,提交表单时,会向浏览器地址栏中的地址提交数据 #}
					<form method="post">
                        {% csrf_token %}
						<input type="text" name="username" class="name_input" value="{
     
     { username }}" placeholder="请输入用户名">
						<div class="user_error">输入错误</div>
						<input type="password" name="pwd" class="pass_input" placeholder="请输入密码">
						<div class="pwd_error">输入错误</div>
						<div class="more_input clearfix">
							<input type="checkbox" name="remember" {
     
     {
     
      checked }}>
							<label>记住用户名</label>
							<a href="#">忘记密码</a>
						</div>
						<input type="submit" name="" value="登录" class="input_submit">
					</form>
				</div>
			</div>
		</div>
	</div>
    {
   
   { errmsg }}
{% endblock body %}

猜你喜欢

转载自blog.csdn.net/qq_27251475/article/details/120639788