Django實現教育平臺全程記錄-----環境配置,使用者註冊,原頁面登入/登出,找回密碼,404頁面配置,郵箱驗證等

旅行耗發表於2018-05-18

windows虛擬環境安裝及配置

pip install virtualenv
virtualenv testvir # 預設在C:\Users\Administrator\testvir\
cd testdir/Scripts

activate.bat  # 進入testdir虛擬環境
deactivate.bat  # 退出環境
##  虛擬環境需要知道當前安裝的虛擬環境的目錄,這樣較麻煩,所以用另一款開發庫
pip install virtualenvwrapper-win    
mkvirtualenv -p C:\Users\Anaconda3\python.exe  testvir2 # 預設安裝在 C:\Users\Administrator\Envs\testvir2
deactivate.bat  # 退出當前環境
workon # 檢視所有虛擬環境

workon testvir2 # 這樣就進入了虛擬環境,就可以在虛擬環境下進行匹配安裝了

 

# pycharm 建立專案匯入虛擬環境的python.exe,檢視setting中的interpreter下安裝了哪些包

配置host 0.0.0.0 ,那麼你本機的ip4地址也能訪問了。
 

django配置

1. 建立log,static,media[存放使用者上傳的檔案],apps目錄,將所有的app(message)放入apps,並在setting中註冊app
2. pycharm設定apps目錄為mark Directory as sources root,pycharm可以直接from message import views(不用from apps.message import views)
3. cmd的命令列不能from message import views,需要settings中設定,把apps新增進去  import sys  sys.path.insert(0,os.path.join(BASE_DIR,'apps'))
4. 配置mysql資料庫
5. python manage.py makemigrations(變更表的比較) // python manage.py migrate (將變更遷徙到資料庫)
6. 配置template路徑  'DIRS': [os.path.join(BASE_DIR, 'templates')]   
配置static路徑:  STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static')

]

django的admin後臺管理

1. 建立超級使用者:python manage.py createsuperuser
2. settings中該admin後臺管理為中文:LANGUAGE_CODE = 'zh-hans'  ,更改時區:TIME_ZONE = 'Asia/Shanghai'  USE_TZ = False
3. 將寫好的UserProfile類新增到admin中,urlProfile參考  點選開啟連結
 class UserProfileAdmin(admin.ModelAdmin):
    pass
admin.site.register(UserProfile,UserProfileAdmin)
這樣就可以管理了

 

使用django的xadmin

1.  windows:https://github.com/sshwsfc/xadmin.git  (可能這個最新的版本不適合django1.9.8了,可以下載git@github.com:movehaolv/eduProj.git   裡的xadmin)然後 pip install   xadmin-master.zip (多試幾次) ;或者在django的根目錄(templates同級目錄下)下建立extra_app,解壓  xadmin-master.zip將裡面的admin資料夾複製到extra_app,再將extra_app 修改許可權mark Directory as sources root,這樣就能直接用admin了,但是xadmin的依賴包crispy_forms(pip install x-admin 附帶下載的)是沒有的(也可以直接pip install django-crispy-forms)。linux可以直接pip
2. 註冊app : 'xadmin' ,'crispy_forms'  
3. 替換url:  import xadmin   url(r'^xadmin/', xadmin.site.urls),
4. 遷徙xadmin的表 : python manage.py migrate  ,會生成4張xadmin的表,裡面的表會自動關聯到上面建立的UserProfile表(比如:xadmin_usersettings)
5. 註冊app到xadmin後臺管理系統:
新建對應的app下新建adminx.py檔案,新增model類(繼承的AbstractUser用以擴充套件的User的Model無需註冊,會自己註冊)
class EmailVerifyRecordAdmin(object):
    list_display = ['code','email','send_type','send_time']     # 開啟xadmin後顯示的欄位。不是新增物件(比如新增課程顯示的欄位,新增課程顯示的欄位是這個表的所有欄位)
    search_fields = ['code','email','send_type']  # 新增搜尋,不要在這裡新增 “時間”的欄位
    list_filter = ['code','email','send_type','send_time']  # 新增過濾功能

xadmin.site.register(EmailVerifyRecord,EmailVerifyRecordAdmin)

1.使用者登入

1. 用post提交form的使用者名稱和密碼,後臺進行驗證

