學習筆記:Django開發網上教育平臺(參考了慕課網的教學視頻)

第一步:進行環境的搭建(用到的IDE:pycharm  ,數據庫爲mysql、nacicat、編輯語言python3.七、以及本身配置的虛擬環境venvpy37)

Django==2.2

配置好mysql數據庫的鏈接html

第二步就是需求分析和app的設計:前端

大體上分爲 四個app(也就是四個模塊,Django是按app來開發的)python

  • users--用戶相關
  • courese--課程相關
  • organization--機構相關
  • operation-用戶操做相關

 

 

而後新建完app並調整好結構,靜態文件的文件夾static,html頁面的文件夾templates,媒體文件文件夾media等mysql

由於系統自帶的User表字段沒有須要的字段因此本身新建覆蓋了一張用戶表來存儲用戶信息git

django提供一種方式讓咱們能夠重載它的類github

繼承AbstractUserajax

from datetime import datetime

from django.db import models
from django.contrib.auth.models import AbstractUser

GENDER_CHOICES=(
    ("male",'男'),
    ("female",'女')
)


class UserProfile(AbstractUser):
    nick_name=models.CharField(max_length=50,verbose_name="暱稱",default="")
    birthday=models.DateField(verbose_name="生日",null=True,blank=True)
    gender=models.CharField(verbose_name="性別",choices=GENDER_CHOICES,max_length=6)
    address=models.CharField(max_length=100,verbose_name="地址",default="")
    mobile=models.CharField(max_length=11,verbose_name="手機號碼")
    image=models.ImageField(verbose_name="用戶頭像",upload_to="head_image/%Y/%m",default="default.jpg")

    class Meta:
        verbose_name="用戶信息"
        verbose_name_plural=verbose_name

    def __str__(self):
        if self.nick_name:
            return self.nick_name
        else:
            return self.username

在模型文件裏設計好以後,須要在全局文件裏設置redis

AUTH_USER_MODEL="users.UserProfile",讓系統知道用的是這張表

運行makemigrations和migrate 生成遷移文件和表 sql

還有coures相關的表:課程表、章節、視頻、課程資源 四個表數據庫

organizations:城市表、課程機構表、講師表

operations:用戶諮詢表、課程評論表、用戶收藏表、用戶信息表、用戶課程表

代碼太多就不詳細貼上去了

在設計過程種使用了分層設計 避免循環導入不一樣app中的model

配置好相關的path 路徑

 

 

而後用xadmin來搭建後臺管理系統:在github上搜索xadmin下載

1. 下載xadmin源碼
2. 在settings的INSTALLED_APPS中添加
    crispy_forms 和 xadmin
3. 安裝xadmin的依賴包
django-crispy-forms
django-import-export
django-reversion
django-formtools
future
httplib2
six
xlwt
xlsxwriter
requests這些依賴包都要安裝到虛擬環境中(進入虛擬環境後進入安裝的文件夾路徑 pip install -r ruquirements.txt 讀取須要的依賴包並所有安裝)
4. 經過migrate生成xadmin須要的表

 

而後配置好urls.py的路徑

import xadmin
path('xadmin/', xadmin.site.urls),

使用xadmin 能夠本身識別用戶本身定義的表

這樣的狀況下後臺照樣把咱們本身定義的User識別進去了,它會幫咱們自動註冊進來

而後在以前新建好的每一個app裏 新建adminx.py文件 配置後臺 例如:course app

import xadmin

from apps.courses.models import Course,Lesson,Video,CourseResource


class GlobalSettings(object):
    #配置後臺的標題和標尾
    site_title="林鵬項目--後臺管理系統"
    site_footer="林鵬項目"
    menu_style="accordion"

class BaseSettings(object):
    #配置後臺主題
    enable_themes = True
    use_bootswatch = True



class CourseAdmin(object):

    list_display = ["id", "name","detail","desc","degree","learn_times","students"]
    search_fields = ["name","detail","desc","degree"]
    list_filter = ["name", "desc", "learn_times","teacher__name"]
    list_editable = ["degree", "desc"]


class LessonAdmin(object):
    list_display = ["course", "name", "learn_times"]
    search_fields = ["course", "name"]
    list_filter = ["course__name", "name", "learn_times"]
    list_editable = ["name"]


class VideoAdmin(object):
    list_display = ["lesson", "name", "learn_times", "url"]
    search_fields = ["lesson", "name"]
    list_filter = ["name", "desc", "learn_times"]
    list_editable = ["lesson", "name"]


class CourseResourceAdmin(object):
    list_display = ["course", "name", "file"]
    search_fields = ["course", "name", "file"]
    list_filter = ["course", "name", "file"]
    list_editable = ["course", "name"]



