天天生鮮Django(2)

用戶注冊并向163郵箱發(fā)送郵件(兩種方式:同步和異步,ubuntu和windows上的不同)


************** 三.用戶注冊功能的實(shí)現(xiàn)* ******************


在總項(xiàng)目下新建static/css,images,js,在總項(xiàng)目下新建templates

注冊頁面出現(xiàn)樣式

1將注冊頁面的html文件放到templates

2.為注冊頁面寫一個(gè)View

from django.shortcuts import render

def register(request):

return render(request,"register.html")

3.為這個(gè)View配置一個(gè)一級路由

在總項(xiàng)目的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.讓程序運(yùn)行起來,通過瀏覽器進(jìn)行訪問127.0.0.1:8000/user/register進(jìn)行訪問

6.修改靜態(tài)資源的路徑

在register.html中的<head>標(biāo)簽上面一行寫

{% load staticfiles %} {#修改靜態(tài)資源的路徑#}

然后將register.html中的css,images,js文件以這種方式來修改href="{% static 'css/reset.css' %}"

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

1.將register.html中的表單form進(jìn)行如下修改

<form method="post" action="/user/register_handle">

{% csrf_token %}

2.為此表單的action屬性提供views,因?yàn)橐?yàn)證郵箱的合法性用到了正則,所有要導(dǎo)入import re

def register_handle(request):

進(jìn)行注冊處理

接收數(shù)據(jù)

    username = request.POST.get("user_name")

    password = request.POST.get("pwd")

    email = request.POST.get("email")

    allow = request.POST.get("allow")        #用戶有沒有接受協(xié)議

進(jìn)行數(shù)據(jù)校驗(yàn)

  if not all([username,password,email]):

        return render(request,"register.html",{"errmsg":"數(shù)據(jù)不完整"})

    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':'請同意協(xié)議'})

    try:

        user = User.objects.get(username = username)

    except User.DoesNotExist:

        user = None

    if user:

        return render(request,"register.html",{"errmsg":"用戶名已經(jīng)存在"})

進(jìn)行業(yè)務(wù)處理,進(jìn)行用戶注冊

返回應(yīng)答

3.寫二級路由: url(r'^register_handle$',views.register_handle,name="register_handle"), #注冊處理

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

.進(jìn)行業(yè)務(wù)處理,進(jìn)行用戶注冊

在views中

from user.models import User

以下兩種方法任選其一,最好選用(2)

(1)

在def register_handle(request):

加入進(jìn)行業(yè)務(wù)處理,進(jìn)行用戶注冊部分,如下寫

    user = User()

    user.username = username

    user.password = password

    user.email = email

    user.save()

(2)使用django自帶的認(rèn)證系統(tǒng)create_user()輔助函數(shù)

在def register_handle(request): 

加入進(jìn)行業(yè)務(wù)處理,進(jìn)行用戶注冊,如下寫

user = User.objects.create_user(username, email, password)

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

返回應(yīng)答:實(shí)現(xiàn)注冊成功以后,跳轉(zhuǎn)到首頁

1.為首頁配置View,首頁屬于商品模塊,所以在goods應(yīng)用的views里面寫代碼,即在goods\views

中要定義一個(gè)index函數(shù)

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中寫重定向,和反轉(zhuǎn)

from django.shortcuts import redirect

from django.core.urlresolvers import reverse

進(jìn)行重定向的時(shí)候,想使用反向解析的形式

return redirect(reverse("goods:index"))

反向解析的過程

在總項(xiàng)目的一級路由urls中url(r'^', include("goods.urls",namespace="goods")), #商品模塊

在商品的urls中url(r'^$', views.index,name="index"), #首頁

運(yùn)行程序后,打開127.0.0.1:8000/user/register,此時(shí)可以實(shí)現(xiàn)注冊成功,自動跳轉(zhuǎn)到index.html頁面中去,注冊的數(shù)據(jù)會出現(xiàn)在數(shù)據(jù)庫里,在mysql數(shù)據(jù)庫中能進(jìn)行如下查詢,select * from df_user \G,

is_active為1表示該用戶已經(jīng)激活了,那如果我不想讓其進(jìn)行激活,在user/view中

進(jìn)行業(yè)務(wù)處理,進(jìn)行用戶注冊的地方

user = User.objects.create_user(username, email, password)

user.is_active = 0

user.save()

再點(diǎn)擊注冊,is_active為0

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

測試一下數(shù)據(jù)不合法的情況

在register.html下的</form>下面一行這樣寫

{{ errmsg }}

再次進(jìn)行注冊測試,注冊不合法的原因會出現(xiàn)在注冊下面

在user/views中要寫驗(yàn)證用戶名是否重復(fù)的代碼,可以使用get()方法,它只能返回滿足條件的一條記錄,且只能有一條的記錄,如果查詢不到它會報(bào)一個(gè)異常,所以我們需要try:

    try:

        user = User.objects.get(username = username)

    except User.DoesNotExist:

        user = None

    if user:

        return render(request,"register.html",{"errmsg":"用戶名已經(jīng)存在"})

    user = User.objects.create_user(username, email, password)

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

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

上面的方法需要兩個(gè)url地址才能完成注冊,下面這種方法是將顯示注冊頁面和注冊處理使用同一個(gè)url地址

----------------(注冊使用的是get請求,注冊處理使用的是post請求)

1.將register.html中的form進(jìn)行修改

<form method="post" action="/user/register">

2.其次,在user/views中進(jìn)行if----else的請求判斷,if requests.method =="GET"則返回return render(request,"register.html")否則else:進(jìn)行注冊處理,和數(shù)據(jù)接收校驗(yàn)

def register(request):

if request.method == "GET":

    return render(request,"register.html")

else:

    # def register_handle(request):

        #這里是進(jìn)行注冊處理

    # 1.接收數(shù)據(jù)

    username = request.POST.get("user_name")

    password = request.POST.get("pwd")

    email = request.POST.get("email")

    allow = request.POST.get("allow")

# 2.進(jìn)行數(shù)據(jù)校驗(yàn)

    if not all([username,password,email]):

        return render(request,"register.html",{"errmsg":"數(shù)據(jù)不完整"})

    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':'請同意協(xié)議'})

    try:

        user = User.objects.get(username = username)

    except User.DoesNotExist:

        user = None

    if user:

        return render(request,"register.html",{"errmsg":"用戶名已經(jīng)存在"})

    # 3.業(yè)務(wù)處理,進(jìn)行用戶注冊

    # user = User()

    # user.username = username

    # user.password = password

    # user.email = email

    # user.save()

    user = User.objects.create_user(username, email, password)

    # 不想讓其進(jìn)行激活

    # user.is_active = 0

    # user.save()

# 4.返回應(yīng)答

    return redirect(reverse("goods:index"))

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

類視圖的使用:使用一個(gè)特定的函數(shù)提供服務(wù),并且具有一個(gè)特定的模板,django使用叫做‘URLconfs’的配置來為URL匹配視圖。 一個(gè)URLconf負(fù)責(zé)使用正則表達(dá)式將URL模式匹配到視圖。

from django.views.generic import View

from django.core.urlresolvers import reverse

from django.shortcuts import render, redirect

一.定義一個(gè)視圖和函數(shù)

class RegisterView(View):

def get(self,request):

    if request.method =="GET":

        return render(request,"register.html")

def post(self,request):

1.接收數(shù)據(jù)

    username = request.POST.get("user_name")

    password = request.POST.get("pwd")

    email = request.POST.get("email")

    allow = request.POST.get("allow")

    print(allow)

2.進(jìn)行數(shù)據(jù)校驗(yàn)

3.業(yè)務(wù)處理,進(jìn)行用戶注冊

4.返回應(yīng)答

二.修改一個(gè)新的視圖的路由配置:

因?yàn)橐呀?jīng)定義好新的視圖RegisterView了,在user/urls里面將注冊的路由register和register_handle的路由注釋掉,寫入新的路由,先導(dǎo)入from user.views import RegisterView

再url(r'^register$',RegisterView.as_view(),name="register"), #注冊與注冊處理

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

到該步驟即可實(shí)現(xiàn)注冊,判斷注冊的是否合法,并在注冊成功后實(shí)現(xiàn)跳轉(zhuǎn)到首頁,

此時(shí)注冊頁面的網(wǎng)址是http://127.0.0.1:8000/user/register

跳轉(zhuǎn)到首頁的網(wǎng)址是http://127.0.0.1:8000/


*********** 四.激活注冊的用戶并實(shí)現(xiàn)同步登陸 *************


因?yàn)樵趗ser/view中

進(jìn)行業(yè)務(wù)處理,進(jìn)行用戶注冊的地方

user = User.objects.create_user(username, email, password)

user.is_active = 0

user.save()

點(diǎn)擊注冊,is_active為0

那么用戶屬于尚未激活,激活用戶的辦法:打算使用將登陸網(wǎng)址發(fā)送郵件給注冊了的用戶,用戶在郵箱點(diǎn)擊進(jìn)入登陸頁面,該用戶才能被激活,登陸成功后,才進(jìn)入首頁,該方法具有一定的安全性。

在發(fā)送激活郵件的時(shí)候,包含的激活鏈接:http://127.0.0.1:8000/user/active/3 其中3為注冊的用戶在mysql數(shù)據(jù)庫中保存的用戶id,由于在激活的鏈接中需要包含用戶的身份信息,所以要先進(jìn)行身份信息加密后,再進(jìn)行鏈接的發(fā)送。

