最近在學習Django,打算玩玩網頁後臺方面的東西,由於一直很好奇但卻沒怎麼接觸過。Django對我來講是一個全新的內容,思路想來也是全新的,或許並不能寫得很明白,因此你們就湊合着看吧~html
本篇筆記(其實個人全部筆記都是),並不會過於詳細的講解。所以若是有你們看不明白的地方,歡迎在我正版博客下留言,有時間的時候我很願意來這裏與你們探討問題。(固然,不能是簡簡單單就能夠百度到的問題-.-)python
我所選用的教材是《The Django Book 2.0》,本節是表單部分,對應書中第七章。linux
------------------------------------------------------------------------------------------------------------------------------------------------web
0、閱讀方法數據庫
本節筆記,略去不少書中學習過程與講解,建議在看完原書此節後,做總結複習之用。django
站點建立:django-admin.py startproject comebackubuntu
一、視圖中的HttpResponse瀏覽器
首先給咱們的代碼加上一個視圖,網址是"http://127.0.0.1:8000/",網站內容就是一個Hello World。安全
顯然,其在"/comeback/views.py"中的代碼內容是:bash
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方法好。
建議:
一、get方式的安全性較Post方式要差些,包含機密信息的話,建議用Post數據提交方式;
二、在作數據查詢時,建議用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版本 |