前置准备
将前端写好的静态页面放入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
配置邮件
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">| {% 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 %}