from django.shortcuts import render,redirect
from django.contrib.auth import authenticate,login
class LoginView(View):
    def get(self,request):
		# 記住來源的url,如果沒有則設定為首頁('/')
        request.session['login_from'] = request.META.get('HTTP_REFERER', '/')
        return render(request,'login.html')
	def post(self,request):
		form = LoginForm(request.POST)
		if form.is_valid():    # form欄位的資料格式是否符合form表單要求
			username = request.POST.get('username')
			password = request.POST.get('password')
			user = authenticate(username=username,password=password)             # 拿使用者名稱和密碼去資料中對比是否存在  A 
			if user is not None:
				login(request,user)                
				if user.is_active:
					login(request,user)     # 登入賬戶,這樣xadmin也就登入了
					# 重定向到來源的url
					return redirect(request.session['login_from'])
				else:
					return render(request, 'login.html', {'msg': '使用者未啟用'})
			else:
				return render(request,'login.html',{'msg':'使用者名稱/密碼錯誤'})    
			else:
				return render(request,'login.html',{'form':form})   # form傳到前端,用以顯示錯誤  <div class="error btns login-form-tips" id="jsLoginTips">{% for k,v in form.errors.items %}{{ v }}{% endfor %}{{ msg }}</div>
		# 記住來源的url,如果沒有則設定為首頁('/')
        request.session['login_from'] = request.META.get('HTTP_REFERER', '/')
        return render(request,'login.html')
	def post(self,request):
		form = LoginForm(request.POST)
		if form.is_valid():    # form欄位的資料格式是否符合form表單要求
			username = request.POST.get('username')
			password = request.POST.get('password')
			user = authenticate(username=username,password=password)             # 拿使用者名稱和密碼去資料中對比是否存在  A 
			if user is not None:
				login(request,user)                
				if user.is_active:
					login(request,user)     # 登入賬戶,這樣xadmin也就登入了
					# 重定向到來源的url
					return redirect(request.session['login_from'])
				else:
					return render(request, 'login.html', {'msg': '使用者未啟用'})
			else:
				return render(request,'login.html',{'msg':'使用者名稱/密碼錯誤'})    
			else:
				return render(request,'login.html',{'form':form})   # form傳到前端,用以顯示錯誤  <div class="error btns login-form-tips" id="jsLoginTips">{% for k,v in form.errors.items %}{{ v }}{% endfor %}{{ msg }}</div>

在前端想要新增錯誤資訊,那麼可以這樣寫,如果有錯就會有提示。為了更有好提示,可以新增樣式

{% for k,v in form.errors.items %}{{ v }}{% endfor %}{{ msg }}

 

2 . 上面的方式只能郵箱,密碼登入。通過重寫authenticate方法,達到郵箱也可以登入

× 在settings中註冊
# Application definition
AUTHENTICATION_BACKENDS = (
    'users.views.CustomBackend',
)

 

 

× 然後重寫authenticate,這樣登入的時候就可以用郵箱登入了。執行順序 A --> B,最後返回B中的user

 

from django.db.models import Q
from django.contrib.auth.backends import ModelBackend
from .models import UserProfile

class CustomBackend(ModelBackend):
    def authenticate(self, username=None, password=None, **kwargs):
        try:
            user = UserProfile.objects.get(Q(username=username)|Q(email=username))  # 獲取到username的記錄  B
            if user.check_password(password):    # django不支援form表單的明文密碼反解,所以只能先將form裡的明文passowrd轉換成密文,然後和資料庫的比對
                return user
        except Exception as e:
            return None

使用者登出

from django.contrib.auth import logout
class LogoutView(View):
    def get(self,request):   # 
        logout(request)
        return redirect(request.META.get('HTTP_REFERER', '/'))

2.使用者註冊

第六章1 100min

第一步:新增驗證碼登入

× pip install django-simple-captcha==0.4.6   #  https://github.com/mbi/django-simple-captcha
× 配置url  url(r'^captcha/', include('captcha.urls')), ----> 註冊app  'captcha',   -----> migrations , migrate
× form表單:

class RegisterForm(forms.Form):
    email = forms.EmailField(required=True,max_length=20)
    password = forms.CharField(required=True,min_length=5)
    captcha = CaptchaField(error_messages={"invalid":u"驗證碼錯誤"})
× 檢視函式:
class RegisterView(View):
    def get(self,request):
        register_form = RegisterForm()
        return render(request,'register.html',{'register_form':register_form})
    def post(self,request):
        pass

× 前端頁面新增: {{ register_form.captcha }}    # 錯誤則紅框標紅顯示  <div class="form-group marb8 {% if register_form.password.errors %}errorput{% endif %} ">
此時,頁面顯示驗證碼。對應的資料庫表中生成相應的驗證碼的值和hash值。同時,前端頁面有個hidden的text框,值為該驗證碼的hash值。
系統會將使用者填寫的驗證碼和hidden框中的hash值跟資料庫中的值進行聯合匹配是否一致。
 

第二步:點選註冊並登入按鈕,傳送郵件至郵箱進行驗證

× 完善上面的post方法,點選註冊,先在userprofile中儲存欄位

def post(self,request):  
    register_form = RegisterForm(request.POST)  
    if register_form.is_valid():  
        username = register_form.cleaned_data['email']  
        password = register_form.cleaned_data['password']  
        if UserProfile.objects.get(email=username):
            return render(request,'register.html',{'msg':"使用者已存在"})
        user_profile = UserProfile()  
        user_profile.username = username  
        user_profile.emial = username  
        user_profile.password = make_password(password)  
        user_profile.is_active = 0  
        user_profile.save()  
  
        send_register_email(username,"register")    # 呼叫定義的郵件方法  
        return render(request,"login.html",{'register_form':register_form})  
    else:  
        return render(request,'register.html',{'register_form':register_form})  

× 新增Model EmailVerifyRecord

注:為什麼設計的該表需要code和email欄位?比如使用者需要密碼找回,伺服器如何才能安全把修改密碼的html返回給使用者,只能通過傳送郵箱等方式讓使用者點選郵箱中的連結,進入到修改密碼的介面(總不能直接給頁面讓你修改密碼)。郵件格式肯定是 localhost/resetpwd/active_code帶上啟用碼這種格式(總不能localhost/resetpwd/email 帶上郵箱的名字,這樣別人也能修改密碼了)。所以就需要一個儲存啟用碼和郵箱關係的表

from utils.email_send import send_register_email
class EmailVerifyRecord(models.Model):
    code = models.CharField(verbose_name=u"驗證碼",max_length=100)
    email = models.EmailField(verbose_name=u"郵箱")
    send_type = models.CharField(max_length=100,verbose_name=u"驗證碼型別",choices=(("register","註冊"),("forget","找回密碼"),('update','修改郵箱')))
    send_time = models.DateTimeField(default=datetime.now,verbose_name=u'傳送時間')
    class Meta:
        verbose_name = u"郵箱驗證"
        verbose_name_plural = verbose_name
    def __str__(self):
        return "{0},{1}".format(self.email,self.send_time)  # 這會在xadmin後臺管理中成功新增資料後返回的導航提示

× settings配置qq郵箱為傳送郵箱,
EMAIL_HOST = 'smtp.qq.com'
EMAIL_HOST_USER = '839985880@qq.com'
EMAIL_HOST_PASSWORD = 'spgqttasdasxzrlqnbegh'          # 需要開啟qq郵箱的POP3/SMTP服務,傳送簡訊獲取該值,可能過段時間會失效,需要重新傳送簡訊獲取
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_FROM = '839985880@qq.com'

× 建立utils的python目錄並建立email.send.py

import random
from django.core.mail import send_mail
from users.models import EmailVerifyRecord
from DjangoDdu.settings import EMAIL_FROM

def random_str(randomLength=8):
    """生成隨機字串"""
    chars = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789"
    str = ""
    for i in range(randomLength):
            j = random.randint(0,len(chars)-1)
            str += chars[j]
    return str

def send_register_email(email,send_type="register"):
    email_record = EmailVerifyRecord()
    code = random_str(16)
    email_record.code = code
    email_record.email = email
    email_record.send_type = send_type
    email_record.save()

    if send_type == "register":
        email_title = "使用者註冊連結"
        email_body = "點選下面連結啟用賬號:http://127.0.0.1:8000/active/{0}".format(code)
        send_status = send_mail(email_title,email_body,EMAIL_FROM,[email])     # EMIAL_FROM:再settings中註冊的郵箱。[email] 傳送給哪個郵箱
        if send_status:
            pass

第三步 郵箱啟用賬戶    (把User表中的is_active設定為1)

× url配置      url(r'^active/(?P<active_code>.*)/$',ActiveUserView.as_view(),name='active')

× 檢視函式    

