美文网首页
天天生鲜Django(2)

天天生鲜Django(2)

作者: 楚糖的糖 | 来源:发表于2018-10-30 09:09 被阅读0次

    用户注册并向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的邮箱发不出去。

    相关文章

      网友评论

          本文标题:天天生鲜Django(2)

          本文链接:https://www.haomeiwen.com/subject/hmwitqtx.html