加密用到的模塊(http://itsdangerous.readthedocs.io/en/latest/

首先安裝pip install itsdangerous

然后在user/views里面from itsdangerous import TimedJSONWebSignatureSerializer as Serializer

TimedJSONWebSignatureSerializer這個(gè)方法就是用來進(jìn)行加密和解密的,使用Serializer()創(chuàng)建對象,需要填寫兩個(gè)參數(shù)(1)secretkey是加密的密鑰,(2)過期時(shí)間是3600,單位是秒,也就是一個(gè)小時(shí)

django項(xiàng)目自帶一個(gè)secret_key,位于總項(xiàng)目的setting下面,可以使用這個(gè)key作為加密的密鑰,也可以自己去設(shè)置,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.在進(jìn)行業(yè)務(wù)處理,進(jìn)行用戶注冊的地方進(jìn)行加密生成新的token

  user = User.objects.create_user(username, email, password)

    user.is_active = 0

    user.save()

加密用戶的身份信息,生成激活的token,

    serializer=Serializer(settings.SECRET_KEY,3600)    #密鑰,過期時(shí)間一個(gè)小時(shí)

    info ={"confirm":user.id}    #定義一個(gè)要加密的字典對象

    token = serializer.dumps(info)    # dumps()方法是進(jìn)行加密的,返回值就是加密后的內(nèi)容(想要解密的話serializer.loads(token))

    token =token.decode("utf8")    #進(jìn)行解碼:默認(rèn)使用的是utf8,“utf8”可以省略

.

.

.

.

    # 返回應(yīng)答,跳轉(zhuǎn)到首頁

    return redirect(reverse("goods:index"))

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

發(fā)送郵件的過程

1.為激活的鏈接地址配置View:

先在user/view里面導(dǎo)入

from django.http import HttpResponse

from itsdangerous import SignatureExpired

class ActiveView(View):

def get(self,request,token):

    # 進(jìn)行用戶激活

    # 解密,獲取激活的用戶信息

    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("激活鏈接已經(jīng)過期")

2.配置新的路由,用于用戶激活,user/views里面

url(r'^active/(?P<token>.*)$',ActiveView.as_view(),name="active"), #用戶激活

此時(shí)在該文件里面導(dǎo)入了兩個(gè)視圖,from user.views import RegisterView,ActiveView

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

1.在用戶激活后需要進(jìn)行登陸,所以還要配置一個(gè)關(guān)于登陸的路由

先在user/views里面寫一個(gè)關(guān)于登陸的路由

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"), #登陸

注意:實(shí)際在做一個(gè)項(xiàng)目的時(shí)候,如果激活鏈接已經(jīng)過期,應(yīng)該再返回一個(gè)頁面,告訴你激活鏈接已經(jīng)過期了,再單擊一個(gè)什么按鈕的,再發(fā)送一個(gè)。

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

django中內(nèi)置了郵件發(fā)送功能,被定義在django.core.mail模塊中。

需要使用SMTP服務(wù)器

163郵件為例:開啟POP3/SMTP/IMAP客戶端授權(quán)密碼

用戶名為:chushang1220525352,密碼: ,授權(quán)碼為:dailyfresh123456

1.打開settings.py文件進(jìn)行配置:

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里面導(dǎo)入發(fā)郵件的包

from django.core.mail import send_mail

在token =token.decode("utf8")下面一行對齊的代碼

# 發(fā)郵件

    subject = "天天都有好吃的,天天生鮮歡迎你的到來"

    message = ""

    html_message = "<h1>%s你好,歡迎%s小朋友來注冊會員</h1>請點(diǎn)擊下面的鏈接激活你的賬戶<br><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自帶的參數(shù),故而使用默認(rèn)值參數(shù)的方法進(jìn)行傳遞,html_message這個(gè)參數(shù)是用戶自己命名的,可以更換

    return redirect(reverse("goods:index"))

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

在windows虛擬環(huán)境中python manage.py runserver

運(yùn)行項(xiàng)目后打開http://127.0.0.1:8000/user/register

進(jìn)行按條件注冊,注冊成功后,django會發(fā)送一封郵件到163郵箱中去,在郵箱中點(diǎn)擊這封郵件的里的鏈接,進(jìn)入到http://127.0.0.1:8000/user/login界面,將之前注冊的那個(gè)賬戶和密碼進(jìn)行登陸,登陸成功后自動跳轉(zhuǎn)到首頁。

該同步的方法存在的問題:send_mail()是堵塞的情況下進(jìn)行發(fā)送的,如果還沒有發(fā)送成功的時(shí)候,就會一直進(jìn)行堵塞的。這樣就會造成用戶長時(shí)間的等待,用戶體驗(yàn)不好。


********* 四.celery異步發(fā)送郵件的問題 *******************


send_mail()方法是將郵件發(fā)送到smtp服務(wù)器,然后,smtp服務(wù)器將郵件發(fā)送到目的郵箱,時(shí)間是不固定

這種情況類似于在send_mail()后面加了個(gè)time.sleep()的方法,用戶體驗(yàn)是非常不好------------------解決辦法---------使用celery

celery是一個(gè)功能完備即插即用的任務(wù)隊(duì)列,任務(wù)隊(duì)列是一種跨線程、跨機(jī)器工作的一種機(jī)制。

celery特點(diǎn)是:簡單靈活高效

celery通過消息進(jìn)行通信,通常使用一個(gè)叫Broker(中間人)來協(xié)client(任務(wù)的發(fā)出者)和worker(任務(wù)的處理者)。clients發(fā)出消息到隊(duì)列中,broker將隊(duì)列中的信息派發(fā)給worker來處理。

作為中間人有種方案可選擇:RabbitMQ、Redis。在這個(gè)項(xiàng)目中使用redis作為中間人。

celery的安裝在windows的虛擬環(huán)境項(xiàng)目里面pip install celery

使用redis作為中間人,要先查看redis是否已經(jīng)啟動,在ubuntu里面,查看redis的進(jìn)程 ps -aux | grep redis

并ifconfig來查看ubuntu此時(shí)的地址inet :192.168.XXX.XXX

給任務(wù)發(fā)出者安裝redis,在windows的虛擬環(huán)境里面,pip install redis

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

在總的項(xiàng)目下面創(chuàng)建一個(gè)celery_tasks/tasks.py的文件,在這個(gè)文件里面

1.創(chuàng)建celery()對象:

from celery import Celery

app = Celery("celery_tasks.tasks",broker="redis://192.168.XXX.XXX:6379/8") #第一個(gè)參數(shù)是所在包的名字,第二個(gè) 是指定中間人,8代表所使用的數(shù)據(jù)庫的編號。

app = Celery("celery_tasks.tasks",broker="redis://127.0.0.1:6379/7") #用于在windows上的redis進(jìn)行鏈接

2.發(fā)送郵件的代碼

在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>請點(diǎn)擊下面的鏈接激活你的賬戶<br><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

因?yàn)榇藭r(shí)在tasks里面已經(jīng)有發(fā)送郵件的代碼了,我們可以將user/views里面的發(fā)送郵件的代碼注釋掉