class ActiveUserView(View):
    def get(self,request,active_code):
        register_user = EmailVerifyRecord.objects.get(code=active_code)
        if register_user:
            email = register_user.email         # Email 表中的email
            user = UserProfile.objects.get(email=email)  # 根據email查詢出User表中的物件
            user.is_active = 1
            user.save()
            return render(request,'login.html')
        else:
            return HttpResponse('啟用連結不對')

3.找回密碼

找回密碼的步驟跟使用者註冊差不多,不詳細寫。

× 點選提交,邏輯:將認證郵箱儲存在

class ForgetPwdView(View):
    def get(self,request):
        forget_form = ForgetForm()
        return render(request,'forgetpwd.html',{'forget_form':forget_form})
    def post(self,request):
        forget_form = ForgetForm(request.POST)
        if forget_form.is_valid():
            email = forget_form.cleaned_data['email']
            user = UserProfile.objects.filter(email=email)
            if user:  
                send_register_email(email,'forget')   # 將active_code和郵箱 保在EmailVersityRecord表中,作為後續修改密碼時候獲取相關使用者使用
                return HttpResponse('驗證碼已傳送,請注意查收')
            else:
                return render(request, 'forgetpwd.html', {'forget_form': forget_form,'msg':"郵箱不存在"})
        else:
            return render(request,'forgetpwd.html',{'forget_form':forget_form})

 

 

 

× 獲取郵箱連結進行密碼重置

class ResetView(View):
    def get(self,request,active_code):
        """通過啟用碼獲取對應的郵箱"""
        record_email_query = EmailVerifyRecord.objects.filter(code=active_code)
        if record_email_query:
            email = record_email_query.first().email
            return render(request,'password_reset.html',{'email':email})
        else:
            return render(request,'email_link_fail.html')

× 提交form表單,進行邏輯認證

class ModifyPwdForm(forms.Form):
    password = forms.CharField(required=True,min_length=5)
    password2 = forms.CharField(required=True,min_length=5)
class ModifyPwdView(View):
    def post(self, request):
        modify_pwd_form = ModifyPwdForm(request.POST)
        email = request.POST.get('email')
        if modify_pwd_form.is_valid():
            password = request.POST.get('password')
            password2 = request.POST.get('password2')
            if password != password2:
                return render(request,'password_reset.html',{'msg':'兩次密碼不一致,請重新輸入','email':email})
            email = request.POST.get("email")
            user = UserProfile.objects.get(email=email)
            user.password = make_password(password)
            user.save()
            return redirect('/login/')
        else:
            return render(request,'password_reset.html',{'modify_pwd_form':modify_pwd_form,'email':email})

4.404或500等頁面顯示

如果想讓django顯示自定義的http錯誤提示,那麼需要讓django進入生產環境,在settings中設定

DEBUG = False

ALLOWED_HOSTS = ["*"]   # × 代表允許所有ip訪問

在主程式的url中新增 (users是建立的一個app)

# 全域性404頁面處理
handler_404 = 'users.views.page_not_found'

在users的app的views中新增,這樣就配置完成了。

def page_not_found(request):
    response = render_to_response('404.html',{})     # 好像404.html只能放在templates目錄的第一層下,不能新建一個目錄
    response.status_code = 404   # 該狀態碼會影響瀏覽器的顯示
    return response

但是由於設定了debug=false,會認為你是生產環境,staticfiles_dirs就會失效,django就找不到你配置的{% ‘static ’css/com.css ‘ %}的樣式檔案。需要在settings中設定

STATIC_ROOT = os.path.join(BASE_DIR,'static')

然後在主url檔案中新增

from DjangoDdu.settings import MEDIA_ROOT,STATIC_ROOT

urlpatterns = [
    # http錯誤頁面
    url(r'^static/(?P<path>.*)$',serve,{'document_root':STATIC_ROOT}),
]

http錯誤顯示

400錯誤

遇到訪問地址不存在就會找到404.html並顯示(比如訪問了 http://127.0.0.1:8000/asdasd  這樣隨意的一個url)。

500錯誤

1 如果訪問了 http://127.0.0.1:8000/course/detail/2000/ 這樣的url會找到500.html返回(因為你的網站中只設定了20門課,只能訪問到http://127.0.0.1:8000/course/detail/20/ )

2 或者你在一個檢視函式中寫了 print(1/0)  也會顯示500錯誤

相關文章