天天生鲜Django(2)

用户注册并向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中的标签上面一行写

{% load staticfiles %} {#修改静态资源的路径#}

然后将register.html中的css,images,js文件以这种方式来修改href="{% static 'css/reset.css' %}"

=====================================================================

1.将register.html中的表单form进行如下修改

{% 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下的

下面一行这样写

{{ 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进行修改

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.*)$',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 = "[email protected]"

EMAIL_HOST_PASSWORD = "dailyfresh123456"

EMAIL_FROM = "叶良臣[email protected]"

======================================================================

同步的方式:

1.在user/views里面导入发邮件的包

from django.core.mail import send_mail

在token =token.decode("utf8")下面一行对齐的代码

# 发邮件

    subject = "天天都有好吃的,天天生鲜欢迎你的到来"

    message = ""

    html_message = "

%s你好,欢迎%s小朋友来注册会员

请点击下面的链接激活你的账户
http://127.0.0.1:8000/user/active/%s"%(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 = "

%s你好,欢迎%s小朋友来注册会员

请点击下面的链接激活你的账户
http://127.0.0.1:8000/user/active/%s" % ( 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的邮箱发不出去。

你可能感兴趣的:(天天生鲜Django(2))