用户注册并向163邮箱发送邮件(两种方式:同步和异步,ubuntu和windows上的不同)
************** 三.用户注册功能的实现* ******************
在总项目下新建static/css,images,js,在总项目下新建templates
注册页面出现样式
1将注册页面的html文件放到templates
2.为注册页面写一个View
from django.shortcuts import render
def register(request):
return render(request,"register.html")
3.为这个View配置一个一级路由
在总项目的users下:url(r'^user/', include("user.urls",namespace="user")), #用户模块
4.写二级路由:
from django.conf.urls import url
from user import views
url(r'^register$', views.register,name="register"), #注册
5.让程序运行起来,通过浏览器进行访问127.0.0.1:8000/user/register进行访问
6.修改静态资源的路径
在register.html中的<head>标签上面一行写
{% load staticfiles %} {#修改静态资源的路径#}
然后将register.html中的css,images,js文件以这种方式来修改href="{% static 'css/reset.css' %}"
=====================================================================
1.将register.html中的表单form进行如下修改
<form method="post" action="/user/register_handle">
{% csrf_token %}
2.为此表单的action属性提供views,因为要验证邮箱的合法性用到了正则,所有要导入import re
def register_handle(request):
进行注册处理
接收数据
username = request.POST.get("user_name")
password = request.POST.get("pwd")
email = request.POST.get("email")
allow = request.POST.get("allow") #用户有没有接受协议
进行数据校验
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":"用户名已经存在"})
进行业务处理,进行用户注册
返回应答
3.写二级路由: url(r'^register_handle$',views.register_handle,name="register_handle"), #注册处理
=====================================================================
.进行业务处理,进行用户注册
在views中
from user.models import User
以下两种方法任选其一,最好选用(2)
(1)
在def register_handle(request):
加入进行业务处理,进行用户注册部分,如下写
user = User()
user.username = username
user.password = password
user.email = email
user.save()
(2)使用django自带的认证系统create_user()辅助函数
在def register_handle(request):
加入进行业务处理,进行用户注册,如下写
user = User.objects.create_user(username, email, password)
=======================================================
返回应答:实现注册成功以后,跳转到首页
1.为首页配置View,首页属于商品模块,所以在goods应用的views里面写代码,即在goods\views
中要定义一个index函数
from django.shortcuts import render
def index(request):
return render(request,"index.html")
2.配置goods下的二级路由
from django.conf.urls import url
from goods import views
urlpatterns = [
url(r'^$', views.index,name="index"), #首页
]
3.在user/views中写重定向,和反转
from django.shortcuts import redirect
from django.core.urlresolvers import reverse
进行重定向的时候,想使用反向解析的形式
return redirect(reverse("goods:index"))
反向解析的过程
在总项目的一级路由urls中url(r'^', include("goods.urls",namespace="goods")), #商品模块
在商品的urls中url(r'^$', views.index,name="index"), #首页
运行程序后,打开127.0.0.1:8000/user/register,此时可以实现注册成功,自动跳转到index.html页面中去,注册的数据会出现在数据库里,在mysql数据库中能进行如下查询,select * from df_user \G,
is_active为1表示该用户已经激活了,那如果我不想让其进行激活,在user/view中
进行业务处理,进行用户注册的地方
user = User.objects.create_user(username, email, password)
user.is_active = 0
user.save()
再点击注册,is_active为0
=======================================================
测试一下数据不合法的情况
在register.html下的</form>下面一行这样写
{{ errmsg }}
再次进行注册测试,注册不合法的原因会出现在注册下面
在user/views中要写验证用户名是否重复的代码,可以使用get()方法,它只能返回满足条件的一条记录,且只能有一条的记录,如果查询不到它会报一个异常,所以我们需要try:
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)
==============================================================
==============================================================
上面的方法需要两个url地址才能完成注册,下面这种方法是将显示注册页面和注册处理使用同一个url地址
----------------(注册使用的是get请求,注册处理使用的是post请求)
1.将register.html中的form进行修改
<form method="post" action="/user/register">
2.其次,在user/views中进行if----else的请求判断,if requests.method =="GET"则返回return render(request,"register.html")否则else:进行注册处理,和数据接收校验
def register(request):
if request.method == "GET":
return render(request,"register.html")
else:
# def register_handle(request):
#这里是进行注册处理
# 1.接收数据
username = request.POST.get("user_name")
password = request.POST.get("pwd")
email = request.POST.get("email")
allow = request.POST.get("allow")
# 2.进行数据校验
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":"用户名已经存在"})
# 3.业务处理,进行用户注册
# user = User()
# user.username = username
# user.password = password
# user.email = email
# user.save()
user = User.objects.create_user(username, email, password)
# 不想让其进行激活
# user.is_active = 0
# user.save()
# 4.返回应答
return redirect(reverse("goods:index"))
==================================================================
类视图的使用:使用一个特定的函数提供服务,并且具有一个特定的模板,django使用叫做‘URLconfs’的配置来为URL匹配视图。 一个URLconf负责使用正则表达式将URL模式匹配到视图。
from django.views.generic import View
from django.core.urlresolvers import reverse
from django.shortcuts import render, redirect
一.定义一个视图和函数
class RegisterView(View):
def get(self,request):
if request.method =="GET":
return render(request,"register.html")
def post(self,request):
1.接收数据
username = request.POST.get("user_name")
password = request.POST.get("pwd")
email = request.POST.get("email")
allow = request.POST.get("allow")
print(allow)
2.进行数据校验
3.业务处理,进行用户注册
4.返回应答
二.修改一个新的视图的路由配置:
因为已经定义好新的视图RegisterView了,在user/urls里面将注册的路由register和register_handle的路由注释掉,写入新的路由,先导入from user.views import RegisterView
再url(r'^register$',RegisterView.as_view(),name="register"), #注册与注册处理
=======================================================================
到该步骤即可实现注册,判断注册的是否合法,并在注册成功后实现跳转到首页,
此时注册页面的网址是http://127.0.0.1:8000/user/register
跳转到首页的网址是http://127.0.0.1:8000/
*********** 四.激活注册的用户并实现同步登陆 *************
因为在user/view中
进行业务处理,进行用户注册的地方
user = User.objects.create_user(username, email, password)
user.is_active = 0
user.save()
点击注册,is_active为0
那么用户属于尚未激活,激活用户的办法:打算使用将登陆网址发送邮件给注册了的用户,用户在邮箱点击进入登陆页面,该用户才能被激活,登陆成功后,才进入首页,该方法具有一定的安全性。
在发送激活邮件的时候,包含的激活链接:http://127.0.0.1:8000/user/active/3 其中3为注册的用户在mysql数据库中保存的用户id,由于在激活的链接中需要包含用户的身份信息,所以要先进行身份信息加密后,再进行链接的发送。
加密用到的模块(http://itsdangerous.readthedocs.io/en/latest/)
首先安装pip install itsdangerous
然后在user/views里面from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
TimedJSONWebSignatureSerializer这个方法就是用来进行加密和解密的,使用Serializer()创建对象,需要填写两个参数(1)secretkey是加密的密钥,(2)过期时间是3600,单位是秒,也就是一个小时
django项目自带一个secret_key,位于总项目的setting下面,可以使用这个key作为加密的密钥,也可以自己去设置,SECRET_KEY = '9yfj@mf=&xw#6&2wxz&dq=dgxo=37i=(riiv!ujiv%2tsi!#%!'
1.在user/views里面
from django.conf import settings
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
2.在进行业务处理,进行用户注册的地方进行加密生成新的token
user = User.objects.create_user(username, email, password)
user.is_active = 0
user.save()
加密用户的身份信息,生成激活的token,
serializer=Serializer(settings.SECRET_KEY,3600) #密钥,过期时间一个小时
info ={"confirm":user.id} #定义一个要加密的字典对象
token = serializer.dumps(info) # dumps()方法是进行加密的,返回值就是加密后的内容(想要解密的话serializer.loads(token))
token =token.decode("utf8") #进行解码:默认使用的是utf8,“utf8”可以省略
.
.
.
.
# 返回应答,跳转到首页
return redirect(reverse("goods:index"))
====================================================================
发送邮件的过程
1.为激活的链接地址配置View:
先在user/view里面导入
from django.http import HttpResponse
from itsdangerous import SignatureExpired
class ActiveView(View):
def get(self,request,token):
# 进行用户激活
# 解密,获取激活的用户信息
serializer = Serializer(settings.SECRET_KEY,3600)
try:
info=serializer.loads(token)
user_id = info["confirm"]
user = User.objects.get(id=user_id)
user.is_active = 1
user.save()
return redirect(reverse("user:login"))
except SignatureExpired as e:
return HttpResponse("激活链接已经过期")
2.配置新的路由,用于用户激活,user/views里面
url(r'^active/(?P<token>.*)$',ActiveView.as_view(),name="active"), #用户激活
此时在该文件里面导入了两个视图,from user.views import RegisterView,ActiveView
==============================================================
1.在用户激活后需要进行登陆,所以还要配置一个关于登陆的路由
先在user/views里面写一个关于登陆的路由
class LoginView(View):
def get(self,request):
return render(request,"login.html")
2.在用户二级路由下,
from user.views import LoginView
url(r'^login$',LoginView.as_view(),name="login"), #登陆
注意:实际在做一个项目的时候,如果激活链接已经过期,应该再返回一个页面,告诉你激活链接已经过期了,再单击一个什么按钮的,再发送一个。
=====================================================================
django中内置了邮件发送功能,被定义在django.core.mail模块中。
需要使用SMTP服务器
163邮件为例:开启POP3/SMTP/IMAP客户端授权密码
用户名为:chushang1220525352,密码: ,授权码为:dailyfresh123456
1.打开settings.py文件进行配置:
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST ="smtp.163.com"
EMAIL_PORT = 25
EMAIL_HOST_USER = "chushang1220525352@163.com"
EMAIL_HOST_PASSWORD = "dailyfresh123456"
EMAIL_FROM = "叶良臣chushang1220525352@163.com"
======================================================================
同步的方式:
1.在user/views里面导入发邮件的包
from django.core.mail import send_mail
在token =token.decode("utf8")下面一行对齐的代码
# 发邮件
subject = "天天都有好吃的,天天生鲜欢迎你的到来"
message = ""
html_message = "<h1>%s你好,欢迎%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,username,token,token)
sender = settings.EMAIL_FROM
receiver = [email]
#time.sleep(10)
send_mail(subject,message,sender,receiver,html_message=html_message)
##由于html_message不是 send_mail自带的参数,故而使用默认值参数的方法进行传递,html_message这个参数是用户自己命名的,可以更换
return redirect(reverse("goods:index"))
=============================================================
在windows虚拟环境中python manage.py runserver
运行项目后打开http://127.0.0.1:8000/user/register
进行按条件注册,注册成功后,django会发送一封邮件到163邮箱中去,在邮箱中点击这封邮件的里的链接,进入到http://127.0.0.1:8000/user/login界面,将之前注册的那个账户和密码进行登陆,登陆成功后自动跳转到首页。
该同步的方法存在的问题:send_mail()是堵塞的情况下进行发送的,如果还没有发送成功的时候,就会一直进行堵塞的。这样就会造成用户长时间的等待,用户体验不好。
********* 四.celery异步发送邮件的问题 *******************
send_mail()方法是将邮件发送到smtp服务器,然后,smtp服务器将邮件发送到目的邮箱,时间是不固定
这种情况类似于在send_mail()后面加了个time.sleep()的方法,用户体验是非常不好------------------解决办法---------使用celery
celery是一个功能完备即插即用的任务队列,任务队列是一种跨线程、跨机器工作的一种机制。
celery特点是:简单灵活高效
celery通过消息进行通信,通常使用一个叫Broker(中间人)来协client(任务的发出者)和worker(任务的处理者)。clients发出消息到队列中,broker将队列中的信息派发给worker来处理。
作为中间人有种方案可选择:RabbitMQ、Redis。在这个项目中使用redis作为中间人。
celery的安装在windows的虚拟环境项目里面pip install celery
使用redis作为中间人,要先查看redis是否已经启动,在ubuntu里面,查看redis的进程 ps -aux | grep redis
并ifconfig来查看ubuntu此时的地址inet :192.168.XXX.XXX
给任务发出者安装redis,在windows的虚拟环境里面,pip install redis
======================================================================
在总的项目下面创建一个celery_tasks/tasks.py的文件,在这个文件里面
1.创建celery()对象:
from celery import Celery
app = Celery("celery_tasks.tasks",broker="redis://192.168.XXX.XXX:6379/8") #第一个参数是所在包的名字,第二个 是指定中间人,8代表所使用的数据库的编号。
app = Celery("celery_tasks.tasks",broker="redis://127.0.0.1:6379/7") #用于在windows上的redis进行链接
2.发送邮件的代码
在celery_tasks/tasks.py的文件里面加上
from django.core.mail import send_mail
from django.conf import settings
import time
@app.task
def send_register_active_email(to_email,username,token):
subject = "天天都有好吃的,天天生鲜欢迎你的到来"
message = ""
html_message = "<h1>%s你好,欢迎%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, username, token, token)
sender = settings.EMAIL_FROM
receiver = [to_email]
#time.sleep(10)
send_mail(subject, message, sender, receiver, html_message=html_message)
time.sleep(10)
3.在user/views里面
from celery_tasks.tasks import send_register_active_email
因为此时在tasks里面已经有发送邮件的代码了,我们可以将user/views里面的发送邮件的代码注释掉
在加密用户身份信息,生成激活token,token =token.decode("utf8")后面只留下
send_register_active_email.delay(email, username, token)
# 返回应答,跳转到首页
return redirect(reverse("goods:index"))
=============================================================
在linux里面直接执行celery会报错:
原因:是因为我们先启动worker的,后启动项目的,那worker中需要用到settings文件中的配置,所以报错了。那启动项目的时候为什么不报错呢,原因是因为,Django已经给我们做了初始化的工作,总项目wsgi代码在下面:
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "dailyfresh1807.settings") #初始化的工作
application = get_wsgi_application()
解决办法:将wsgi代码初始化工作的代码复制在celery_tasks/tasks.py的文件里面
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "dailyfresh1807.settings")
==================================================================
任务的处理者-----------------方法一:开启ubuntu
1.在windows的虚拟环境的项目里面pip freeze > requirements.txt
该创建好的文件位于项目下面
2.使用ftp将该总的项目传到ubuntu的桌面,打开linux的虚拟环境,在项目下pip install -r requirements.txt
3.执行celery,celery -A celery_tasks.tasks worker -l info
方法二:使用windows的redis
1.确认windows虚拟环境中的redis已经开启,celery_tasks/tasks.py的文件里面
app = Celery("celery_tasks.tasks",broker="redis://127.0.0.1:6379/7")
2.其次还要在windows的虚拟环境中安装eventlet,pip install eventlet
3.在cmd的项目下celery -A celery_tasks.tasks worker -l info -P eventlet
===============================================================
有时会出现数据库的密码错误问题,原因是有的用户的windows数据库和linux的数据库密码不一致
解决方法有两种:
① 让项目使用linux系统上的mysql数据库,并修改配置文件中数据库的配置,而且还在Linux系统中创建项目所需要的数据库,重新生成迁移文件,并通过迁移文件生成表。
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': "dailyfresh1807",
'USER': "root",
'PASSWORD': "root",
'HOST': "192.168.xxx.xxx",
# 'HOST': "127.0.0.1",
'PORT': "3306",
}
}
② 让任务的处理者与任务的发出者在同一台电脑上,也就是都是windows电脑,并修改redis的链接代码。
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': "dailyfresh1807",
'USER': "root",
'PASSWORD': "root",
# 'HOST': "192.168.xxx.xxx",
'HOST': "127.0.0.1",
'PORT': "3306",
}
}
=============================================================
对数据库的表里的字段进行修改的话,要重新生成迁移文件,并执行迁移文件,生成新的表
查看是否已经被修改了,例如select * from df_user \G
==============================================================
如果任务的发出者,中间人还有任务的处理者,不在同一台电脑上的话,这三者之间必须要能进行通信的,也就是说需要在同一个网段,而且处理者所在的电脑必须要能上网,否则它也给163的邮箱发不出去。
网友评论