02 用户注册通过发送邮箱激活
配置静态文件
在项目根目录下创建静态文件static目录,用于放置静态的文件
在settings 文件中定义静态内容
STATIC_URL = '/static/' STATICFILES_DIRS = [ os.path.join(BASE_DIR, 'static'), ]
把静态的文件如css,js,image放入static目录中:
把当前关于注册用到的模板放到模板文件中,
在应用users中 views视图定义处理注册的请求函数,返回注册的页面:
类视图:http://python.usyiyi.cn/translate/django_182/topics/class-based-views/intro.html
# 导入类视图,主要给urls from django.views.generic import View class RegisterView(View): """注册""" def get(self, request): """对应get请求方式,提供注册页面""" return render(request, "register.html", )
在根级urls中配置到应用users的跳转路径
import users.urls url(r'^users/', include(users.urls, namespace="users")),
在uers.urls.py中配置请求路径
from django.conf.urls import url from . import views urlpatterns = [ url(r'^register$', views.RegisterView.as_view(), name="register"), ]
在浏览器地址栏中输入:
http://127.0.0.1:8000/users/register
返回的页面如下所示:
注册页中的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"> <head> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"> <title>天天生鲜-注册</title> <link rel="stylesheet" type="text/css" href="../static/css/reset.css"> <link rel="stylesheet" type="text/css" href="../static/css/main.css"> <script type="text/javascript" src="../static/js/jquery-1.12.4.min.js"></script> <script type="text/javascript" src="../static/js/register.js"></script> </head> <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"> {% 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> {{ errmsg }} </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> </div> </div> </div> <div class="footer no-mp"> <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> </body> </html>
用户提交表单的时候接受发送的post请求,由于表单中action表单的地址没写,所以还在原来的请求地址和视图函数中处理,只不过是post的请求而已,
在视图函数中,添加post请求处理的函数:
from django.core.urlresolvers import reverse from django import db from .models import * import re def post(self, request): """对应post请求方式,接收处理用户的注册数据""" # 接收传入的参数 user_name = request.POST.get("user_name") password = request.POST.get("pwd") email = request.POST.get("email") allow = request.POST.get("allow") # 检验参数的正确性 if not all([user_name, password, email]): # 重定向到注册页面 return redirect(reverse("users:register")) 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: # django的AbstractUser基类提供的创建用户的方法 user = User.objects.create_user(user_name, email, password) except db.IntegrityError: # 如果用户名已存在,则抛出此异常信息 return render(request, "register.html", {"errmsg": "用户名已存在!"}) # 将用户的激活状态设置为假 user.is_active = False user.save() # 将结果返回给前端 return redirect(reverse("goods:index"))
在根基目录的urls中,构造商品应用的请求地址
import goods.urls urlpatterns = [ url(r'^', include(goods.urls, namespace='goods')), ]
在商品的views中,构造商品主页的请求函数
from django.shortcuts import render from django.views.generic import View class IndexView(View): def get(self,request): return render(request,'index.html')
在应用goods中的urls中,构造商品主页的请求地址
from django.conf.urls import url from goods import views urlpatterns = [ url(r"^$", views.IndexView.as_view(), name="index"), ]
注册成功在数据库中的查询状态:
使用celery实现异步调用django的发送邮件的模块,让用户激活自己的账号.
主要思想是,先生成一个唯一的口令用来表示用户,通过django中的(itsdangerous模块),在使用celery服务器实现异步发送邮件.
1 根据用户的ID生成唯一的口令:
在用户模模型类中,创建生成唯一口令的方法:
用户生成注册口令文档 http://itsdangerous.readthedocs.io/en/latest/
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer class User(AbstractUser, BaseModel): """用户""" class Meta: db_table = "df_users" def generate_active_token(self): """生成激活令牌""" # 构建序列化器(转换工具)对象 serializer = Serializer(settings.SECRET_KEY, 3600) # 转换参数 token = serializer.dumps({"confirm": self.id}) # 返回bytes类型 return token.decode()
2 在项目根目录下创建celery_tasks的包用来存储用celery发送任务的模块,在包中创建tasks.py模块其内容为:
from celery import Celery import os #把jiango的配置模型放到系统的环境变量中,celery才可以调用django的模块 os.environ["DJANGO_SETTINGS_MODULE"] = "dailyfresh_13.settings" # 放到celery服务器上时将注释打开 # import django # django.setup() from django.core.mail import send_mail from django.conf import settings # 创建celery应用对象 app = Celery("celery_tasks.tasks", broker="redis://10.211.55.5/2") # 定义任务 @app.task def send_active_email(user_name, to_email, token): """发送激活邮件""" subject = "天天生鲜用户激活" # 邮件标题 body = "" # 邮件体 sender = settings.EMAIL_FROM # 发件人 receivers = [to_email] # 接收人 html_body = '<h1>尊敬的用户 %s, 感谢您注册天天生鲜!</h1>' \ '<br/><p>请点击此链接激活您的帐号<a href="http://127.0.0.1:8000/users/active/%s">' \ 'http://127.0.0.1:8000/users/active/%s<a></p>' % (user_name, token, token) # html邮件体 send_mail(subject, body, sender, receivers, html_message=html_body)
3 调用celery任务入到broker队列中,以便刚才我们创建的celery workder服务器能够从队列中取出任务并执行。如何将任务函数加入到队列中,可使用delay()
在用户注册的post请求处理函数中,将发送邮件的任务函数加入到队列中,
把下面的两行代码加到 class RegisterView(View):的post请求函数中
from celery_tasks.tasks import send_active_email # 为用户生成激活口令 token = user.generate_active_token() # 使用celery异步发送邮件 send_active_email.delay(user_name, email, token)
4 打开项目的settings.py文件,配置Email:
# Email EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' EMAIL_HOST = 'smtp.126.com' EMAIL_PORT = 25 #发送邮件的邮箱 EMAIL_HOST_USER = 'daily_fresh@126.com' #在邮箱中设置的客户端授权密码 EMAIL_HOST_PASSWORD = 'ITCAST123' #收件人看到的发件人 EMAIL_FROM = '天天生鲜<daily_fresh@126.com>'
5 处理用户激活的请求:
定义用户激活的请求的处理函数:
# 口令过期 from itsdangerous import SignatureExpired # 定义了激活的接口 class ActiveView(View): """激活""" def get(self, request, token): """ :param request: :param token: token是用户携带的口令,唯一标识用户 :return: """ # 解析口令token,获取用户身份 # 构建序列化器 s = Serializer(settings.SECRET_KEY) try: data = s.loads(token) except SignatureExpired: # 表示token过期 return HttpResponse("链接已过期!") # 表示token未过期, user_id = data.get("confirm") # 查询用户的数据.处理bug try: user = User.objects.get(id=user_id) except User.DoesNotExist: # 用户不存在 return HttpResponse("用户不存在!") # 设置用户的激活状态 user.is_active = True user.save() # 返回处理结果 return HttpResponse("ok") # return redirect(reverse("users:login"))
6 配置用户激活请求的url:
url(r'^active/(?P<token>.+)', views.ActiveView.as_view(), name="active"),
5 .打开终端,执行命令:
celery -A celery_tasks.tasks worker --loglevel=info
用户dsa提交表单:
celery发送邮件成功:
用户dsa接受到邮件点击激活后,查询数据库中的is_active的状态为1,激活成功:
定义用户登陆请求的处理函数:
Django认证系统文档 http://python.usyiyi.cn/documents/django_182/topics/auth/default.html
from django.contrib.auth import authenticate, login class LoginView(View): """登录""" def get(self, request): """提供登录页面""" return render(request, "login.html") def post(self, request): """处理登录的数据""" # 获取参数 user_name = request.POST.get("username") password = request.POST.get("pwd") # 参数校验 if not all([user_name, password]): # 参数不完整 return render(request, "login.html") # 登录业务逻辑处理 # try: # password = sha256(password) # User.objects.get(username=user_name, password=password) # except User.DoesNotExist: # return HttpResponse("用户名或密码错误") # 使用django的认证系统进行用户密码的校验 user = authenticate(username=user_name, password=password) if user is None: # 用户的登录信息有误 return render(request, "login.html", {"errmsg": "用户名或密码错误!"}) # 判断用户的激活状态 if user.is_active is False: return render(request, "login.html", {"errmsg": "用户尚未激活!"}) # 保存用户的登录状态 # 使用django的login函数保存用户的session数据 login(request, user) # 登录成功,跳转到主页 return HttpResponse('登陆成功') #return redirect(reverse("goods:index"))
在浏览器输入以下的网址:
http://127.0.0.1:8000/users/login
用户和激活的状态判断成功后,显示以下的页面:
配置用户登陆请求的url:
url(r'^login$', views.LoginView.as_view(), name="login"),
======================================================================================================
用户登陆成功,返回主页的请求处理函数,这个请求处理函数写在goods应用中的views函数中:
from django.shortcuts import render from django.views.generic import View # Create your views here. class IndexView(View): """主页""" def get(self, request): return render(request, "index.html")
定义用户登陆主页的请求路径:
现在根据的请求路径urls中,定义再跳到goods应用中的urls
在根基定义跳转的路径:
url(r'^/', include(goods.urls, namespace="goods")),
在goods应用中的urls模块中写请求登陆主页的路径:
from django.conf.urls import url from goods import views urlpatterns = [ url(r"^$", views.IndexView.as_view(), name="index"), ]
在用户登陆处理函数login中把最后的登陆成功的路径跳转到主页
# return HttpResponse('登陆成功') return redirect(reverse('goods:index'))
点击登陆,认证成功后,返回以下的页面:
============================================================
把session和缓存储在redis数据库中,在setings中配置代码以下:
pip install django-redis
django-redis文档:http://django-redis-chs.readthedocs.io/zh_CN/latest/#
django的session使用文档:http://python.usyiyi.cn/documents/django_182/topics/http/sessions.html
CACHES = { "default": { "BACKEND": "django_redis.cache.RedisCache", "LOCATION": "redis://localhost/3", "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient", } } } # Session # http://django-redis-chs.readthedocs.io/zh_CN/latest/#session-backend SESSION_ENGINE = "django.contrib.sessions.backends.cache" SESSION_CACHE_ALIAS = "default"
用户登陆成功后,在redis数据库中的查询的结果如下: