后臺(tái)學(xué)習(xí)——django(2)

經(jīng)過對(duì)django的初步學(xué)習(xí),我們已經(jīng)對(duì)后臺(tái)的基本流程以及django的運(yùn)作有了一定的了解,但是這還不足夠,django還有許多方法和API需要我們?cè)敿?xì)滴學(xué)習(xí),是時(shí)候開始進(jìn)階學(xué)習(xí)了。
上期文章:后臺(tái)學(xué)習(xí)——django(1)

零、上篇文章修改

  1. 靜態(tài)文件引用修改
  • 在上篇文章就說過,要盡可能滴把前端跟后臺(tái)分開,不要前后端代碼混在一起,我們看回之前的server/learning/templates/index.html
    <!doctype html>
    <html lang="en">
    <head>
    <meta charset="UTF-8" />
    <title>登錄注冊(cè)系統(tǒng)</title>
    {% load staticfiles %} # 這里
    <link rel="stylesheet" type="text/css" href='{% static "css/index.css" %}' />
    </head>
    <body>
    <div>
    <form action="login_register" method="post">
    {% csrf_token %}# 還有這里
    <table>
    <tr>
    <th>帳號(hào):</th>
    <td><input type="text" id="username" name="username" maxlength="20"/></td>
    </tr>
    <tr>
    <th>密碼:</th>
    <td><input type="password" id="password" name="password" maxlength="20"/></td>
    </tr>
    <tr>
    <th></th>
    <td>
    <label>
    <input type="radio" name="way" value="login" checked="checked"/>登錄
    </label>
    <label>
    <input type="radio" name="way" value="register"/>注冊(cè)
    </label>
    </td>
    </tr>
    <tr>
    <th></th>
    <td><input type="submit" id="submit" value="提交" onclick="return check()"/></td>
    </tr>
    </table>
    </form>
    </div>
    <script type="text/javascript">
    function check(){
    var username = document.getElementById('username');
    var password = document.getElementById('password');
    if(username.value == ''){
    alert('帳號(hào)不能為空,請(qǐng)重新輸入');
    username.select();
    }else if(password.value == ''){
    alert('密碼不能為空,請(qǐng)重新輸入');
    password.select();
    }else{
    return true;
    }
    return false;
    }
    </script>
    </body>
    </html>
  • 可以看到,里面還是存在了兩段混入了后臺(tái)代碼了,其中一個(gè)就是靜態(tài)文件引入所用到了{% load staticfiles %},實(shí)際上,我們可以把這句代碼刪掉,將引入鏈接的部分<link rel="stylesheet" type="text/css" href='{% static "css/index.css" %}' />修改成
    <link rel="stylesheet" type="text/css" href="/static/css/index.css"/>,你們可以打開服務(wù)器再次看一下頁(yè)面,實(shí)際效果是一樣的
  • 至于{% csrf_token %}這句代碼,我也沒有什么好的辦法解決,要不就不要用django的防止CSRF模式攻擊功能,要不就換一種傳輸方式,例如ajax,但是這樣做就遠(yuǎn)離了初衷了,還是不要改吧,到時(shí)候后臺(tái)工程師自己修改好了,反正也就是添加一句話的事

一、進(jìn)階學(xué)習(xí)——驗(yàn)證碼

在一個(gè)正常的登錄系統(tǒng)中,驗(yàn)證碼是非常重要的,用于識(shí)別人機(jī),畢竟我們都知道,這個(gè)世界中存在著萬惡的爬蟲,驗(yàn)證碼有很多種方式,有圖片的,有郵件的,有短信的,有拼圖的,不管什么樣的驗(yàn)證碼,目的都是驗(yàn)證訪問用戶到底是人還是機(jī)器,要對(duì)機(jī)器say no,接下來我們要實(shí)踐一個(gè)圖片性的驗(yàn)證碼。

  1. URL拓展
  • 還記得URL設(shè)置文件urls.py里面,匹配路徑的是用正則表達(dá)式的么,學(xué)過正則表達(dá)式的應(yīng)該會(huì)知道分組吧,其實(shí)在路徑匹配的正則表達(dá)式那里使用分組,django還可以將之匹配出來當(dāng)作參數(shù)給接口函數(shù),不多說先試試效果
  • 假設(shè)要通過URL直接傳遞兩個(gè)參數(shù),返回兩個(gè)數(shù)相加的結(jié)果
    • 先改server/server/urls.py文件
      # -- coding:utf-8 --
      from django.conf.urls import url
      from django.contrib import admin
      from learning import views as learning
      from django.conf.urls.static import static
      from django.conf import settings
      urlpatterns = [
      url(r'^admin/', admin.site.urls),
      url(r'^$', learning.index),
      # 通過正則分組匹配兩個(gè)相加的數(shù)字
      url(r'^add/(\d+)/(\d+)/$', learning.add),
      ]
      urlpatterns+=static(settings.STATIC_URL,document_root=settings.STATIC_ROOT)
      這樣我們可以通過訪問例如localhost:8000/add/3/4/的網(wǎng)址(后面兩個(gè)數(shù)字可以換),來實(shí)現(xiàn)直接傳參的效果
    • 再在server/server/views.py添加接口函數(shù)
      def add(request, a, b):
      return HttpResponse(str(int(a) + int(b)))
      沒錯(cuò),括號(hào)里面的數(shù)字會(huì)當(dāng)作參數(shù)直接就傳到接口函數(shù)里面去了,不過這些參數(shù)都是字符串,如果是要當(dāng)作數(shù)字或者其他類型使用的時(shí)候,記得要轉(zhuǎn)換類型
    • 再次懷著激動(dòng)的心情開啟服務(wù)器(為啥要說再次???),輸入網(wǎng)址localhost:8000/add/4/5/
    • 換個(gè)數(shù)字再試一遍localhost:8000/add/200/300/

      Perfect!!!
  1. 下一步我們就要開始構(gòu)造驗(yàn)證碼函數(shù)了,也就是返回一張驗(yàn)證碼圖片的函數(shù),怎么建呢?我們又沒有驗(yàn)證碼圖片在?沒有就直接畫出來唄,python擁有一個(gè)庫(kù)pillow專門用于畫圖的,安裝命令pip install pillow
  • 首先還是要添加路由,在server/server/urls.py中添加下面的代碼
    url(r'^verify/(\d+)/(\d+)/', learning.verify)
    大家可以看得到這里用到了上面URL拓展的知識(shí),為的就是使得這驗(yàn)證碼函數(shù)可以得到重用的機(jī)會(huì),那兩個(gè)參數(shù)就是寬度width和高度height,這樣的話以后要用到寬度或者高度不同的驗(yàn)證碼都可以使用這個(gè)函數(shù)了
  • 下一步我們就要開始準(zhǔn)備我們的畫筆構(gòu)造我們的驗(yàn)證碼了
    • 使用pillow畫圖,第一步就是創(chuàng)建一張畫布,有了畫布我們才可以在上面畫畫對(duì)不,創(chuàng)建畫布代碼如下
      # 創(chuàng)建畫布需要導(dǎo)入Image
      from PIL import Image
      # 用到了Imagenew函數(shù)
      # 第一個(gè)參數(shù)是顏色通道,這里使用了RGB通道,還有其他的一些通道,如CMYK之類的,但不用管
      # 第二個(gè)參數(shù)是由寬高組成的元組,數(shù)字
      # 第三個(gè)參數(shù)是圖片的背景色,這里用rgb的顏色顯示,例如( 255, 255, 255),注意這是元組
      img = Image.new('RGB', (width, height), bgColor)
    • 有了畫布,但我們用什么來畫圖,(難道是。。。畫筆?),廢話,當(dāng)然是,來,給你支畫筆
      # 創(chuàng)建畫筆需要導(dǎo)入ImageDraw
      from PIL import ImageDraw
      # 用到了ImageDrawDraw函數(shù)
      # 有且只有一個(gè)參數(shù),就是之前創(chuàng)建的畫布
      draw = ImageDraw.Draw(img)
    • 有了畫布和畫筆,但驗(yàn)證碼中是有字的,難道我們真的要像現(xiàn)實(shí)一樣一筆一劃滴寫字么,而且用電腦寫好難看啊,其實(shí)pillow還可以導(dǎo)入電腦的字體,你不用再想些亂七八糟的東西了
      # 導(dǎo)入字體需要導(dǎo)入ImageFont
      from PIL import ImageFont
      # 用到了ImageFonttruetype函數(shù),可以自動(dòng)查詢電腦中的字體
      # 第一個(gè)參數(shù)是字體名字
      # 第二個(gè)參數(shù)是字體大小
      # 注意這個(gè)是windows系統(tǒng)下默認(rèn)的字體,其他系統(tǒng)自己找
      font = ImageFont.truetype('arial.ttf', size)
  • 好了,畫布畫筆字體我們都有了,那我們開畫吧,該畫些什么呢。。。。。。。
    • 不想那么多,反正驗(yàn)證碼總該有字吧,我們研究怎么寫字
      # 寫字需要使用drawtext方法
      # 第一個(gè)參數(shù)是一個(gè)坐標(biāo)軸元組,分別是距離左邊和上邊的距離
      # 第二個(gè)參數(shù)是要寫的字(字符串)
      # 后面的兩個(gè)參數(shù)分別是字體和字體顏色
      draw.text((x, y), text, font=font, fill=textColor)
    • 還有可能要一些干擾元素,例如線條
      # 畫線條需要使用drawline方法
      # 第一個(gè)參數(shù)是包含了兩個(gè)坐標(biāo)的元組,分別是線條一頭一尾的坐標(biāo)
      # 后面的參數(shù)是線條的顏色
      draw.line((x1, y1, x2, y2), fill=lineColor)
    • 還想畫些什么,算了吧,畫那么復(fù)雜干嘛


  • 一切都準(zhǔn)備就緒了,開工寫接口函數(shù)
    # 導(dǎo)入繪圖對(duì)象
    from PIL import Image, ImageFont, ImageDraw
    # 導(dǎo)入隨機(jī)函數(shù),randint:生成固定范圍內(nèi)的隨機(jī)整數(shù)
    from random import randint
    def verify(request, width, height):
    wordsCount = 4# 驗(yàn)證碼中的字符長(zhǎng)度
    width = int(width)# 圖片寬度
    height = int(height)# 圖片高度
    size = int(min(width/wordsCount, height)/1.5)# 字體大小設(shè)置
    bgColor = (randint(200, 255), randint(200, 255), randint(200, 255))# 隨機(jī)背景色(淺色)
    img = Image.new('RGB', (width, height), bgColor)# 創(chuàng)建圖像
    font = ImageFont.truetype('Arial.ttf', size)# 導(dǎo)入字體
    draw = ImageDraw.Draw(img)# 創(chuàng)建畫筆
  • 等等,我們要怎么返回生成的圖片????
    • 我們要知道,網(wǎng)頁(yè)間的傳輸都是字符串的傳輸,并沒有其他數(shù)據(jù)結(jié)構(gòu),所以不管你要傳輸什么,都要把它轉(zhuǎn)成字符串
    • 圖像的字符串形式實(shí)際上就是二進(jìn)制數(shù)字
    • pillow中并沒有直接返回圖像二進(jìn)制的功能
    • 實(shí)際上有個(gè)笨方法就是利用Image對(duì)象的save方法保存到本地,然后讀取本地文件返回
    • 但是每一次請(qǐng)求都要在本地存儲(chǔ)驗(yàn)證碼圖片,會(huì)耗費(fèi)大量的內(nèi)存空間滴,而且這驗(yàn)證碼用過一次就沒用了
    • 為了應(yīng)付這需求,python有個(gè)內(nèi)置模塊StringIO,可以將圖片緩存到內(nèi)存里面,讀取后就清空內(nèi)存,是不是很爽
    • 廢話不多說,讓我們看看要怎么弄
      # 導(dǎo)入StringIO模塊
      from StringIO import StringIO
      # 建立一個(gè)緩存對(duì)象
      mstream = StringIO()
      # 將圖片保存到內(nèi)存中
      img.save(mstream, 'jpeg')
      # 返回內(nèi)存中的圖片
      return HttpResponse(mstream.getvalue(), 'image/jpeg')
  • 還有還有,我們要畫一個(gè)什么樣驗(yàn)證碼,總該定好一些規(guī)則吧
    • 規(guī)則一:均勻繪畫字符,居中
    • 規(guī)則二:字符顏色要比較深
    • 規(guī)則三:要有線條雪花等干擾元素
    • 規(guī)則四:一切能隨機(jī)的都隨機(jī)
  • 我***,那么多規(guī)則,怎么畫??!不管了不管了,先想一下文字怎么畫吧,這個(gè)是重點(diǎn)。然而,pillow里面是沒有旋轉(zhuǎn)文字這東西的,就只有圖像本身的旋轉(zhuǎn)
    • 首先要確定用什么字符,當(dāng)然是數(shù)字加大小寫字母啦
      text = '1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
    • 其次要確定文字的位置,這里需要一定的數(shù)學(xué)知識(shí)(加減乘除,別說你不會(huì))
      left = width * i / num + (width / 4 - size) / 2# i為第幾個(gè)文字
      top = (height - size) / 2
    • 還要確定文字的顏色,要隨機(jī)的顏色,顏色要比較深
      textColor = (randint(0, 160), randint(0, 160), randint(0, 160))
    • 合起來就是這樣滴
      text = '1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
      for i in range(wordsCount):
      textColor = (randint(0, 160), randint(0, 160), randint(0, 160))
      left = width * i / wordsCount + (width / 4 - size) / 2
      top = (height - size) / 2
      draw.text((left, top), text[randint(0, len(text) - 1)], font=font, fill=textColor)
  • 再畫雪花,話說雪花是什么東東,呵呵,就是白色的*嘛~~~
    • 顏色:白色
      textColor = (255, 255, 255)
    • 位置:隨機(jī)
      left = randint(0, width)
      top = randint(0, height)
    • 合起來:
      for i in range(30):
      textColor = (255, 255, 255)
      left = randint(0, width)
      top = randint(0, height)
      draw.text((left, top), '*', font=font, fill=textColor)
  • 最后畫線條,這個(gè)簡(jiǎn)單
    • 位置:頭尾都隨機(jī)
      line = (randint(0, width), randint(0, height), randint(0, width), randint(0, height))
    • 顏色:隨機(jī)
      linecolor = (randint(0, 160), randint(0, 160), randint(0, 160))
    • 合起來:
      for i in range(5):
      linecolor = (randint(0, 160), randint(0, 160), randint(0, 160))
      line = (randint(0, width), randint(0, height), randint(0, width), randint(0, height))
      draw.line(line, fill=linecolor)
  • OK,把所有的步驟都合起來就是結(jié)果了
    # -- coding:utf-8 --
    import sys
    reload(sys)
    sys.setdefaultencoding('utf-8')
    from django.http import HttpResponse
    from PIL import Image, ImageFont, ImageDraw
    from StringIO import StringIO
    from random import randint
    def verify(request, width, height):
    wordsCount = 4
    width = int(width)
    height = int(height)
    size = int(min(width / wordsCount, height) / 1.3)
    bgColor = (randint(200, 255), randint(200, 255), randint(200, 255))
    img = Image.new('RGB', (width, height), bgColor)
    font = ImageFont.truetype('arial.ttf', size)
    draw = ImageDraw.Draw(img)
    text = '1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
    for i in range(wordsCount):
    textColor = (randint(0, 160), randint(0, 160), randint(0, 160))
    left = width * i / wordsCount + (width / 4 - size) / 2
    top = (height - size) / 2
    draw.text((left, top), text[randint(0, len(text) - 1)], font=font, fill=textColor)
    for i in range(30):
    textColor = (255, 255, 255)
    left = randint(0, width)
    top = randint(0, height)
    draw.text((left, top), '*', font=font, fill=textColor)
    for i in range(5):
    linecolor = (randint(0, 160), randint(0, 160), randint(0, 160))
    line = (randint(0, width), randint(0, height), randint(0, width), randint(0, height))
    draw.line(line, fill=linecolor)
    del draw
    mstream = StringIO()
    img.save(mstream, 'jpeg')
    return HttpResponse(mstream.getvalue(), 'image/jpeg')
  • 保存,打開服務(wù)器,打開網(wǎng)址localhost:8000/verify/100/40/,你會(huì)看到這樣的一張圖片
  • 刷新一下


  • 換個(gè)網(wǎng)址localhost:8000/verify/200/40/
    Paste_Image.png
  • 非常完美不是么
  1. 然而的然而,你還沒完成呢??!只有驗(yàn)證碼圖片卻不能驗(yàn)證有什么用??!
  • 要起到驗(yàn)證的效果首先就要保存相應(yīng)的文字,那么就要在寫文字那里保存相應(yīng)的文字verifyText

  • 然后然后還要將之保存在session里面
    request.session['verify'] = verifytext

  • 這個(gè)函數(shù)基本完成了,但一個(gè)完整的驗(yàn)證碼系統(tǒng)還是不夠的,我們來完善以下,接下來由于個(gè)人問題,實(shí)在不想看到之前的代碼了,會(huì)將之前沒用的清掉,我會(huì)把變動(dòng)的文件代碼全部給出(這算做潔癖么,還是強(qiáng)迫癥)

  • 更改后的文件目錄
    server
    ├────learning
    | ├────migrations
    | | └──init.py
    | ├────static
    | ├────templates
    | | └──index.html
    | ├────init.py
    | ├────admin.py
    | ├────apps.py
    | ├────models.py
    | ├────tests.py
    | └────views.py
    ├────server
    | ├────init.py
    | ├────settings.py
    | ├────urls.py
    | └────wsgi.py
    ├────db.sqlite3
    └────manage.py

  • server/server/urls.py文件:
    # -- coding:utf-8 --
    from django.conf.urls import url
    from django.contrib import admin
    from learning import views as learning
    from django.conf.urls.static import static
    from django.conf import settings

       urlpatterns = [
           url(r'^admin/', admin.site.urls),
           url(r'^$', learning.index),
           url(r'^verify/(\d+)/(\d+)/$', learning.verify),
           url(r'^check/$', learning.check),
       ]
       urlpatterns+=static(settings.STATIC_URL,document_root=settings.STATIC_ROOT)
    
  • server/learning/templates/index.html文件:
    <!doctype html>
    <html lang="en">
    <head>
    <meta charset="UTF-8" />
    <title>Document</title>
    </head>
    <body>
    <form action="/check/" method="post">
    {% csrf_token %}
    <img src="verify/300/80/" /><br />
    <input type="text" name="verify" id="verify"/>
    <input type="submit"/>
    </form>
    </body>
    </html>
    這個(gè)html文件通過verify/300/80/顯示一張300x80的驗(yàn)證碼,點(diǎn)擊提交按鈕后提交到/check/

  • server/learning/views.py文件:
    # -- coding:utf-8 --
    import sys
    reload(sys)
    sys.setdefaultencoding('utf-8')
    from django.shortcuts import render
    from django.http import HttpResponse
    from PIL import Image, ImageFont, ImageDraw
    from StringIO import StringIO
    from random import randint

       # 頁(yè)面接口:返回`index.html`頁(yè)面
       def index(request):
           return render(request, 'index.html')
    
       # 功能接口:返回驗(yàn)證碼輸入正確與否(忽略大小寫)
       def check(request):
           if request.POST['verify'].lower() == request.session['verify'].lower():
               return HttpResponse('success')
           else:
               return HttpResponse('failed')
    
       # 功能接口:返回驗(yàn)證碼圖片
       def verify(request, width, height):
           wordsCount = 4
           width = int(width)
           height = int(height)
           size = int(min(width / wordsCount, height) / 1.3)
           bgColor = (randint(200, 255), randint(200, 255), randint(200, 255))
           img = Image.new('RGB', (width, height), bgColor)
           font = ImageFont.truetype('arial.ttf', size)
           draw = ImageDraw.Draw(img)
           text = '1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
           verifytext = ''
           for i in range(wordsCount):
               textColor = (randint(0, 160), randint(0, 160), randint(0, 160))
               left = width * i / wordsCount + (width / 4 - size) / 2
               top = (height - size) / 2
               word = text[randint(0, len(text) - 1)]
               verifytext += word
               draw.text((left, top), word, font=font, fill=textColor)
           for i in range(30):
               textColor = (255, 255, 255)
               left = randint(0, width)
               top = randint(0, height)
               draw.text((left, top), '*', font=font, fill=textColor)
           for i in range(5):
               linecolor = (randint(0, 160), randint(0, 160), randint(0, 160))
               line = (randint(0, width), randint(0, height), randint(0, width), randint(0, height))
               draw.line(line, fill=linecolor)
           del draw
           mstream = StringIO()
           img.save(mstream, 'jpeg')
           request.session['verify'] = verifytext
           return HttpResponse(mstream.getvalue(), 'image/jpeg')
    
  1. OK,開始驗(yàn)證成果
  • 打開網(wǎng)址localhost:8000,顯示頁(yè)面如下(驗(yàn)證碼圖片可能不同):
  • 輸入正確的驗(yàn)證碼


  • 點(diǎn)擊提交后,顯示success
  • 刷新頁(yè)面localhost:8000,重復(fù)上面的步驟,輸入錯(cuò)誤的驗(yàn)證碼,顯示failed
  • 到此,一個(gè)完整的驗(yàn)證碼就完成了,如果你有更好的想象力,還可以畫出更好的驗(yàn)證碼,深究請(qǐng)查閱pillow文檔

二、進(jìn)階學(xué)習(xí)——用戶管理系統(tǒng)

  1. 先不說那么多,先清空兩個(gè)文件先(真的是強(qiáng)迫癥),分別是:
  • server/learning/admin.py文件:
    from django.contrib import admin
    # Register your models here.
  • server/learning/models.py文件:
    from future import unicode_literals
    from django.db import models
    # Create your models here.
  • 清完同步數(shù)據(jù)庫(kù):
    python manage.py makemigrations
    python manage.py migrate
  1. 再修改server/learning/templates/idnex.html,用于應(yīng)用相關(guān)接口函數(shù):
    <!doctype html>
    <html lang="en">
    <head>
    <meta charset="UTF-8" />
    <title>登錄注冊(cè)系統(tǒng)</title>
    </head>
    <body>
    <div>
    <form action="check/" method="post">
    {% csrf_token %}
    <table>
    <tr>
    <th>帳號(hào):</th>
    <td><input type="text" id="username" name="username" maxlength="20" /></td>
    </tr>
    <tr>
    <th>密碼:</th>
    <td><input type="password" id="password" name="password" maxlength="20" /></td>
    </tr>
    <tr>
    <th>驗(yàn)證碼</th>
    <td>
    <input type="text" id="verify" name="verify" maxlength="4"/>
    <img src="verify/100/30/" alt="" />
    </td>
    </tr>
    <tr>
    <th></th>
    <td>
    <label><input type="radio" name="way" value="login" checked="checked"/>登錄</label>
    <label><input type="radio" name="way" value="register"/>注冊(cè)</label>
    </td>
    </tr>
    <tr>
    <th></th>
    <td><input type="submit" id="submit" value="提交" onclick="return check()" /></td>
    </tr>
    </table>
    </form>
    </div>
    <script type="text/javascript">
    function check() {
    var username = document.getElementById('username');
    var password = document.getElementById('password');
    var verify = document.getElementById('verify');
    if(username.value == '') {
    alert('帳號(hào)不能為空,請(qǐng)重新輸入');
    username.select();
    } else if(password.value == '') {
    alert('密碼不能為空,請(qǐng)重新輸入');
    password.select();
    } else if(verify.value.length != 4){
    alert('驗(yàn)證碼輸入錯(cuò)誤,請(qǐng)重新輸入');
    verify.select();
    } else {
    return true;
    }
    return false;
    }
    </script>
    </body>
    </html>
  • 打開服務(wù)器,進(jìn)入網(wǎng)址localhost:8000,結(jié)果如下:
  • 很難看,不過將就一下吧


  1. 修改server/learning/views.py文件中的check函數(shù),并添加兩個(gè)空函數(shù):
    # check函數(shù)的作用已經(jīng)在之前涉及過了,這里就不講了
    def check(request):
    if request.POST['verify'].lower() == request.session['verify'].lower():
    username = request.POST['username']
    password = request.POST['password']
    if request.POST['way'] == 'login':
    return HttpResponse(login(request, username, password))
    elif request.POST['way'] == 'register':
    return HttpResponse(register(request, username, password))
    else:
    return HttpResponse('驗(yàn)證碼錯(cuò)誤')

    def login(request, username, password):
        pass
    
    def register(request, username, password):
        pass
    
  2. OK,終于準(zhǔn)備好所有東西了,我們開始研究django的用戶認(rèn)證系統(tǒng)吧

  • 用戶的基本操作一共有四個(gè),分別是:
    • 注冊(cè)
    • 登錄
    • 登出
    • 判斷是否已經(jīng)登錄
  • django封裝了一個(gè)比較完善的用戶認(rèn)證系統(tǒng),各種操作都非常方便,注冊(cè)便是如此
    • 添加一個(gè)用戶只需用到下面的代碼:
      from django.contrib.auth.models import User
      # username為用戶名,password為密碼
      User.objects.create_user(username=username, password=password).save()
    • 然而注冊(cè)需要考慮到一種狀況:用戶已存在,其實(shí)如果是這樣的話,在創(chuàng)建的時(shí)候就會(huì)報(bào)錯(cuò),做好錯(cuò)誤處理就好
      try:
      User.objects.create_user(username=username, password=password).save()
      return '注冊(cè)成功'
      except:
      return '已存在用戶'
  • 有了注冊(cè)好的用戶,自然需要登錄:
    • 登錄之前肯定要驗(yàn)證帳號(hào)密碼是否正確啦
      from django.contrib.auth import authenticate
      # username為用戶名,password為密碼
      # 該函數(shù)會(huì)返回一個(gè)user對(duì)象,如果不存在該用戶或者密碼錯(cuò)誤則返回None
      authenticate(username=username, password=password)
    • 驗(yàn)證正確自然要儲(chǔ)存登錄信息,通常而言都會(huì)用session來存儲(chǔ),而django對(duì)此方法進(jìn)行了包裝,只需使用以下代碼就行了:
      from django.contrib.auth import login
      # request為請(qǐng)求對(duì)象,user為用戶對(duì)象
      login(request, user)
  • 登錄后,怎么登出呢,其實(shí)這個(gè)更簡(jiǎn)單,其他后臺(tái)或許直接清掉session就可以了,django也一樣將其封裝好了:
    from django.contrib.auth import logout
    # request為請(qǐng)求對(duì)象
    logout(request)
  • 最后一個(gè),判斷是否已經(jīng)登錄,這個(gè)django用request對(duì)象中的user對(duì)象中的is_authenticated()方法(此處應(yīng)該有哭笑不得的表情)
    # 用戶登錄時(shí),該函數(shù)返回True,否則返回False
    request.user.is_authenticated()
  1. OK,一切都準(zhǔn)備就緒后就可以寫之前那兩個(gè)函數(shù)了,代碼如下:
    from django.contrib.auth.models import User
    from django.contrib import auth

     def login(request, username, password):
         user = auth.authenticate(username=username, password=password)
         if user is None:
             return '登錄錯(cuò)誤'
         else:
             auth.login(request, user)
             return '登錄成功'
    
     def register(request, username, password):
         try:
             User.objects.create_user(username=username, password=password).save()
             # 注冊(cè)后默認(rèn)為登錄狀態(tài)
             login(request, username, password)
             return '注冊(cè)成功'
         except:
             return '已存在用戶'
    
  • 大家可以打開服務(wù)器試一下,分別注冊(cè)新的用戶,注冊(cè)已經(jīng)存在相同用戶名的用戶,登錄帳號(hào)密碼正確的用戶, 登錄不存在的用戶,登錄密碼錯(cuò)誤的用戶,是不是都如想象中一般返回了想要返回的信息?是的話就恭喜你完成了登錄和注冊(cè)的操作了
  1. 然而還沒有完呢,登錄注冊(cè)完總該有個(gè)用戶界面吧,在用戶界面總該有登出的設(shè)置吧,訪問用戶界面時(shí)總該有確認(rèn)是否已經(jīng)登錄了吧
  • 為了有一個(gè)更好的應(yīng)用場(chǎng)景,這里需要添加一個(gè)html文件server/learning/templates/user.html,內(nèi)容如下:
    <!doctype html>
    <html lang="en">
    <head>
    <meta charset="UTF-8" />
    <title>user</title>
    </head>
    <body>
    <script type="text/javascript">
    username = location.search.match(/username=(.*?)(&|$)/);
    if(username) {
    document.write('歡迎' + username[1] + '的到來<a href="/logout/">注銷</a>');
    }
    </script>
    </body>
    </html>
    現(xiàn)在的目錄如下:
    server
    ├────learning
    | ├────migrations
    | | └──init.py
    | ├────static
    | ├────templates
    | | ├──index.html
    | | └──user.html
    | ├────init.py
    | ├────admin.py
    | ├────apps.py
    | ├────models.py
    | ├────tests.py
    | └────views.py
    ├────server
    | ├────init.py
    | ├────settings.py
    | ├────urls.py
    | └────wsgi.py
    ├────db.sqlite3
    └────manage.py

  • 還要修改一下server/server/urls.py文件,接上對(duì)應(yīng)的接口函數(shù):
    urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^$', learning.index),
    url(r'^user$', learning.user),
    url(r'^verify/(\d+)/(\d+)/$', learning.verify),
    url(r'^check/$', learning.check),
    url(r'^logout/$', learning.logout)
    ]

  • 最后寫上各個(gè)接口函數(shù)就搞定啦,具體細(xì)節(jié)看注釋:
    # -- coding:utf-8 --
    import sys
    reload(sys)
    sys.setdefaultencoding('utf-8')
    from django.shortcuts import render
    # 這里新導(dǎo)入了一個(gè)HttpResponseRedirect函數(shù),用于頁(yè)面的重定向
    from django.http import HttpResponse,HttpResponseRedirect
    from PIL import Image, ImageFont, ImageDraw
    from StringIO import StringIO
    from random import randint
    from django.contrib.auth.models import User
    from django.contrib import auth

       def index(request):
           return render(request, 'index.html')
    
       def user(request):
           # 使用request.user.is_authenticated()來判斷是否已經(jīng)登錄
           if request.user.is_authenticated():
               return render(request, 'user.html')
           else:
               # 如果沒有登錄的話自動(dòng)重定向到首頁(yè)
               return HttpResponseRedirect('/')
    
       def logout(request):
           # 登出設(shè)置,并重定向到首頁(yè)
           auth.logout(request)
           return HttpResponseRedirect('/')
    
       def check(request):
           if request.POST['verify'].lower() == request.session['verify'].lower():
               username = request.POST['username']
               password = request.POST['password']
               if request.POST['way'] == 'login':
                   # 這里修改為直接返回函數(shù)返回的值
                   return login(request, username, password)
               elif request.POST['way'] == 'register':
                   return register(request, username, password)
           else:
               return HttpResponse('驗(yàn)證碼錯(cuò)誤')
    
       def login(request, username, password):
           user = auth.authenticate(username=username, password=password)
           if user is None:
               return HttpResponse('登錄錯(cuò)誤')
           else:
               auth.login(request, user)
               # 如果登錄成功就自動(dòng)跳轉(zhuǎn)到用戶頁(yè)面
               return HttpResponseRedirect('/user?username=' + username)
    
       def register(request, username, password):
           try:
               User.objects.create_user(username=username, password=password).save()
               # 剛注冊(cè)完肯定可以登錄成功,直接返回用戶頁(yè)面
               return login(request, username, password)
           except:
               return HttpResponse('已存在用戶')
       # 驗(yàn)證碼函數(shù)已省略
    

    這里解析一下什么是重定向,其實(shí)也就是服務(wù)器端的網(wǎng)頁(yè)跳轉(zhuǎn),重定向后就轉(zhuǎn)到不同的路徑去了,比如你要訪問a網(wǎng)頁(yè),在地址欄輸入a網(wǎng)頁(yè)的URL,但a網(wǎng)頁(yè)會(huì)重定向到b網(wǎng)頁(yè),那么返回來的是b網(wǎng)頁(yè)的信息,而且地址欄也是顯示b網(wǎng)頁(yè)的URL(當(dāng)然這個(gè)只是給一臉懵的人看的,想了解更詳細(xì)的信息,就看百度百科的解析吧)

  1. 我們來測(cè)試一下成果,如果你看到的跟下面的一樣就證明你已經(jīng)搞定了
  • 打開服務(wù)器,進(jìn)入網(wǎng)址localhost:8000,進(jìn)入首頁(yè):
  • 注冊(cè)一個(gè)正確的帳號(hào),自動(dòng)跳轉(zhuǎn)到用戶頁(yè)面:


  • 點(diǎn)擊注銷,跳轉(zhuǎn)到首頁(yè):


  • 注冊(cè)一個(gè)已經(jīng)存在的用戶,返回錯(cuò)誤信息:


  • 返回首頁(yè)輸入正確的帳號(hào)密碼,登錄帳號(hào),自動(dòng)跳轉(zhuǎn)到用戶頁(yè)面:


  • 注銷返回首頁(yè),登錄不存在的用戶,返回錯(cuò)誤信息:


  • 返回首頁(yè),登錄密碼錯(cuò)誤的用戶,返回錯(cuò)誤信息:


  1. 基本功能通通都實(shí)現(xiàn)了,然而還不足夠完善,我們還需要更多的功能
  • 我們可以發(fā)現(xiàn),之前的代碼在出現(xiàn)登錄錯(cuò)誤的時(shí)候是無法分辨是用戶不存在還是密碼錯(cuò)誤的,而且我們?nèi)蘸罂隙〞?huì)經(jīng)常需要查詢用戶存不存在的信息,不一定要用戶密碼都正確才可以查詢得到。但是django的用戶認(rèn)證模塊并區(qū)分這兩個(gè)錯(cuò)誤,不過我們可以發(fā)現(xiàn)用戶認(rèn)證是基于django的模型的,也就是之前說的數(shù)據(jù)庫(kù)操作那里,我們可以通過數(shù)據(jù)庫(kù)操作的方法只通過用戶名來查詢是否存在該用戶,登錄接口函數(shù)修改如下:
    def login(request, username, password):
    try:
    # 當(dāng)不存在該用戶時(shí)會(huì)出現(xiàn)錯(cuò)誤
    User.objects.get(username=username)
    user = auth.authenticate(username=username, password=password)
    if user is None:
    return HttpResponse('密碼錯(cuò)誤')
    else:
    auth.login(request, user)
    return HttpResponseRedirect('/user?username=' + username)
    except:
    return HttpResponse('不存在該用戶')
  • 還有,我們以后會(huì)有大量的頁(yè)面需要登錄用戶才可以訪問,但如果每次都使用request.user.is_authenticated()來判斷會(huì)顯得很麻煩,有沒有更好的方法呢?答案是有的,django在設(shè)計(jì)的時(shí)候就已經(jīng)考慮到這個(gè)問題了,它提供了一種裝飾器,只要在需要登錄后訪問的頁(yè)面接口函數(shù)前加上這個(gè)裝飾器就可以自動(dòng)判斷是否已經(jīng)登錄了,使用方法如下:
    # 導(dǎo)入裝飾器
    from django.contrib.auth.decorators import login_required
    # 使用裝飾器,在函數(shù)前@這個(gè)裝飾器
    # 參數(shù)login_url為沒有登錄的時(shí)候需要跳轉(zhuǎn)到哪里,這里設(shè)置為跳轉(zhuǎn)到首頁(yè)
    # 參數(shù)redirect_field_name為重定向時(shí)傳輸當(dāng)前的頁(yè)面路徑,這里設(shè)置為沒有
    @login_required(login_url='/', redirect_field_name=None)
    def user(request):
    # 不再需要判斷是否已經(jīng)登錄了
    return render(request, 'user.html')
  • 有時(shí)候我們存儲(chǔ)的用戶信息可不單單只有這些(包括username、first_name、last_name、email、password),我們還需要額外添加一些用戶信息,如登錄時(shí)間,注冊(cè)時(shí)間(其實(shí)也有滴,不過我就懶得想其他啦,反正原理都一樣)等,但是如果直接更改原本的模型又很麻煩,這個(gè)時(shí)候就需要使用模型的代理繼承功能了。
    • 模型的代理繼承實(shí)際上借用了python類的繼承,修改server/learning/models.py代碼如下:
      from future import unicode_literals
      from django.db import models
      from django.contrib.auth.models import User
      # 繼承與django默認(rèn)的User模型
      class learningUser(User):
      # 添加兩個(gè)字段,分別是登錄日期和注冊(cè)日期(自動(dòng)填入)
      logindate = models.DateField(auto_now=True)
      registerdate = models.DateField(auto_now_add=True)

    • 實(shí)際上,這并不是一種正規(guī)的寫法(但經(jīng)驗(yàn)證,是可以這樣做的),正規(guī)的寫法如下:
      class learningUser(models.Model):
      # 設(shè)置與User對(duì)象一一對(duì)應(yīng)
      user = models.OneToOneField(User)
      logindate = models.DateField(auto_now=True)
      registerdate = models.DateField(auto_now_add=True)
      但是這樣一來引用的時(shí)候會(huì)顯得很麻煩,創(chuàng)建用戶的時(shí)候還要?jiǎng)?chuàng)建多一個(gè)對(duì)象,而且到時(shí)候要獲取用戶的logindate的時(shí)候還要用user.learningUser.logindate來獲取,雖然這樣可以動(dòng)態(tài)改變相關(guān)model,但如果當(dāng)前項(xiàng)目中每一個(gè)用戶都需要有這個(gè)拓展就顯得很麻煩了,所以之后還是以第一種方法來拓展User模型

    • 接下來就是模型改動(dòng)必要的程序了,記住以后都要這樣做
      修改server/learning/admin.py文件:
      from django.contrib import admin
      from learning.models import learningUser
      admin.site.register(learningUser)
      終端執(zhí)行代碼:
      python manage.py makemigrations
      python manage.py migrate

    • 最后一步就是修改接口了,將from django.contrib.auth.models import User替換成from learning.models import learningUser
      修改登錄注冊(cè)函數(shù)為如下:
      def login(request, username, password):
      try:
      # 使用learning_user模型獲取用戶
      guest = learningUser.objects.get(username=username)
      user = auth.authenticate(username=username, password=password)
      if user is None:
      return HttpResponse('密碼錯(cuò)誤')
      else:
      auth.login(request, user)
      # 輸出我們之前新添加的兩個(gè)屬性(懶的在頁(yè)面上顯示了)
      print guest.logindate, guest.registerdate
      return HttpResponseRedirect('/user?username=' + username)
      except:
      return HttpResponse('不存在該用戶')

       def register(request, username, password):
           try:
               # 記住也要用learningUser創(chuàng)建對(duì)象
               learningUser.objects.create_user(username=username, password=password).save()
               login(request, username, password)
               return HttpResponseRedirect('/user?username=' + username)
           except:
               return HttpResponse('已存在用戶')
      

      這里要提醒的是如果使用的是django用戶認(rèn)證的函數(shù),他們的操作對(duì)象都是最原本的User對(duì)象,而不是我們新創(chuàng)建的用戶對(duì)象

    • 我們重新注冊(cè)一個(gè)帳號(hào),在終端可以看到登錄和注冊(cè)的日期


  • 如果我們要對(duì)用戶進(jìn)行分級(jí)又該怎么辦呢?比如要分管理員和普通用戶(注意這里不是可以進(jìn)入admin頁(yè)面的超級(jí)用戶,管理員可以管理用戶,刪除用戶等),或者更負(fù)責(zé)的分類又怎么辦呢?其實(shí)這里涉及到用戶權(quán)限還有組的問題,組用于標(biāo)識(shí)用戶的類別(一個(gè)用戶可以多個(gè)類),權(quán)限對(duì)應(yīng)具體的權(quán)限,一個(gè)組可以對(duì)應(yīng)多個(gè)權(quán)限。說這么多,不用理解,先看下面就會(huì)明白(我會(huì)告訴你我也研究了好久么):
    • 首先我們要知道權(quán)限是什么,有什么用。權(quán)限通俗來講就是你能不能夠做某種事,就像男人不能生孩子(先天性上帝沒有給你權(quán)限,除了某個(gè)從上帝盜取的權(quán)限的人),權(quán)限的存在可以很方便滴管理用戶,一些特殊的功能就只能給有權(quán)限的用戶使用,而組就是對(duì)用戶的一種分類,組包含了多個(gè)權(quán)限,用戶屬于這個(gè)組的話就擁有了這些權(quán)限,這樣就不需要很麻煩滴對(duì)用戶添加多個(gè)權(quán)限了,而且分組后還可以更直接滴對(duì)用戶進(jìn)行管理,比如一個(gè)班有部分人當(dāng)選了組長(zhǎng)(組),他們可以管理組員,檢查作業(yè)、管理組員、組織小組活動(dòng)等(權(quán)限),如果老師需要所有人的名單,可以直接讓組長(zhǎng)提供小組名單(對(duì)組的管理),這樣一來就不需要一個(gè)一個(gè)權(quán)限的管理了,上升到組的層次。

    • 最近廢話有點(diǎn)多,還是專心研究代碼吧(以下代碼在server/learning/views.py文件寫的)。首先你要懂得怎樣創(chuàng)建一個(gè)新的權(quán)限:
      # 在django中權(quán)限就是一個(gè)Permission模型的實(shí)例
      from django.contrib.auth.models import Permission
      from django.contrib.contenttypes.models import ContentType
      # 用get_or_create防止重復(fù)創(chuàng)建
      newPermission = Permission.objects.get_or_create(**{
      # 創(chuàng)建Permission對(duì)象需要三個(gè)參數(shù)(都是必須的)
      # codename是代碼中使用的名字
      # name是顯示出來的名字
      # content_type要通過創(chuàng)建ContentType對(duì)象獲取一個(gè)該模型的ContentType實(shí)例
      'codename': 'new_permission',
      'name': 'New Permission',
      'content_type': ContentType.objects.get_for_model(learningUser)
      })[0]
      本質(zhì)上講ContentType實(shí)例可以從更高的層次操作模型(這里操作learningUser模型,不過你們都不需要知道啦,現(xiàn)在知道怎么寫就好,重點(diǎn)是codenamename

    • 創(chuàng)建之后總該要添加吧,其實(shí)添加很簡(jiǎn)單,假設(shè)user是一個(gè)User的實(shí)例(實(shí)際上可以是User的繼承模型,如我們之前設(shè)置的learningUser模型):
      # 注意所有的newPermission都是Permission的實(shí)例對(duì)象
      # 設(shè)定user對(duì)象的權(quán)限為列表中的權(quán)限
      user.permission = [newPermission1, newPermission2, ...]
      # 向user對(duì)象添加權(quán)限
      user.permission.add(newPermission1, newPermission2, ...)
      # 移除user對(duì)象中的權(quán)限
      user.permission.remove(newPermission1, newPermission2, ...)
      # 清空user對(duì)象的權(quán)限
      user.permission.clear()

    • user對(duì)象有了權(quán)限,那么我們應(yīng)該怎么判斷user到底有沒有該權(quán)限呢?這時(shí)候就需要調(diào)用user對(duì)象的has_perm()或者has_perms()方法了
      # 還記得之前寫的codename么,判斷權(quán)限的時(shí)候就需要用到了
      # 格式為:app_name.codename
      user.has_perm('learning.new_permission')
      # 這個(gè)是判斷同時(shí)擁有多個(gè)權(quán)限的(傳一個(gè)列表或者元組過去)
      user.has_perms(['learning.new_permission'])

    • 像登錄一樣,權(quán)限判斷也有相對(duì)因的裝飾器在需要權(quán)限的函數(shù)前面寫上這個(gè)裝飾器就可以了
      # 第一個(gè)參數(shù)是權(quán)限(多個(gè)權(quán)限時(shí),傳遞一個(gè)列表)
      @permission_required('learning.new_permission', login_url='/')

    • 權(quán)限搞定了,那么組又該怎么做呢?其實(shí)組也有相對(duì)應(yīng)的模型Group,每個(gè)組都是Group的實(shí)例,我們先創(chuàng)建一個(gè):
      newGroup = Group.objects.get_or_create(name='manager')[0]

    • 這個(gè)時(shí)候,這個(gè)組是不沒有任何的權(quán)限的,我們要添加權(quán)限到組里面,添加方法跟添加到User對(duì)象類似:
      # 注意所有的newPermission都是Permission的實(shí)例對(duì)象
      # 設(shè)定newGroup對(duì)象的權(quán)限為列表中的權(quán)限
      newGroup.permission = [newPermission1, newPermission2, ...]
      # 向newGroup對(duì)象添加權(quán)限
      newGroup.permission.add(newPermission1, newPermission2, ...)
      # 移除newGroup對(duì)象中的權(quán)限
      newGroup.permission.remove(newPermission1, newPermission2, ...)
      # 清空newGroup對(duì)象的權(quán)限
      newGroup.permission.clear()

    • 創(chuàng)建好組就可以添加到user對(duì)象中了,添加方法還是一樣滴:
      # 注意所有的newGroup都是Group的實(shí)例對(duì)象
      # 設(shè)定user對(duì)象的權(quán)限為列表中的組
      user.groups= [newGroup1, newGroup2, ...]
      # 向user對(duì)象添加組
      user.groups.add(newGroup1, newGroup2, ...)
      # 移除user對(duì)象中的組
      user.groups.remove(newGroup1, newGroup2, ...)
      # 清空user對(duì)象的組
      user.groups.clear()

    • 用戶屬于一個(gè)組,就擁有了這個(gè)組所包含的權(quán)限,判斷有沒有權(quán)限的方法跟之前用戶直接擁有某權(quán)限的方法一樣,這里就不寫了

    • 為了更好滴應(yīng)用到權(quán)限和組的功能,這里用一個(gè)實(shí)例(只有是manager的用戶才可以查看所有的用戶名字)來演示

    • 先添加一個(gè)html文件server/learning/templates/manager.html,內(nèi)容如下:
      <!doctype html>
      <html lang="en">
      <head>
      <meta charset="UTF-8" />
      <title>manager</title>
      <script src="http://code.jquery.com/jquery-latest.js" type="text/javascript" charset="utf-8"></script>
      </head>
      <body>
      <script type="text/javascript">
      $.ajax({
      type:"get",
      url:"/show_all_user/",
      dataType:"json",
      success:function(data){
      write = '當(dāng)前有以下用戶<br />'
      for(var i = 0; i < data.length; i++){
      write += data[i] + '<br />'
      }
      document.write(write);
      }
      });
      </script>
      </body>
      </html>

    • 修改server/server/urls.py文件,添加以下幾個(gè)路徑:
      url(r'^manager$', learning.manager),
      url(r'^show_all_user/$', learning.show_all_user),

    • 修改server/learning/views.py文件,以下列出要新導(dǎo)入的方法和要修改或者添加的函數(shù):
      from django.contrib.auth.decorators import user_passes_test
      from django.contrib.auth.models import Group, Permission
      from django.contrib.contenttypes.models import ContentType

       # 獲取組的函數(shù),傳遞組的名字和對(duì)應(yīng)的權(quán)限名(列表,自動(dòng)創(chuàng)建)
       def get_or_create_group(name, permissions=None):
           group = Group.objects.get_or_create(name=name)[0]
           if permissions:
               permissions_codename = [i.codename for i in group.permissions.all()]
               for i in permissions:
                   if i not in permissions_codename:
                       group.permissions.add(Permission.objects.get_or_create(**{
                           'codename': i,
                           'name': ' '.join(i.split('_')).title(),
                           'content_type': ContentType.objects.get_for_model(learningUser)
                       })[0])
           return group
      
       # 因?yàn)閐jango沒有提供判斷是否有組的裝飾器,自己就寫了一個(gè)
       # 這個(gè)裝飾器跟權(quán)限裝飾器一樣,是不過傳遞的組名而不是權(quán)限名
       def group_required(group, **kwargs):
           def check_group(user):
               return set(groups).issubset(set([i.name for i in user.groups.all()]))
           groups = group if isinstance(group, (list, tuple)) else (group, )
           # 這個(gè)函數(shù)用于跳轉(zhuǎn),check_group判斷是否存在組,然后返回布爾值
           return user_passes_test(check_group, **kwargs)
      
       # 組的裝飾器都寫了,就不介意自己寫一個(gè)權(quán)限的裝飾器吧
       def permission_required(perm, **kwargs):
           perms = perm if isinstance(perm, (list, tuple)) else (perm, )
           return user_passes_test(lambda user:user.has_perms(perms), **kwargs)
      
       # 必須是擁有組manager的用戶才可以訪問
       @group_required('manager', login_url='/', redirect_field_name=None)
       def manager(request):
           return render(request, 'manager.html')
      
       # 必須是擁有l(wèi)earning.show_all_user權(quán)限的才可以訪問
       @permission_required('learning.show_all_user', login_url='/', redirect_field_name=None)
       def show_all_user(request):
           return HttpResponse(json.dumps([i.username for i in User.objects.all()]))
       
       def register(request, username, password):
           try:
               user = learningUser.objects.create_user(username=username, password=password)
               # 直接默認(rèn)給新添加的用戶添加manager組(主要是懶)
               user.groups = [get_or_create_group('manager', permissions=['show_all_user'])]
               user.save()
               login(request, username, password)
               return HttpResponseRedirect('/user?username=' + username)
           except: 
               return HttpResponse('已存在用戶')
      
    • 現(xiàn)在你用新注冊(cè)的用戶去訪問localhost:8000/manager頁(yè)面就會(huì)顯示如下:

    • 如果是沒有權(quán)限的用戶就直接返回首頁(yè)

  1. 好了,現(xiàn)在為止用戶系統(tǒng)才基本完善,其實(shí)還有很多需要深入的,不過目前為止還是了解到這為好,因?yàn)槲覀冇忠M(jìn)入下一輪的進(jìn)階學(xué)習(xí)了。

下期文章:后臺(tái)學(xué)習(xí)——django(3)(還無有)

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

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

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