xadmin.site.register(Course,CourseAdmin)
xadmin.site.register(Lesson,LessonAdmin)
xadmin.site.register(Video,VideoAdmin)
xadmin.site.register(CourseResource,CourseResourceAdmin)

#xadmin全局設置
xadmin.site.register(xadmin.views.CommAdminView,GlobalSettings)
xadmin.site.register(xadmin.views.BaseAdminView,BaseSettings)

這裏包括了列表、過濾 、搜索、編輯框的功能  list_display 爲列表  search-fields 搜索列 list_filter 過濾  list_editable 編輯框

接下來就是開始第一個功能的編寫:登陸

在作登陸以前須要配置好首頁和登陸頁面

而後編寫邏輯函數 在views.py文件

 

使用的是CBV的邏輯來開發

CBV-class base view(優勢:

class是能夠繼承的 爲了後期便於維護有利於代碼重用

由於是登陸因此和用戶相關是在users這個app裏的views裏編寫

class LoginView(View): def get(self,request, *args, **kwargs): #判斷用戶是否登陸 是否登陸的狀態is_authenticated在django2.o以後版本都是屬性若是是登陸狀態則重定向到首頁 if request.user.is_authenticated: return HttpResponseRedirect(reverse("index")) login_form=DynamicLoginForm() return render(request,"login.html",{ "login_form":login_form }) def post(self,request, *args, **kwargs): #表單驗證 login_form=LoginForm(request.POST) if login_form.is_valid(): user_name=login_form.cleaned_data["username"] password=login_form.cleaned_data["password"] # 用於經過用戶名和密碼查詢用戶是否存在 user = authenticate(username=user_name, password=password) if user is not None: #查詢到用戶 login(request,user) #login方法會自動登陸,request爲上面django傳遞的參數 #登陸成功以後返回頁面 return HttpResponseRedirect(reverse("index")) else: # 未查詢到用戶 return render(request, "login.html", {"msg": "用戶名或密碼錯誤","login_form":login_form}) else: return render(request,"login.html",{"login_form":login_form}) 

 

 

 這裏的代碼邏輯 所有備註好了: 登錄頁面有兩種請求 一種是提交的post  一種是get

request.user.is_authenticated:  這句話是判斷是否登錄,若是有就重定向到首頁,login_form牽涉到表單驗證的內容,
user_name=login_form.cleaned_data["username"]
password=login_form.cleaned_data["password"]是獲取到表單清洗後的值[]裏的值是html頁面 input標籤的name屬性的值,這兩行就是獲取前段頁面提交的數據

而後下面就是邏輯判斷 ,判斷用戶是否存在 存在就登錄,用django 內部的login方法

不然就返回錯誤信息,並返回表單驗證的錯誤信息

<div class="error btns login-form-tips" id="jsLoginTips">{% if login_form.errors %}{% for key,error in login_form.errors.items %}{{ error }}{% endfor %}{% else %}{{ msg }}{% endif %}

這是前端頁面的判斷邏輯,

{% if login_form.errors %}意思爲若是表單中存在錯誤,
{% for key,error in login_form.errors.items %}{{ error }}:for key 是哪一個字段 ,error in login_form.errors.items是錯誤類型 , {{ error }}是輸出錯誤結果
{% else %}{{ msg }}:不然輸出msg

 

這裏的判斷邏輯是:若是在表單驗證中發現用戶名或者密碼錯誤:將前端樣式的輸入框變成紅色 

在表單提交的時候出現403錯誤是由於django 內部的安全驗證  防止跨域攻擊只須要在表單的</form> 以前插入這句就好了 {% csrf_token %} 

登錄開發完了以後:就是開發退出登錄的藉口

編寫的View函數都是繼承django 自己自帶的view

編寫一個LogoutView(View)

class LogoutView(View): def get(self,request, *args, **kwargs): logout(request) return HttpResponseRedirect(reverse("index")) 

而後再urls.py中修改一下配置

path('logout/', LogoutView.as_view(),name="logout"),

而後就是在html頁面配置退出的url

<a class="fr" href="{% url 'logout' %}">退出</a>

這裏的url 都是這樣的書寫格式 和前面的path  的name屬性 一直   它會自動識別出路徑   並且方便後期維護,只須要改動path的url路徑名

接下來還學習了一種用手機驗證碼來登錄的功能以及註冊:

首先爲了可以發送手機驗證碼,須要找有發送手機短信資質的運營商 ,這裏使用的是雲片網 (具體如何註冊使用就不說明了) 

在雲片網:www.yunpian.com

 

此時準備完以後就須要在pycharm中編寫代碼了:

在apps文件夾下新建一個目錄爲utils 專門存放這種工具類文件

查看API文檔有步驟說明

咱們短信驗證只須要用到單條短信發送 進入相關的文檔說明裏

這是相關的參數,只需選擇3個必傳參數便可 其餘參數看需求

 

apikey 在控制檯裏

 

apikey 最好在項目的全局設置裏配置好 方便後期更改

 

短信發送須要用到request

 

import requests import json def send_single_sms(apikey,code,mobile): #發送單條短信 url="https://sms.yunpian.com/v2/sms/single_send.json" text="【林鵬先生】您的驗證碼是{}".format(code) res = requests.post(url, data={ "apikey":apikey, "mobile":mobile, "text":text }) re_json=json.loads(res.text) return re_json if __name__ == "__main__": res=send_single_sms("f3444e2cea8176e2a16aad30ddc4af19","test","13587701643") import json res_json=json.loads(res.text) code=res_json["code"] msg=res_json["msg"] if code==0: print("發送成功") else: print("發送失敗:{}".format(msg)) print(res.text) 

這裏的if __name__== "__main__"

用來測試的

 

這裏的url 爲文檔說明中的url, text 爲雲片網審覈經過的模板樣式

有了url 和text 以後就是發送數據了:

經過request的post方法 一個參數爲url 一個參數爲data data爲dict形式

dict裏面的值爲文檔裏面要傳的參數

這樣就算完成了,可是還須要用ajax的方式來完成短信驗證碼的發送用到了js的知識

$.ajax({
    cache: false,
    type: 'post',
    dataType:'json',
    url:"/send_sms/",
    data:{
        mobile:$inpRegMobile.val(),
        "captcha_1":$inpRegCaptcha.val(),
        "captcha_0":$('#id_captcha_0').val(),
    },
class SendSmsView(View): def post(self, request, *args, **kwargs): send_sms_form = DynamicLoginForm(request.POST) re_dict={} if send_sms_form.is_valid(): mobile=send_sms_form.cleaned_data["mobile"] #隨機生成數字驗證碼 code=generate_random(4,0) re_json=send_single_sms(yp_apikey,code,mobile=mobile,)#這個是json數據用於交互 if re_json["code"] == 0: re_dict["status"] = "success" r=redis.Redis(host=REDIS_HOST,port=REDIS_PORT,db=0,charset="utf8",decode_responses=True) r.set(str(mobile),code)#set模式 能夠持久化 r.expire(str(mobile),60*5)#設置驗證碼5分鐘過時 else: re_dict["msg"]= re_json["msg"] else: for key,value in send_sms_form.errors.items(): #若是出錯 哪一個字段出錯,錯誤緣由是什麼 表單驗證以後errors每一個key 它的錯誤信息是list形式 re_dict[key]=value[0] return JsonResponse(re_dict)

這裏return不用rander 而是用JsonResponse 由於這裏是用ajax 異步發送短信此時,上面提到的403錯誤不能再用

{% csrf_token %}來解決了

由於本來用form表單提交的方式只須要在form標籤 里加入{% csrf_token %}便可 瀏覽器會幫咱們直接表單裏的全部值

可是咱們使用ajax異步的方式來進行方式 這樣瀏覽器就不會幫咱們提交上去

全部使用{% csrf_token %} 並無用

此時就用到csrf_exempt() 讓服務器不驗證csrf

 

此時思考一下 若是在js文件裏面 添加csrf_token 獲取這個值是否能夠???可是在html頁面點擊查看源碼能夠看出值並無賦值進來 因此這個方法行不通

 

 

這裏還牽涉到了redis來記錄發送的數據

 經過雲片網發送驗證碼以後 這個驗證碼咱們得保存起來 進行驗證

因此保存數據:第一種 就是保存在數據庫 另外一種就是保存在內存中

用存在內存的方式 有兩個問題:

1. 重啓django以後,變量不存在了

2.隨着驗證碼愈來愈多,,內存佔用愈來愈大,驗證碼過時處理

用數據庫能夠實現 可是有特殊狀況 發送完驗證碼以後用戶並無進行後續操做 這樣數據就會愈來愈多

此時用 redis k-v數據庫

(redis要運行起來 redis-server.exe   redis-cli.exe)

import redis

 

if re_json["code"] == 0: re_dict["status"] = "success" r=redis.Redis(host=REDIS_HOST,port=REDIS_PORT,db=0,charset="utf8",decode_responses=True) r.set(str(mobile),code)#set模式 能夠持久化 r.expire(str(mobile),60*5)#設置驗證碼5分鐘過時

把redis的對象取出來賦給r,而後r.set 模式來持久化 

同時驗證碼還須要一個驗證邏輯,咱們使用表單來驗證 這個驗證碼和發送的驗證碼是否一致

class DynamicLoginPostForm(forms.Form): mobile = forms.CharField(required=True,min_length=11,max_length=11) code = forms.CharField(min_length=4,max_length=4,required=True) def clean_code(self): mobile = self.data.get("mobile") code = self.data.get("code") # 取出這兩個值以後就是涉及到在redis中查詢了 r = redis.Redis(host=REDIS_HOST, port=REDIS_PORT, db=0, charset="utf8", decode_responses=True) redis_code = r.get(str(mobile)) # 而後比較兩個code值 if code != redis_code: raise forms.ValidationError("驗證碼不正確") # 若是正確直接返回 return code

把redis對象取出來以後查詢它的code值 和 表單提交的code 進行比較  若是不相等拋出異常

這裏的拋出異常咱們用 raise

若是正確就返回這個code

 

 

:

 

還須要動態驗證碼:

在github上搜索 django-captcha-simple 裏面有安裝和配置步驟

而後文檔會告訴如何使用,按照步驟咱們建立一個表單

 

class DynamicLoginForm(forms.Form): #這是動態驗證碼和短信的表單 mobile =forms.CharField(required=True,min_length=11,max_length=11) captcha = CaptchaField()

有了這個表單以後在views.py裏面咱們把表單裏的傳遞過去 ,並顯示到前段頁面

<div class="form-group marb20 blur" id="jsRefreshCode"> {{ login_form.captcha }} {{ dynamic_form.captcha }} </div>

這裏的兩個只會顯示其中一個不會衝突

這樣動態驗證碼就實現了

 

接下來就是配置前段的html的url 和邏輯判斷

全部的配置完了以後不要忘記配置urls.py文件的路徑

 

 

動態登錄的邏輯完了以後須要實現動態註冊的功能:

class RegisterView(View): def get(self, request, *args, **kwargs): register_get_form=RegisterGetForm() return render(request,"register.html",{ "register_get_form":register_get_form }) def post(self, request, *args, **kwargs): register_post_form = RegisterPostForm(request.POST) if register_post_form.is_valid(): # 若是驗證經過說明不存在用戶,要新建 mobile = register_post_form.cleaned_data["mobile"] password = register_post_form.cleaned_data["password"] #新建用戶 user = UserProfile(username=mobile) # 由於數據庫裏的密碼是加密後的 因此不能直接存儲明文密碼須要經過set_password加密 user.set_password(password) user.mobile = mobile user.save() login(request, user) return HttpResponseRedirect(reverse("index")) else: register_get_form = RegisterGetForm() return render(request, "register.html.html", { "register_get_form":register_get_form, "register_post_form":register_post_form, }) 

註冊頁面也有兩個請求 1個是get  1個post ,get 請求須要獲取到註冊頁面的動態驗證碼

post 請求是要把前段提交的數據存儲,可是有兩個判斷邏輯,第一個是用戶沒有註冊,這個比較簡單把用戶提交的數據存儲到數據庫裏面, 第二個邏輯是用戶已經註冊過了,此時須要用到表單驗證,專門驗證mobile這個字段時候存在

class RegisterPostForm(forms.Form): mobile = forms.CharField(required=True, min_length=11, max_length=11) code = forms.CharField(min_length=4, max_length=4, required=True) password = forms.CharField(min_length=4) def clean_mobile(self): mobile = self.data.get("mobile") # 驗證手機號碼是否註冊過 users=UserProfile.objects.filter(mobile=mobile) if users: raise forms.ValidationError("該手機號碼已註冊") return mobile def clean_code(self): mobile = self.data.get("mobile") code = self.data.get("code") # 取出這兩個值以後就是涉及到在redis中查詢了 r = redis.Redis(host=REDIS_HOST, port=REDIS_PORT, db=0, charset="utf8", decode_responses=True) redis_code = r.get(str(mobile)) # 而後比較兩個code值 if code != redis_code: raise forms.ValidationError("驗證碼不正確") # 若是正確直接返回 return code

而後注意前段頁面的配置:

form表單的方法要是post方法 action屬性值爲{% url 'register'%}這種形式

input標籤的name屬性的值要和view函數裏一一對應 而且注意403錯誤 在表單里加入 {%csrf_token%}

 

而後就是錯誤信息判斷完的顯示,和以前在作登錄的時候同樣

相關文章
相關標籤/搜索