最近在學習Django,打算玩玩網頁后台方面的東西,因為一直很好奇但卻沒怎么接觸過。Django對我來說是一個全新的內容,思路想來也是全新的,或許並不能寫得很明白,所以大家就湊合着看吧~
本篇筆記(其實我的所有筆記都是),並不會過於詳細的講解。因此如果有大家看不明白的地方,歡迎在我正版博客下留言,有時間的時候我很願意來這里與大家探討問題。(當然,不能是簡簡單單就可以百度到的問題-.-)
我所選用的教材是《The Django Book 2.0》,本節是表單部分,對應書中第七章。
------------------------------------------------------------------------------------------------------------------------------------------------
0、閱讀方法
本節筆記,略去很多書中學習過程與講解,建議在看完原書此節后,作總結復習之用。
站點創建:django-admin.py startproject comeback
1、視圖中的HttpResponse
首先給我們的代碼加上一個視圖,網址是"http://127.0.0.1:8000/",網站內容就是一個Hello World。
顯然,其在"/comeback/views.py"中的代碼內容是:
from django.http import HttpResponse def hello(request): return HttpResponse("Hello World")
其中,HttpResponse對象,即request變量,是有很多成員(屬性和方法)的。通過他們,你可以知道很多信息,例如:正在加載這個頁面的用戶是誰,他用的是什么瀏覽器。
這里列舉一些屬性:
| 成員 | 說明 | 舉例 |
| request.path | 除域名以外的請求路徑,以斜杠(即 /)開頭 | ”/hello/“ |
| request.get_host() | 主機名(例如:通常所說的域名) | "127.0.0.1" or "www.example.com" |
| request.get_full_path() | 請求路徑,可能包含查詢字符串 | "/hello/?print=true" |
| request.is_secure() | 如果通過https訪問,返回True;否則,返回False | True or False |
還有一個屬性要重點說明,request.META,這是一個python字典,包含了所有本次HTTP請求的Header信息。這個信息是由用戶的瀏覽器所提交的:
| 成員 | 說明 | 備注 |
| HTTP_REFERER | 進站前鏈接網頁(如果有的話) | 這是REFERRER的筆誤-.-||| |
| HTTP_USER_AGENT | 用戶瀏覽器的user-agent字符串(如果有的話) | 詳見這一篇博文 |
| REMOTE_ADDR | 客戶端IP | 如果經過代理服務器,那么是逗號分割的多個IP地址 |
應當注意,既然是用戶瀏覽器提交的,這個信息也就不一定靠譜。因此,應當使用下列方式讀取其中內容:
1. 使用 try / except 語句
def ua_display_good1(request): try: ua = request.META['HTTP_USER_AGENT'] except KeyError: ua = 'unknown' return HttpResponse("Your browser is %s" % ua)
2. 使用 python字典的 get()方法(推薦)
def ua_display_good2(request): ua = request.META.get('HTTP_USER_AGENT', 'unknown') return HttpResponse("Your browser is %s" % ua)
書中建議,你寫一個函數,把request.META中所有數據打印出來看看,比如這樣
1 def display_meta(request): 2 values = request.META.items() 3 values.sort() 4 html = [] 5 for k, v in values: 6 html.append('<tr><td>%s</td><td>%s</td></tr>' % (k, v)) 7 return HttpResponse('<table>%s</table>' % '\n'.join(html))
request.META的內容太多了,我把其內容做了初步的整理和翻譯,有興趣的同學可以到本節末尾附錄中看。
當然,也可以用模板實現,而非手動輸入代碼,這里不多說。
request中,還有兩個屬性,內含用戶所提交的信息:
| 成員 | 說明 |
| request.GET | HTML中的<form>標簽提交的 or URL中的查詢字符串(the query string) |
| request.POST | HTML中的<form>標簽提交的 |
這兩個都是類字典對象,即其實現了字典的所有成員,另外還有些字典沒有的成員。
2. 利用GET請求,查詢一本書籍
要做的事情很簡單,做一個書籍查詢頁面,可以輸入書名查書的信息。做法如下:
1. 按照模型一節所講,創建書籍的數據庫。
2. 在"/books/"下,創建幾個文件
search_form.html
1 <html> 2 <head> 3 <title>Search</title> 4 </head> 5 <body> 6 {% if errors %} 7 <ul> 8 {% for error in errors %} 9 <li>{{ error }}</li> 10 {% endfor %} 11 </ul> 12 {% endif %} 13 <form action="" method="get"> 14 <input type="text" name="q"> 15 <input type="submit" value="Search"> 16 </form> 17 </body> 18 </html>
search_results.html
1 <p>You searched for: <strong>{{ query }}</strong></p> 2 3 {% if books %} 4 <p>Found {{ books|length }} book{{ books|pluralize }}.</p> 5 <ul> 6 {% for book in books %} 7 <li>{{ book.title }}</li> 8 {% endfor %} 9 </ul> 10 {% else %} 11 <p>No books matched your search criteria.</p> 12 {% endif %}
views.py
1 from django.shortcuts import render_to_response 2 from books.models import Book 3 4 # Create your views here. 5 6 def search(request): 7 errors = [] 8 if 'q' in request.GET: 9 q = request.GET['q'] 10 if not q: 11 errors.append('Enter a search term.') 12 elif len(q)>20: 13 errors.append('Please enter at most 20 characters.') 14 else: 15 books = Book.objects.filter(title__icontains=q) 16 return render_to_response('search_results.html', {'books': books, 'query': q}) 17 return render_to_response('search_form.html', {'errors': errors})
3. 在url中的urlpatterns屬性內,加入如下一條 url(r'^search/$', views.search), 並對應寫出from...import語句
4. 運行站點:python manage.py runserver
5. 打開搜索頁面:http://127.0.0.1:8000/search/
3. 表單簡介
在HTTP中,表單(form標簽),是用來提交數據的,其action屬性說明了其傳輸數據的方法:如何傳、如何接收。
訪問網站時,表單可以實現客戶端與服務器之間的通信。例如我們上面的查詢書籍,就用到了表單(其屬性中,action=get)。再比如說注冊與登陸,也是要用到表單的。但這里由於涉及到隱私問題,需要保證數據傳輸的安全性,因此其傳輸方法就應當使用post而非get。
總之,對客戶端來說,表單就是用來向服務器提交數據的;而對服務器來說,表單就是你提供給客戶端的發送信息的渠道,你需要對用戶發送來的信息進行處理和響應,以達到頁面的交互。
3+. get與post方法簡介
這里做一些擴展——介紹一下表單的傳輸方法。
表單,一共有四種數據傳輸方法(即action的值):get、post、put、delete,即查、改、增、刪。
比如,上面查詢書籍的 search_form.html 代碼中,用的就是get方法。
由於put和delete都可以用post實現,因此往往只使用get和post兩種,甚至傳統的Web MVC框架基本上都只支持這兩種HTTP方法(-.-||)。這里,暫不介紹put和delete方法。
首先給出一段百度知道上對於get和post的簡介,原文作者是tawa08,原文在這里。
1. get是從服務器上獲取數據,post是向服務器傳送數據。
2. get是把參數數據隊列加到提交表單的action屬性所指的url中,值和表單內各個字段一一對應,在url中可以看到。
post是通過http post機制,將表單內各個字段與其內容放置在html header內一起傳送到action屬性所指的url地址。
用戶看不到這個過程。
3. 對於get方式,服務器端用Request.QueryString獲取變量的值,對於post方式,服務器端用Request.Form獲取提交的數據。
4. get傳送的數據量較小,不能大於2KB。post傳送的數據量較大,一般被默認為不受限制。
但理論上,IIS4中最大量為80KB,IIS5中為100KB。
5. get安全性非常低,post安全性較高。但是執行效率卻比Post方法好。
建議:
1、get方式的安全性較Post方式要差些,包含機密信息的話,建議用Post數據提交方式;
2、在做數據查詢時,建議用Get方式;而在做數據添加、修改或刪除時,建議用Post方式;
如果希望對get和post進一步了解,那我這里推薦一篇文章,雖然長但卻清晰而且很全:為什么大型網站都采用get方法,而非post方法。
至此,對於傳輸數據方法的介紹告一段落,咱們言歸正傳。
4. CSRF簡介
由於我們要使用django中的form庫,而且要用到post,所以便需要了解CSRF。
CSRF,Cross Site Request Forgery,跨站請求偽造,這是一種黑客攻擊方式,這里不過多介紹。你只需知道,當你使用表單傳輸數據時,有可能會接觸這種攻擊方式。因此,我們學習表單,最好知道這種攻擊方式的存在。對於想深入了解的同學,我推薦一篇博文:淺談CSRF攻擊方式。
從之前對get和post的介紹中,大家可以了解到,在標准的用法中,get由於毫無安全性可言,因此只應用作數據的查詢;而一旦涉及到數據的添加、修改、刪除時,則一定要采用post方式。那么,只要網站設計的符合規范,針對get的CSRF攻擊便無從談起。
因此,django則假設大家遵守這個標准,只在除了get之外那三種方法中才有針對CSRF的防御機制。
綜上,在django中,若你接觸到form中的post,要么就只使用get,要么就關了CSRF防御機制,要么就正確打開並使用CSRF防御機制,若不正確設置則無法使用form庫。
這里給出官方的設置CSRF防御機制的步驟,若想進一步了解,參見官方文檔:
1. 在所有使用post方法的模板(html)中,做如下修改:
把表單開頭的代碼 <form action="." method="post">
改成這樣 <form action="." method="post">{% csrf_token %}
2. 在所有上面修改過的模板對應的視圖(views.py)中,做如下修改:
把原視圖代碼,比如這樣的
from django.shortcuts import render_to_response def my_view(request): return render_to_response("a_template.html")
改成這樣
from django.core.context_processors import csrf from django.shortcuts import render_to_response def my_view(request): c = {} c.update(csrf(request)) return render_to_response("a_template.html", c)
5. email設置
我們后面要發送郵件,因此還需要先設置好email相關的內容。
先說一下后面具體要用django.form做什么:我們要做一個表單,效果如下圖:

