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


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

Django==2.2

配置好mysql數據庫的連接

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

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

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

 

 

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

因為系統自帶的User表字段沒有需要的字段所以自己新建覆蓋了一張用戶表來存儲用戶信息

django提供一種方式讓我們可以重載它的類

繼承AbstractUser

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

在模型文件里設計好之后,需要在全局文件里設置

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

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

還有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%}

 

然后就是錯誤信息判斷完的顯示,和之前在做登陸的時候一樣


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM