這個(gè)系列(或者成不了一個(gè)系列。。)預(yù)計(jì)會(huì)全程參考Vamei様?shù)腄jango系列,膜一發(fā)。說句題外話,其實(shí)更加崇拜像Vamei那樣的能夠玩轉(zhuǎn)生活、各個(gè)領(lǐng)域都能取得不小成就的人。 【Django】 ■ 概述 Django久聞大名,是Python中最為有名的Web框架之一了。相比于其他框架,D的特點(diǎn)就是提供了各種各樣的組件,重量級(jí),可以解決很多很多問題。讓W(xué)eb編程一簡再簡。之前一直都學(xué)習(xí)使用Flask,不否認(rèn)Flask有其有點(diǎn),但是一個(gè)很大的不方便的地方在于,F(xiàn)lask的擴(kuò)展沒有統(tǒng)一的標(biāo)準(zhǔn)而且開放,所有人都可以寫自己的Flask擴(kuò)展。雖然說是具有開源精神,但是一些比較常見的功能出現(xiàn)多種實(shí)現(xiàn)還是會(huì)讓人有些困擾。下面簡單說說Django的使用 安裝Django依然使用pip:pip install Django,總的包大小大概6.8M。 下載完成后可以在Python shell中運(yùn)行以下命令: >>>import django >>>print django.VERSION (1, 11, 6, u'final', 0) 說明安裝成功。 ● 構(gòu)建一個(gè)Django項(xiàng)目目錄框架的快捷方法 讓我第一次感受到Django的重型和周到是相比較于Flask構(gòu)建項(xiàng)目時(shí)要自己一個(gè)個(gè)文件建立,Django可以一鍵幫助生成一個(gè)較為完整的項(xiàng)目框架等待你填充內(nèi)容。在pip安裝完成之后,如果是windows環(huán)境則在$PYTHON_HOME的scripts,如果是Linux環(huán)境則是在/usr/bin這些目錄下,里面會(huì)有一個(gè)django-admin[.exe]可執(zhí)行程序。運(yùn)行這個(gè)程序,后接上參數(shù)startproject <項(xiàng)目名>可以在當(dāng)前工作目錄下生成Django項(xiàng)目的目錄框架。得到的框架是這樣的: 這些也不全是空文件,像manage.py,settings.py等文件都是帶有默認(rèn)內(nèi)容的。 考慮到一般開發(fā)肯定是在windows上用IDE比如Pycharm,Pycharm也可以一鍵生成Django項(xiàng)目框架,而且比django-admin生成的多一個(gè)templates文件夾用來盛放模板文件。 構(gòu)建完成框架之后可以python manage.py runserver 8000來運(yùn)行起這個(gè)server,內(nèi)容由Django內(nèi)置好??吹降慕缑媸穷愃朴谶@樣的:
● 第一次為請(qǐng)求返回HTTP內(nèi)容 Django框架采用MVC架構(gòu),F(xiàn)lask框架中對(duì)于路由的響應(yīng)通過裝飾器來綁定響應(yīng)函數(shù)完成。而Django的路由設(shè)置統(tǒng)一放在urls.py這個(gè)文件中。下面將修改一下urls.py(因?yàn)橹按嬖谝恍﹥?nèi)容了) from django.conf.urls import url from django.contrib import admin #####下面這行是新加的##### from DjangoTest.views import first_page urlpatterns = [ url(r'^admin/', admin.site.urls), #####下面這行是新加的##### url(r'^$', first_page) ] 先來解釋一下urlpatterns這個(gè)列表,維護(hù)了整個(gè)項(xiàng)目的url和響應(yīng)函數(shù)的對(duì)應(yīng)關(guān)系。這里比較NB的一點(diǎn)在于支持的是正則表達(dá)式,也就是說可以為一批URL綁定相同的響應(yīng)函數(shù)。這一個(gè)和Flask還是比較不同的。Flask如果需要正則路由匹配的支持,則需要自己到werkzeug.routing中自己實(shí)現(xiàn)一個(gè)支持正則的Convertor對(duì)象。 默認(rèn)的自帶了Django服務(wù)器后面的URI如果是/admin/的話,那么路由到Django-Admin界面,而下面我們添加的那條,則是說明了當(dāng)請(qǐng)求URL為空(即訪問的URI是/時(shí)),則路由到first_page這個(gè)函數(shù)中去。那么first_page在哪里定義呢,看上面的import語句,是DjangoTest目錄下的views文件。這個(gè)文件是我們自己建的并且要往里面寫內(nèi)容的。比如下面這樣: from django.http import HttpResponse def first_page(request): return HttpResponse('<h1>Hello World</h1>')
● 進(jìn)行模塊化管理 一個(gè)網(wǎng)站可能有很多功能,因此肯定需要進(jìn)行模塊化的代碼管理。這一點(diǎn)在Flask中可以用類似于blueprint的結(jié)構(gòu)來實(shí)現(xiàn)。而在Django中這個(gè)被稱為app(默默吐槽,比Flask剛好高了一級(jí)) 運(yùn)行一個(gè)項(xiàng)目中的manage.py比如python manage.py startapp new_app就可以在當(dāng)前項(xiàng)目中增加一個(gè)名為new_app的目錄,下面也有admin.py,__init__.py,models.py,tests.py和views.py等文件。 光添加一個(gè)新的目錄并沒有用,還需要將這個(gè)目錄所代表的APP和當(dāng)前項(xiàng)目關(guān)聯(lián)起來。關(guān)聯(lián)的方法就是修改DjangoTest下的settings.py文件。這個(gè)文件中有一個(gè)INSTALLED_APPS列表,在其中添加'new_app'即可。另外可以看到這個(gè)INSTALLED_APP里面已經(jīng)有一些內(nèi)容存在了,這些內(nèi)容代表了Django內(nèi)置的一些功能比如用戶驗(yàn)證,會(huì)話管理,顯示靜態(tài)文件等等。Django識(shí)別APP是從項(xiàng)目的根目錄開始的,所以不用像已有的那些寫得比較復(fù)雜比如django.contrib.admin,而直接寫new_app即可。 為了訪問到一個(gè)單獨(dú)APP中的頁面,我們首先在項(xiàng)目總的urls.py中添加new_app的urls的相關(guān)信息(這樣做有利于不同模塊的url映射的各自維護(hù),模塊間解耦)。把DjangoTest下的urls.py修改: from django.conf.urls import url,include from django.contrib import admin from DjangoTest.views import first_page urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^$', first_page) #####下面這行新增##### url(r'^new/', include('new_app.urls')) ]
看到這里其實(shí)已經(jīng)可以大概看出來Django中如何進(jìn)行url的配置了。往urlpatterns里面可以增加url對(duì)象。url對(duì)象構(gòu)建時(shí)第一個(gè)參數(shù)是正則匹配路徑,第二個(gè)參數(shù)可以是一個(gè)callable的對(duì)象,此時(shí)需要在之前import進(jìn)去;也可以是include方法的返回,include方法的參數(shù)是一個(gè)字符串,字符串中以endpoint的形式指向另一個(gè)urls文件,此時(shí)那個(gè)urls文件中規(guī)定的正則匹配路徑在整個(gè)項(xiàng)目中應(yīng)該加上調(diào)用其include方法前面的路徑整個(gè)拿來匹配。 上面把new_app.urls文件中定義的url映射都include到了根目錄下,然而在new_app下目前還沒有urls.py文件,所以在這個(gè)目錄下新建urls.py。文件中的內(nèi)容參考下面這樣子: from django.conf.urls import url from .views import new_first_page urlpatterns = [ url('^$', new_first_page), ]
這么處理之后訪問/new/就會(huì)映射到new_first_page這個(gè)函數(shù)下了。
■ 數(shù)據(jù)庫和ORM初步 一個(gè)WEB應(yīng)用的根基在于數(shù)據(jù)庫中的數(shù)據(jù),一個(gè)好的web框架必須有很好的和數(shù)據(jù)庫交互的手段。之前在Flask的時(shí)候,采用了SQLAlchemy的第三方模塊的方法,將flask和數(shù)據(jù)庫的交互做得比較友好。到了Django的場合,Django有一套自己的ORM機(jī)制,看起來很像SQLAlchemy(事實(shí)上好像就是改造了SQLAlchemy),貼合度更好。 要進(jìn)行數(shù)據(jù)庫交互,首先得有數(shù)據(jù)庫。比如我先到虛擬機(jī)的mysql中創(chuàng)建一個(gè)Django項(xiàng)目用的數(shù)據(jù)庫,并且指定(或者創(chuàng)建)一個(gè)用戶來管理這個(gè)庫: $mysql -u root -p password: mysql>CREATE DATABASE Django_Test DEFAULT CHARSET utf8; mysql>GRANT ALL PRIVILEGES ON Django_Test.* TO weiyz@'%'; /*將新建的數(shù)據(jù)庫的操作權(quán)限賦給管理用戶*/
然后在Django項(xiàng)目的settings.py中的數(shù)據(jù)庫相關(guān)配置更改: DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'Django_Test', 'USER' : 'weiyz', 'PASSWORD' : '123456', 'HOST' : '192.168.191.112', 'PORT' : '3306', } }
這樣就關(guān)聯(lián)了數(shù)據(jù)庫和Django項(xiàng)目。 web和數(shù)據(jù)庫的交互形式是一個(gè)很重要的問題。如果我們使用MySQLdb這類包裝的比較低級(jí)的模塊來做的話,每一個(gè)數(shù)據(jù)庫操作都要寫一個(gè)SQL語句出來,略顯笨拙。ORM的妙處就在于能夠把數(shù)據(jù)庫操作封裝得像是一段原生的程序。Django采取的抽象數(shù)據(jù)庫操作的方式和SQLAlchemy很像,就是把一張表抽象成一個(gè)python類。比如我們?cè)趎ew_app這個(gè)APP中的models.py中加入以下內(nèi)容: from __future__ import unicode_literals from django.db import models class Character(models.Model): name = models.CharField(max_length=200) age = models.IntergerField() def __unicode__(self): return self.name
此時(shí)項(xiàng)目中已經(jīng)設(shè)計(jì)好了表,就差把表結(jié)構(gòu)給注入數(shù)據(jù)庫了。然而這個(gè)過程如果手動(dòng)做就沒有意義了。Django給出了自動(dòng)的解決方案。依次運(yùn)行如下命令: python manage.py migrate python manage.py makemigrations python manage.py migrate 第一個(gè)migrate可以看做是數(shù)據(jù)庫的初始化,運(yùn)行完第一條之后進(jìn)入數(shù)據(jù)庫看可以看到Django_Test庫中有了下面這些表 +----------------------------+ | Tables_in_Django_Test | +----------------------------+ | auth_group | | auth_group_permissions | | auth_permission | | auth_user | | auth_user_groups | | auth_user_user_permissions | | django_admin_log | | django_content_type | | django_migrations | | django_session | +----------------------------+ 這些表也不是空表,都是有數(shù)據(jù)的而且比較重要。至于為什么后面再說。 第二條命令是將我們新建的表(也就是models里面的類)給記錄到APP的migrations目錄中的一個(gè)文件。這個(gè)目錄在python manage.py startapp的時(shí)候自動(dòng)創(chuàng)建,且里面自帶一個(gè)__init__.py。這些文件是作為后續(xù)數(shù)據(jù)庫版本的升降級(jí)操作的依據(jù),所以也不應(yīng)該擅自刪改。此時(shí)migrations目錄下應(yīng)該就有了一個(gè)0001_initial.py的文件了。 第三條則是把本地的數(shù)據(jù)庫信息同步到真的數(shù)據(jù)庫中,在操作完這個(gè)命令之后,我們可以看到數(shù)據(jù)庫中會(huì)多出一張new_app_character的表,其表結(jié)構(gòu)是和我們的Character類定義的相同。 ● 如何初始化migrations相關(guān)數(shù)據(jù) 可以看到有django_migrations這個(gè)表,說明在數(shù)據(jù)庫升降級(jí)的時(shí)候并不是單單看migrations目錄下面有哪些文件的,而是參考了數(shù)據(jù)庫中的數(shù)據(jù)。所以說數(shù)據(jù)庫中的那些初始化信息也很重要。這么一來,想要初始化所有數(shù)據(jù)庫數(shù)據(jù)就不能簡單的把migrations下面的文件刪光了。比較徹底的做法是把整個(gè)Django_Test數(shù)據(jù)庫drop掉,然后把migrations中的除了__init__外所有文件刪掉。再重新建庫,migrate,makemigrations這樣來做。 當(dāng)然在正式生產(chǎn)中肯定不能這么做,這就表明,對(duì)于數(shù)據(jù)庫初始化出來的數(shù)據(jù)以及migrations中的文件一定不能輕易修改刪除。理想的做法是對(duì)表結(jié)構(gòu)做出調(diào)整之后先makemigrations再migrate一下。 這里順便一提,進(jìn)行數(shù)據(jù)庫版本升級(jí)的時(shí)候django做得還是很智能的。比如我一開始沒有age字段,migrate的時(shí)候添加了age字段但是沒指定default,django就提示說如果表里原來有記錄的話就不知道新插入這個(gè)age字段該取什么值,讓我選擇時(shí)系統(tǒng)自動(dòng)給一個(gè)default值呢還是回models.py中手動(dòng)指定一個(gè)default值。 通過ORM進(jìn)行增刪查改以后慢慢說,這里只展示一下: 首先通過后臺(tái)插入數(shù)據(jù): INSERT INTO new_app_character (name,age) VALUES('Django',50); 需要注意的是一定要給出(name,age),因?yàn)樯厦娑x的時(shí)候我們沒有給出主鍵,orm自動(dòng)為這個(gè)表添加一個(gè)名為id的字段作為主鍵,從1開始自然計(jì)數(shù)。 然后在程序中比如在views中可以: from .models import Character from django.http import HttpResponse def new_first_page(request): namelist = Character.objects.all() res = '' for name in namelist: res += '<p>%s</p>' % name return HttpResponse(res) 這就實(shí)現(xiàn)了通過ORM從數(shù)據(jù)庫中取出數(shù)據(jù)的目的了。
■ Django模板初步 如上面目錄中那樣,在項(xiàng)目的根目錄級(jí)下有一個(gè)templates目錄,里面存放的自然就是項(xiàng)目的模板文件了。 在settings.py中,可以看到一個(gè)配置項(xiàng)是TEMPLATES,下面有一個(gè)DIRS的配置項(xiàng),這是一個(gè)list。其中默認(rèn)的是那個(gè)templates目錄,也可以自己手動(dòng)再增加一些其他的目錄,這樣就實(shí)現(xiàn)了多個(gè)模板目錄的設(shè)置了。至于模板的語法和渲染模板的方法以下面這個(gè)例子做個(gè)最簡單的說明: ####模板文件test.html#### <h1>{{ label }}</h1> ####某個(gè)views.py文件中增加內(nèi)容#### from django.shortcuts import render def template_show(request): context = {} context['label'] = 'Hello World' return render(request, 'test.html', context) 之后在相應(yīng)的APP的urls.py中添加對(duì)url的路由,訪問相關(guān)頁面就可以看到html為<h1>Hello World</h1>的頁面了。這里可以注意一下render方法和flask中render_template的一些不同,為了避免大量變量渲染時(shí)引起的參數(shù)過長的問題,把所有參數(shù)都維護(hù)到一個(gè)context字典里面可以說是一個(gè)非常好的辦法。 Django在渲染模板的時(shí)候,首先將上下文數(shù)據(jù)context傳遞給模板文件,分析模板文件中需要渲染的部分,具體化之后又自動(dòng)生成了HTTPResponse返回,所以我們?cè)谶@里可以直接return render方法的返回。結(jié)合上面說的ORM,可以從數(shù)據(jù)庫中取出數(shù)據(jù),進(jìn)行一定程度的處理之后再傳遞給模板,這就完成了一個(gè)非常MVC的流程。 之前在學(xué)習(xí)flask的時(shí)候就聽說過Django的模板和Jinja2不太一樣,但是粗粗看了下教程,發(fā)現(xiàn)是大同小異的,這里就不再費(fèi)口舌說一些基本的東西了。主要補(bǔ)充一些據(jù)說和jinja2不同的地方(動(dòng)態(tài)更新中...),比如在Django的模板中,無參函數(shù)的調(diào)用時(shí)不用機(jī)加括號(hào)的,但是jinja2是和原生的Python一樣加括號(hào)。 總體而言,Django的模板系統(tǒng)比Jinja2有更多的限制,也更不像是python或者其他的編程語言。比如Jinja2中可能會(huì)有{% if name == 'takanashi' %}這樣的表達(dá)但是據(jù)說Django是不行的等等。
● 關(guān)于宏 之前似乎沒有提到,django的模板系統(tǒng)中是不存在宏這種設(shè)定的。也就是說不能直接使用{% macro xxx %}這樣的方式來定義宏從而減少編寫重復(fù)代碼的次數(shù)。 不過好在有解決方案就是django-macros。pip install一下之后,在項(xiàng)目的settings中的INSTALLED_APPS中添加'macros',然后再在相關(guān)模板中{% load macros %}之后,這個(gè)模板文件里就可以自由使用macro了。 ● 關(guān)于自動(dòng)反轉(zhuǎn)義 如果后端傳到模板的字符串中含有一些HTML敏感的字符比如<,>,&等,Django在渲染模板的時(shí)候會(huì)自動(dòng)將這些內(nèi)容進(jìn)行一個(gè)反轉(zhuǎn)義,從而使頁面可以正確地顯示這些文本。比如 <div>{{ text }}</div>是模板,然后后端的ctx={'text': 'Hello,<b>World</b>'},渲染出得到的頁面會(huì)是Hello,<b>World</b>,而不是Hello,World這樣加粗字體的。 有時(shí)如果需要反過來,不要他強(qiáng)行自動(dòng)轉(zhuǎn)義,則可以在模板中使用{% autoescape %}標(biāo)簽,包含在{% autoescape off %}{% endescape %}這個(gè)block中的所有待渲染的內(nèi)容,是不會(huì)自動(dòng)轉(zhuǎn)義,而是保持HTML原有的樣式的。
■ Django的表單處理 任何一個(gè)Web框架都少不了對(duì)表單的支持。下面演示一個(gè)最簡單的通過POST方法發(fā)送數(shù)據(jù)給WEB應(yīng)用然后將數(shù)據(jù)存入數(shù)據(jù)庫之后返回一個(gè)頁面的Django的結(jié)構(gòu)。models等一些數(shù)據(jù)復(fù)用了前面提到過的東西: <!-- 模板頁面 --> <form method="post" action="/new/process/"> {% csrf_token %} <input type="text" name="name" /> <input type="number" name="age" /> <input type="submit" value="Submit" /> </form> <p>{{ rlt }}</p> <p>{{ age }}</p> 在后臺(tái)的new_app/views.py中: from django.shortcuts import render from django.template.context_processors import csrf def process(request): ctx = {} ctx.update(csrf(request)) if request.POST: ctx['rlt'] = request.POST['name'] ctx['age'] = request.POST['age'] return render(request, 'formtest.html', ctx)
這里需要注意的是在模板中我們就做了csrf的處理,然后在后臺(tái)也要進(jìn)行一個(gè)csrf的處理,然后根據(jù)處理完之后的context再來渲染頁面??梢钥吹?,在Django里面默認(rèn)不做出對(duì)某個(gè)url的訪問方法的限制。所以在urls.py中定義了到這個(gè)處理函數(shù)的路由(/new/process)后訪問這個(gè)路由,首先是GET方法獲取頁面,此時(shí)因?yàn)閏ontext中沒有定義rlt和age這兩個(gè)模板中的變量,所以頁面下方是兩個(gè)空行。然后填完數(shù)據(jù)表單提交,因?yàn)閒orm標(biāo)簽的action指向還是這個(gè)路由,所以POST數(shù)據(jù)到process函數(shù)下面。因?yàn)槭荘OST,進(jìn)入分支,context中有了關(guān)于rlt和age的值,于是就渲染出有值的頁面了。 如果需要將POST上來的數(shù)據(jù)根據(jù)剛才定義的model存進(jìn)數(shù)據(jù)庫那么可以: def process(request): ctx = {} ctx.update(csrf(request)) if request.POST: name,age = request.POST.get('name'),request.POST.get('age') new_record = Character(name=name,age=age) new_record.save() return render(request, 'formtest.html', ctx) 這也是大概地展示一下如何用ORM進(jìn)行“增”的數(shù)據(jù)庫操作
在flask中,我們用到了wtforms來進(jìn)行方便的表單渲染和管理,Django也有類似的功能。而且Django把表單管理的模塊也一并整合到了Django這個(gè)大模塊中,所以使得表單的描述和數(shù)據(jù)庫表結(jié)構(gòu)的描述可以統(tǒng)一起來。而這兩者在實(shí)際中又常常是互相關(guān)聯(lián)的。比如上面的那個(gè)表單,我們可以做以下改造: from django import forms class CharacterForm(froms.Form): name = forms.Charfield(max_length=200,label="Your Name") age = forms.IntegerField(min_value=18) def process(request): context = {} context.update(csrf(request)) if request.POST: form = CharacterForm(request.POST) if form.is_valid(): #do something with data form = CharacterForm() context['form'] = form return render(request, 'form_test.html', context) ####在模板中可以這么寫#### {{ form.as_p }} ####這樣就可以自動(dòng)地生成一個(gè)表單了#### 雖說是自動(dòng)生成了表單,但是需要注意的是并不是全部要素,只是要填的一些字段和相關(guān)的label等等,比如<form> 標(biāo)簽已經(jīng)submit的input等還是要自己手寫的,相當(dāng)于as_p方法只是放回了純的變成了p標(biāo)簽形式的表單html代碼。 ● 對(duì)于ajax發(fā)起post請(qǐng)求的csrf處理 以上對(duì)于表單發(fā)起post請(qǐng)求的舉例都是通過了<form>這個(gè)DOM來實(shí)現(xiàn)的。但是在有ajax的時(shí)候我們可以不必拘泥于form而采用更加自由的ajax發(fā)起POST請(qǐng)求。這就引起了一個(gè)小問題,在form的時(shí)候我們只要在前端模板里面寫上{% csrf_token %}就可以自動(dòng)給我們的表單DOM增加防csrf驗(yàn)證功能。但是在ajax的時(shí)候如何將這部分信息和一個(gè)特定的ajax請(qǐng)求聯(lián)系起來。辦法有很多種,比如可以在頁面中引入額外的一個(gè)csrf.js文件來適應(yīng)csrf驗(yàn)證【參考https://code./django/django-csrf.html】。 一個(gè)更加簡單的方法是在全局ajax設(shè)置中增肌相關(guān)csrf驗(yàn)證的設(shè)置: $.ajaxSetup({ data: { csrfmiddlewaretoken: '{{ csrf_token }}' } });
這個(gè)辦法需要注意的是1. 這段代碼應(yīng)該寫在模板文件的<script>標(biāo)簽中因?yàn)閧{ csrf_token }}這個(gè)只有在模板里面才能被識(shí)別,寫在.js中是無法被識(shí)別的。2. csrf_token要用{{ }}括起來而不是{% %},具體原因不知道。??傊挥羞@樣才行。 還有一個(gè)不是辦法的辦法,就是在相關(guān)的ajax的POST發(fā)向的那個(gè)view,from django.views.decorators.csrf import csrf_exempt,然后在這個(gè)view函數(shù)上面增加裝飾器@csrf_exempt來迫使這個(gè)view接受到的請(qǐng)求不進(jìn)行CSRF驗(yàn)證。 ■ Django自帶的WebApp 之所以稱Django是一個(gè)很大的框架,原因在于它自帶了很多功能,其中感覺到最神奇的就是這個(gè),自帶的一個(gè)管理數(shù)據(jù)庫的APP。這個(gè)APP通常在settings.py的INSTALLED_APPS中已經(jīng)預(yù)安裝好,并且在項(xiàng)目根目錄的urls.py中還設(shè)置好了url,通常是[site]/admin來訪問。 這個(gè)App其實(shí)是一個(gè)管理數(shù)據(jù)庫模型的一個(gè)App,在通過它管理模型之前還需要在相關(guān)模型所在的應(yīng)用中的admin.py下進(jìn)行模型的注冊(cè)。做法是: ###admin.py### from models import Character admin.site.register(Character)
如果是第一次訪問管理界面,那么需要用manage.py工具的createsuperuser命令來建立管理員用戶,按照字符界面的提示輸入管理員的用戶名密碼等信息來注冊(cè)管理員用戶。 登錄之后我們可以看到這樣的界面: 可以看到我們注冊(cè)的new_app下面的Character模型已展示在頁面上了。至于上面的是Django預(yù)裝的Auth模塊,我們以后還可以用它來進(jìn)行用戶的管理。因?yàn)橛脩粽f到底也只是數(shù)據(jù)庫中的一張表,一個(gè)模型而已。另外這個(gè)web界面管理真的十分方便,這個(gè)Character的模型還略顯簡單了點(diǎn),如果是個(gè)稍微復(fù)雜一點(diǎn)的模型比如: class Role(models.Model): role_code = models.IntegerField(primary_key=True) role_name = models.CharField(max_length=100) def __unicode__(self): return self.role_name class User(models.Model): name = models.CharField(max_length=100,primary_key=True) age = models.IntegerField() email = models.EmailField() role = models.ForeignKey(Role) def __unicode__(self): return '%s(%s) Mail:%s' % (self.name,self.age,self.email) 涉及到了外鍵的設(shè)置,此時(shí)如果先點(diǎn)擊Role旁邊的Add,可以手動(dòng)為數(shù)據(jù)庫中添加角色信息: 當(dāng)添加完一些信息之后,到添加User的界面中還可以看到剛才添加過的角色信息供選擇: 需要注意的是添加一條記錄后會(huì)給出一個(gè)提示中有中文,再有就是比如這邊角色的幾個(gè)名字,這些都是根據(jù)__unicode__的返回決定的。所以在model類實(shí)現(xiàn)的時(shí)候需要實(shí)現(xiàn)__unicode__方法,并且想好一種表達(dá)清晰的返回。在開發(fā)階段進(jìn)行小批量的數(shù)據(jù)插入的時(shí)候,這個(gè)界面顯然比到后臺(tái)用SQL插入數(shù)據(jù)要友好很多。 此外如果需要對(duì)某些字段進(jìn)行隱藏處理的話可以修改一下new_app/admin.py中的內(nèi)容,之前我們直接admin.site.register了所有模型類。如果想要進(jìn)行字段顯示的調(diào)整可以在new_app/admin.py中進(jìn)行: from django.contrib import admin from models import User,Role class UserShow(admin.ModelAdmin): fields = ('name','age','role') #沒加email admin.site.register(User,UserShow) admin.site.register(Role) #Role表不變,但是User表有所調(diào)整
這樣的話在界面上無論是讀取User記錄的信息還是新增一個(gè)User的記錄,Email字段就都不會(huì)顯示出來了。其實(shí)這么做的原理顯而易見,就是把一個(gè)ModelAdmin類的衍生類和我們的模型類關(guān)聯(lián)起來。上面這個(gè)ModelAdmin的子類UserShow用到了fields這個(gè)屬性來設(shè)置顯示不顯示,其實(shí)還有更強(qiáng)大的屬性比如fieldsets: class UserShow(admin.ModelAdmin): fieldsets = ( ['基本信息',{ 'classes' : ('collapse',), # CSS設(shè)置 'fields' : ('name','age') }], ['更多信息',{ 'classes' : ('collapse',), 'fields' : ('email','role') }] )
如果UserShow跟上面這樣寫的話,得到的界面就是: 基本信息和更多信息兩個(gè)Panel都可以通過點(diǎn)擊旁邊的hide和show來隱藏顯示。 類似的,設(shè)置這個(gè)類中的list_display屬性可以改變展示界面的字段展示,下面貼出代碼和修改前后界面的圖: class UserShow(admin.ModelAdmin): list_display = ('name','age','email','role') 改變前: 改變后:
■ 用戶驗(yàn)證與管理 之前提到過Django預(yù)裝了Auth這個(gè)用戶管理模塊,那么要怎么使用。首先在settings.py中的INSTALLED_APPS中一般是有admin.contrib.auth這個(gè)APP在了。在http://site/admin這個(gè)管理界面上可以在系統(tǒng)自帶的那個(gè)Users中去添加刪除用戶,當(dāng)然這個(gè)頁面最好不要暴露給一般用戶,正確的做法應(yīng)該是自己設(shè)計(jì)一個(gè)表單收集用戶提供的信息,然后根據(jù)信息進(jìn)行后臺(tái)的相關(guān)操作。因?yàn)橛脩舻男畔⒁苍贒jango設(shè)計(jì)的用戶系統(tǒng)的框架內(nèi)所以后臺(tái)那些操作自然也是Django自身提供的一些封裝好的方法。 在進(jìn)行下面的說明之前,我們先建立一個(gè)名為users的App來單獨(dú)進(jìn)行用戶的一些操作:python manage.py startapp users ● 用戶的登錄、驗(yàn)證和登出 前端模板: <!-- login.html --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>{{ request.path }}</title> </head> <body> <form method="post" action="/users/login"> {% csrf_token %} {{ form.as_p }} <input type="submit" value="Go!" /> </form> </body> </html> 后端views.py: # -*- coding: utf-8 -*- from __future__ import unicode_literals from django.shortcuts import render,redirect from django.template.context_processors import csrf from django import forms from django.contrib.auth import authenticate,login # Create your views here. class LoginForm(forms.Form): username = forms.CharField(max_length=100,min_length=3) password = forms.CharField(min_length=8,widget=forms.PasswordInput) email = forms.EmailField() def user_login(request): context = {} context.update(csrf(request)) login_form = LoginForm() if request.POST: username = password = '' username = request.POST.get('username') password = request.POST.get('password') user = authenticate(username=username,password=password) if user is not None and user.is_active: login(request,user) return redirect('/') else: return redirect('/users/login') context['form'] = login_form return render(request,'login.html',context)
這里比較新鮮的就是contrib.auth中的authentication和login兩個(gè)方法。當(dāng)用戶在表單中輸入信息然后POST上來之后,我們可以直接調(diào)用authentication方法來快速驗(yàn)證用戶密碼的正確性,如果驗(yàn)證通過將返回這個(gè)用戶的User對(duì)象。如果沒有通過則是返回None。需要注意的是Django內(nèi)建的User對(duì)象還具有active這個(gè)屬性即用戶是否在有效期內(nèi),如果不是的那么也不能將用戶放行。另外,自帶的這個(gè)驗(yàn)證機(jī)制似乎不能判別是用戶不存在還是密碼錯(cuò)誤導(dǎo)致的驗(yàn)證沒通過。 login方法則更像是一個(gè)狀態(tài)置活方法,通過驗(yàn)證之后調(diào)用這個(gè)方法來告訴系統(tǒng)當(dāng)前用戶XXX是登錄狀態(tài)的。在調(diào)用過login方法之后就可以保持整個(gè)會(huì)話過程中用戶身份的保持了。 在其他的視圖中,可以通過request.user這個(gè)對(duì)象來調(diào)用當(dāng)前登錄用戶相關(guān)的一些信息。比如user.get_username()返回用戶名,user.set_password('xxx')可以重設(shè)密碼,user.password則可以看到密碼的密文,date_joined和last_login分別可以看到賬號(hào)創(chuàng)建時(shí)間和上次登錄時(shí)間,不過這兩個(gè)時(shí)間的時(shí)區(qū)有點(diǎn)詭異,反正都不是東八區(qū)。user.check_password('xxx')可以對(duì)一段明文是否是密碼進(jìn)行驗(yàn)證,可以用于用戶登錄之后還需要驗(yàn)證密碼的場合比如授權(quán)等等。下面是一個(gè)在其他視圖(非登錄相關(guān)視圖)中調(diào)用user信息的示例: def auth_test(request): context = {} if request.user.is_authenticated(): return HttpResponse('<p>%s</p>' % request.user.get_username()) else: return redirect('/user/login')
這里用的判斷當(dāng)前是否有用戶的方式是用了if 語句判斷user.is_authenticated方法返回的狀態(tài)。其實(shí)還有更加方便的方法就是使用裝飾器login_required: from django.contrib.auth.decorators import login_required @login_required def auth_test(request): return HttpResponse('<p>%s</p>' % request.user.get_username()) 這里一個(gè)小問題是當(dāng)用戶處于登出狀態(tài)的時(shí)候,如果訪問@login_required修飾的路由函數(shù)會(huì)怎么樣。默認(rèn)情況下,會(huì)轉(zhuǎn)送到uri為accounts/login?next=/xxxx(auth_test的路由),如果沒有定義過accounts/login的話那么引起的就是404了。其實(shí)在使用@login_required的時(shí)候可以加上參數(shù)(login_url='/users/login')來把頁面重定向到我們?cè)O(shè)計(jì)好的/users/login下面去。只不過next這個(gè)參數(shù)還是沒有自帶的實(shí)現(xiàn)的,所以還需要我們?cè)趌ogin的路由函數(shù)中再添加對(duì)GET參數(shù)next的一些處理。如果不希望在后臺(tái)做判斷的話也可以在前臺(tái)的模板中進(jìn)行判斷,比如添加一條{% if user.is_authenticated %}(Django模板函數(shù)不加括號(hào)哦),具體就不寫出來了。 說到登出,登出更加簡單: from django.contrib.auth import logout def user_logout(request): logout(request) return redirect('/') 相比于login,logout只需要request這一個(gè)參數(shù)。
■ 用戶注冊(cè) 用戶注冊(cè)和用戶登錄類似,只不過接受表單的數(shù)據(jù)之后做的處理不太一樣。當(dāng)然我們可以手動(dòng)寫一個(gè)表單的html來做,不過對(duì)于簡單的用戶注冊(cè),還有更加方便的操作: from django.contrib.auth.forms import UserCreationForm from django.shortcuts import redirect,render from django.template.context_processors import csrf def user_register(request): if request.method == 'POST': form = UserCreationForm(request.POST) if form.is_valid(): new_user = form.save() return redirect('/') else: form = UserCreationForm() context = {'form':form} context.update(csrf(request)) return render(request, 'register.html', context) 前端模板: <form action="" method="post"> {% csrf_token %} {{ form.as_p }} <input type="submit" value="Submit" /> </form> 嘗試了一下,渲染出來的表單還是非常難看的。。
■ 自定義用戶表 如果覺得django.contrib.models.User這個(gè)模型不能滿足你的需求的話,也可以通過繼承這個(gè)模型來自定義一個(gè)用戶類。這個(gè)自定義的用戶類可以適用于之前所有User類可以用的方法和函數(shù)。相當(dāng)于是對(duì)django自帶的User類進(jìn)行了一個(gè)很好的擴(kuò)展。 繼承是一種辦法,另一種辦法就是自定義一個(gè)模型類之后,在類中增加一個(gè)OneToOneField映射到User這個(gè)表上。通過OneToOneField映射的好處就是在調(diào)用時(shí)可以直接request.user.xxx來調(diào)用我們自定義的類,十分方便。 如果對(duì)于定制要求比較高,那么也可以到更底層去,比如從AbstractBaseUser(User類的父類的父類)和AbstractUserManager開始繼承。 =============== 至此,Django的基本介紹結(jié)束,原博主還寫了兩篇關(guān)于用Web容器部署項(xiàng)目以及買服務(wù)器部署服務(wù)的,這里不太需要就不多說了。 這篇文章頂多是對(duì)Django有一個(gè)大概的印象,接下來我會(huì)看書本,把上面提到的知識(shí)細(xì)化,分塊記錄下來。 以上。 |
|