點擊Submit之后,網頁后台會發送一封郵件。標題就是hello,內容是hi!,從郵箱A發送到郵箱B。這兩個郵箱都是我們提前設置好的。
我們設想的場景就是:這是一個網站,網站的訪問者可以通過這里直接向網站的制作者發送郵件。
這個過程,是需要用到郵箱A的SMTP服務的,這需要你的開通。比如我用的qq郵箱,開通方式就如教程所說。
另外,這過程是django后台做的,那你自然需要告訴django你的郵箱用戶名密碼,還有SMTP服務的主機和端口,這需要在settings.py中添加以下參數:
EMAIL_HOST = 'smtp.qq.com' EMAIL_PORT = 25 EMAIL_HOST_USER = '820645278@qq.com' EMAIL_HOST_PASSWORD = 'nicai'
這樣一來,你便可以讓django從后台幫你用你設置的這個郵箱(EMAIL_HOST_USER)發送郵件了。
這個存兩個疑問,希望高手予以解答:
1. 為什么把EMAIL_PORT參數屏蔽了,仍可以正常運行?難道端口可以自動找?
2. 本例原意是郵箱A由訪問者輸入,但我這種設置方法則鎖定郵箱A必須是設置的這個郵箱,於是這個Email的輸入框形同虛設。
比如我就像下面代碼中那樣實現,send_mail()中的變量f若不是820645278@qq.com,則會報錯:
SMTPSenderRefused at /contact/ (501, 'mail from address must be same as authorization user', u'82064527@qq.com')
如果我想達到訪問者可以用任意郵箱訪問的效果,那么我應當如何設置呢?
6. django中的form庫:django.form
了解了表單、django中的CSRF防御機制、email的設置,下面我們終於可以介紹django.form了。
django是框架,那么它的存在始終只有一個目的:讓你寫網站更加方便。django.form,就是一個可以讓你快速寫出表單的庫。(不用form庫的寫法,原書中有,有興趣的可以去看看,這里不寫了)
具體例子上面介紹email設置時已經說過,下面直接給出實現步驟:
0. 把上面的email設置做好
1. 創建"/contact/"目錄
2. 在其中創建一個空的__init__.py,以及下列文件
contact_form.html
1 <html> 2 <head> 3 <title>Contact us</title> 4 </head> 5 <body> 6 <h1>Contact us</h1> 7 8 {% if form.errors %} 9 <p style="color: red;"> 10 Please correct the error{{ form.errors|pluralize }} below. 11 </p> 12 {% endif %} 13 14 {% csrf_token %} 15 16 <form action="" method="post">{% csrf_token %} 17 <table> 18 {{ form.as_table }} 19 </table> 20 <input type="submit" value="Submit"> 21 </form> 22 </body> 23 </html>
forms.py
1 from django import forms 2 3 class ContactForm(forms.Form): 4 subject = forms.CharField() 5 email = forms.EmailField(required=False) 6 message = forms.CharField()
views.py
1 from django.core.mail import send_mail 2 from django.core.context_processors import csrf 3 from django.shortcuts import render_to_response, RequestContext 4 from contact.forms import ContactForm 5 from django.http import HttpResponseRedirect 6 7 def thanks(request): 8 return render_to_response('thanks.html') 9 10 def contact(request): 11 c = {} 12 c.update(csrf(request)) 13 if request.method == 'POST': 14 form = ContactForm(request.POST) 15 if form.is_valid(): 16 cd = form.cleaned_data 17 f = cd.get('email', '820645278@qq.com') 18 if f == '': f = '820645278@qq.com' 19 send_mail( 20 cd['subject'], 21 cd['message'], 22 f, 23 ['icedream@sjtu.edu.cn'], 24 ) 25 return HttpResponseRedirect('/contact/thanks/', {'method': request.method}) 26 else: 27 form = ContactForm() 28 c['form'] = form 29 return render_to_response('contact_form.html', c, context_instance=RequestContext(request))
thanks.html
1 <html> 2 <body> 3 Thanks! 4 </body> 5 </html>
3. 在url的urlpatterns屬性內,加入如下兩條 url(r'^contact/$', contact), url(r'^contact/thanks/$', thanks), 並對應寫出from...import語句
4. 運行站點:python manage.py runserver
5. 打開站點聯系表單頁面:http://127.0.0.1:8000/contact/
6. 如果成功,你應該能在郵箱B(即代碼中icedream@sjtu.edu.cn)中收到你用郵箱A(即代碼中820645278@qq.com)所發的郵件。
7. 總結:這一節都講了些什么
這一節首先介紹了之前一直在使用卻並不了解的HttpResponse對象,
然后介紹了表單、GET和POST方法、CSRF攻擊方式、email設置,
最后介紹了django中的表單(form)庫,以及如何使用它做出網站的表單。
------------------------------------------------------------------------------------------------------------------------------------------------
至此,“表單”一章筆記完成,django基礎部分學習完畢。后面開始高級部分,下一章是“高級視圖與URL配置”。
附錄1+. HttpResponse.META內容
| 名稱 | 值(斷句方式僅供參考) | 參考翻譯 |
| CLUTTER_IM_MODULE | xim | CLUTTER輸入法模塊 |
| COLORTERM | gnome-terminal | 終端配色 |
| COMPIZ_CONFIG_PROFILE | ubuntu | 特效配置資料 |
| CONTENT_LENGTH | 內容長度 | |
| CONTENT_TYPE | text/plain | 內容類型 |
| CSRF_COOKIE | R6SCXazGfl9QGZ2YsCI3VniLFiYNeOUj | CSRFcookie |
| DBUS_SESSION_BUS_ADDRESS | unix:abstract=/tmp/dbus-HALHk0izgV | 數據總線會話 總線地址 |
| DEFAULTS_PATH | /usr/share/gconf/ubuntu.default.path | 默認路徑 |
| DESKTOP_SESSION | ubuntu | 桌面會話 |
| DISPLAY | :0 | 展示 |
| DJANGO_SETTINGS_MODULE | comeback.settings | django設置模塊 |
| GATEWAY_INTERFACE | CGI/1.1 | 網關接口 |
| GDMSESSION | ubuntu | GDM會話 |
| GDM_LANG | zh_CN | GDM語言 |
| GNOME_DESKTOP_SESSION_ID | this-is-deprecated | GNOME桌面會話ID |
| GNOME_KEYRING_CONTROL | /run/user/1000/keyring-SkW2gT | GNOME鑰匙控制 |
| GNOME_KEYRING_PID | 2176 | GNOME鑰匙PID |
| GPG_AGENT_INFO | /run/user/1000/keyring-SkW2gT/gpg:0:1 | GPG代理信息 |
| GTK_IM_MODULE | fcitx | GTK輸入法模塊 |
| GTK_MODULES | overlay-scrollbar: unity-gtk-module |
GTK模塊 |
| HOME | /home/icedream | 家 |
| HTTP_ACCEPT | text/html, application/xhtml+xml, application/xml;q=0.9, image/webp, */*;q=0.8 |
HTTP接收 |
| HTTP_ACCEPT_ENCODING | gzip, deflate, sdch |
HTTP接收編碼 |
| HTTP_ACCEPT_LANGUAGE | zh-CN, zh;q=0.8, en;q=0.6, en-US;q=0.4, en-GB;q=0.2 |
HTTP接收語言 |
| HTTP_CONNECTION | keep-alive | HTTP連接 |
| HTTP_COOKIE | sessionid=8ifnqpfwvuh0pm04kq24zz4djw3lx4fp; csrftoken=R6SCXazGfl9QGZ2YsCI3VniLFiYNeOUj |
HTTPcookie 會話id & CSRF令牌 |
| HTTP_HOST | 127.0.0.1:8000 | HTTP主機 |
| HTTP_USER_AGENT | Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.134 Safari/537.36 |
HTTP用戶代理 |
| IM_CONFIG_PHASE | 1 | 輸入法配置階段 |
| INFOPATH | :/usr/local/texlive/2015/texmf-dist/doc/info | 信息路徑 |
| INSTANCE | Unity | 實例 |
| JOB | gnome-session | 工作 |
| LANG | zh_CN.UTF-8 | 語言 |
| LANGUAGE | zh_CN: zh |
語言 |
| LESSCLOSE | /usr/bin/lesspipe %s %s | LESS關閉 |
| LESSOPEN | | /usr/bin/lesspipe %s | LESS打開 |
| LOGNAME | icedream | 登陸用戶名 |
| LS_COLORS | rs=0: |
LS顏色 |
| MANDATORY_PATH | /usr/share/gconf/ubuntu.mandatory.path | MANDATORY(托管)路徑 |
| MANPATH | :/usr/local/texlive/2015/texmf-dist/doc/man | MAN路徑 |
| OLDPWD | /home/icedream | 舊的工作目錄 |
| PATH | /usr/local/sbin: /usr/local/bin: /usr/sbin: /usr/bin: /sbin: /bin: /usr/games: /usr/local/games: /usr/local/texlive/2015/bin/i386-linux |
路徑 |
| PATH_INFO | /cookie/ | 路徑信息 |
| PWD | /home/icedream/workspace/django/comeback | 當前目錄 |
| QT4_IM_MODULE | fcitx | QT4輸入法模塊 |
| QT_IM_MODULE | fcitx | QT輸入法模塊 |
| QT_QPA_PLATFORMTHEME | appmenu-qt5 | QT_QPA平台主題 |
| QUERY_STRING | 查詢字符串 | |
| REMOTE_ADDR | 127.0.0.1 | 遠程地址 |
| REMOTE_HOST | 遠程主機 | |
| REQUEST_METHOD | GET | 請求方法 |
| RUN_MAIN | true | 運行MAIN |
| SCRIPT_NAME | 腳本名稱 | |
| SERVER_NAME | localhost | 服務器名稱 |
| SERVER_PORT | 8000 | 服務器端口 |
| SERVER_PROTOCOL | HTTP/1.1 | 服務器協議 |
| SERVER_SOFTWARE | WSGIServer/0.1 Python/2.7.8 | 服務器軟件 |
| SESSIONTYPE | gnome-session | 會話類型 |
| SHELL | /bin/bash | 命令行 |
| SHLVL | 1 | 命令行層次 |
| SSH_AUTH_SOCK | /run/user/1000/keyring-SkW2gT/ssh | SSH_AUTH_SOCK |
| TERM | xterm | TERM |
| TEXTDOMAIN | im-config | 文本域 |
| TEXTDOMAINDIR | /usr/share/locale/ | 文本域目錄 |
| TZ | UTC | 時區 |
| UPSTART_EVENTS | started starting | UPSTART事件 |
| UPSTART_INSTANCE | UPSTART距離 | |
| UPSTART_JOB | unity-settings-daemon | UPSTART作業 |
| UPSTART_SESSION | unix:abstract=/com/ubuntu/upstart-session/1000/2178 | UPSTART會話 |
| USER | icedream | 用戶 |
| VTE_VERSION | 3603 | VTE版本 |
| WINDOWID | 69206028 | 窗口ID |
| XAUTHORITY | /home/icedream/.Xauthority | X權威 |
| XDG_CONFIG_DIRS | /etc/xdg/xdg-ubuntu: /usr/share/upstart/xdg: /etc/xdg |
XDG配置路徑 |
| XDG_CURRENT_DESKTOP | Unity | XDG當前桌面 |
| XDG_DATA_DIRS | /usr/share/ubuntu: /usr/share/gnome: /usr/local/share/: /usr/share/ |
XDG數據路徑 |
| XDG_GREETER_DATA_DIR | /var/lib/lightdm-data/icedream | XDG_GREETER數據路徑 |
| XDG_RUNTIME_DIR | /run/user/1000 | XDG運行時路徑 |
| XDG_SEAT | seat0 | XDG椅子 |
| XDG_SEAT_PATH | /org/freedesktop/DisplayManager/Seat0 | XDG椅子路徑 |
| XDG_SESSION_DESKTOP | ubuntu | XDG會話桌面 |
| XDG_SESSION_ID | c2 | XDG會話ID |
| XDG_SESSION_PATH | /org/freedesktop/DisplayManager/Session0 | XDG會話路徑 |
| XDG_SESSION_TYPE | x11 | XDG會話類型 |
| XDG_VTNR | 7 | XDG_VTNR |
| XMODIFIERS | @im=fcitx | XMODIFIERS |
| _ | /usr/bin/python | _ |
| wsgi.errors | ', mode 'w' at 0xb74d20d0> | WSGI錯誤 |
| wsgi.file_wrapper | wsgiref.util.FileWrapper | WSGI文件包裝 |
| wsgi.input | <socket._fileobject object="" at="" 0xb5a726ec=""> | WSGI輸入 |
| wsgi.multiprocess | False | WSGI多進程 |
| wsgi.multithread | True | WSGI多線程 |
| wsgi.run_once | False | WSGI運行一次 |
| wsgi.url_scheme | http | WSGI網址類型 |
| wsgi.version | (1, 0) | WSGI版本 |