在加密用戶身份信息,生成激活token,token =token.decode("utf8")后面只留下

    send_register_active_email.delay(email, username, token)

    # 返回應(yīng)答,跳轉(zhuǎn)到首頁

    return redirect(reverse("goods:index"))

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

在linux里面直接執(zhí)行celery會報(bào)錯(cuò):

原因:是因?yàn)槲覀兿葐觲orker的,后啟動項(xiàng)目的,那worker中需要用到settings文件中的配置,所以報(bào)錯(cuò)了。那啟動項(xiàng)目的時(shí)候?yàn)槭裁床粓?bào)錯(cuò)呢,原因是因?yàn)?,Django已經(jīng)給我們做了初始化的工作,總項(xiàng)目wsgi代碼在下面:

import os

from django.core.wsgi import get_wsgi_application

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "dailyfresh1807.settings") #初始化的工作

application = get_wsgi_application()

解決辦法:將wsgi代碼初始化工作的代碼復(fù)制在celery_tasks/tasks.py的文件里面

import os

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "dailyfresh1807.settings")

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

任務(wù)的處理者-----------------方法一:開啟ubuntu

1.在windows的虛擬環(huán)境的項(xiàng)目里面pip freeze > requirements.txt

該創(chuàng)建好的文件位于項(xiàng)目下面

2.使用ftp將該總的項(xiàng)目傳到ubuntu的桌面,打開linux的虛擬環(huán)境,在項(xiàng)目下pip install -r requirements.txt

3.執(zhí)行celery,celery -A celery_tasks.tasks worker -l info

方法二:使用windows的redis

1.確認(rèn)windows虛擬環(huán)境中的redis已經(jīng)開啟,celery_tasks/tasks.py的文件里面

app = Celery("celery_tasks.tasks",broker="redis://127.0.0.1:6379/7")

2.其次還要在windows的虛擬環(huán)境中安裝eventlet,pip install eventlet

3.在cmd的項(xiàng)目下celery -A celery_tasks.tasks worker -l info -P eventlet

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

有時(shí)會出現(xiàn)數(shù)據(jù)庫的密碼錯(cuò)誤問題,原因是有的用戶的windows數(shù)據(jù)庫和linux的數(shù)據(jù)庫密碼不一致

解決方法有兩種:

① 讓項(xiàng)目使用linux系統(tǒng)上的mysql數(shù)據(jù)庫,并修改配置文件中數(shù)據(jù)庫的配置,而且還在Linux系統(tǒng)中創(chuàng)建項(xiàng)目所需要的數(shù)據(jù)庫,重新生成遷移文件,并通過遷移文件生成表。

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",

}

}

② 讓任務(wù)的處理者與任務(wù)的發(fā)出者在同一臺電腦上,也就是都是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",

}

}

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

對數(shù)據(jù)庫的表里的字段進(jìn)行修改的話,要重新生成遷移文件,并執(zhí)行遷移文件,生成新的表

查看是否已經(jīng)被修改了,例如select * from df_user \G

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

如果任務(wù)的發(fā)出者,中間人還有任務(wù)的處理者,不在同一臺電腦上的話,這三者之間必須要能進(jìn)行通信的,也就是說需要在同一個(gè)網(wǎng)段,而且處理者所在的電腦必須要能上網(wǎng),否則它也給163的郵箱發(fā)不出去。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容