Django 視圖與網址進階
一 、在網頁上做加減法
1. 采用 /add/?a=4&b=5 這樣GET方法進行
1 2 3 |
django-admin.py startproject zqxt_views
cd zqxt_views
python manage.py startapp calc
|
自動生成目錄大致如下(因不同的 Django 版本有一些差異,如果差異與這篇文章相關,我會主動提出來,沒有說的,暫時可以忽略他們之間的差異,後面的教程也是這樣做):
1 2 3 4 5 6 7 8 9 10 11 12 13 |
zqxt_views/
├── calc
│ ├── __init__.py │ ├── admin.py
│ ├── models.py
│ ├── tests.py
│ └── views.py
├── manage.py
└── zqxt_views
├── __init__.py
├── settings.py
├── urls.py
└── wsgi.py
|
我們修改一下 calc/views.py文件
1 2 3 4 5 6 7 8 |
from django.shortcuts import render
from django.http import HttpResponse
def add(request): a = request.GET[ ‘a‘ ]
b = request.GET[ ‘b‘ ]
c = int (a) + int (b)
return HttpResponse( str (c))
|
註:request.GET 類似於一個字典,更好的辦法是用 request.GET.get(‘a‘, 0) 當沒有傳遞 a 的時候默認 a 為 0
接著修改 zqxt_views/urls.py 文件,添加一個網址來對應我們剛才新建的視圖函數。
Django 1.7.x 及以下的同學可能看到的是這樣的:
1 2 3 4 5 6 7 8 9 10 11 12 |
from django.conf.urls import patterns, include, url
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns(‘‘,
# Examples:
url(r ‘^add/$‘ , ‘calc.views.add‘ , name = ‘add‘ ), # 註意修改了這一行
# url(r‘^blog/‘, include(‘blog.urls‘)),
url(r ‘^admin/‘ , include(admin.site.urls)),
)
|
Django 1.8.x及以上,Django 官方鼓勵(或說要求)先引入,再使用。
1 2 3 4 5 6 7 8 9 |
from django.conf.urls import url
from django.contrib import admin
from calc import views as calc_views
urlpatterns = [
url(r ‘^add/$‘ , calc_views.add, name = ‘add‘ ), # 註意修改了這一行
url(r ‘^admin/‘ , admin.site.urls),
]
|
註意:低版本的 Django 也可以先引入,再使用
Django 2.0 版本的同學看下面(上面 Django 1.8 的在 2.0 中也可以用,是兼容的)
1 2 3 4 5 6 7 8 9 |
from django.contrib import admin
from django.urls import path
from calc import views as calc_views
urlpatterns = [
path( ‘add/‘ , calc_views.add, name = ‘add‘ ), # new
path( ‘admin/‘ , admin.site.urls),
]
|
我們打開開發服務器並訪問
1 2 3 |
python manage.py runserver 8002
默認端口是 8000,上面使用了自定義端口 8002
如果提示 Error: That port is already in use. 我們可以在後面加上端口號8001,8888等
|
打開網址:http://127.0.0.1:8002/add/ 就可以看到
MultiValueDictKeyError at /add/
這是因為我們並沒有傳值進去,我們在後面加上 ?a=4&b=5,即訪問 http://127.0.0.1:8002/add/?a=4&b=5
就可以看到網頁上顯示一個 9,試著改變一下a和b對應的值試試看?
2. 采用 /add/3/4/ 這樣的網址的方式
前面介紹的時候就說過 Django 支持優雅的網址
我們接著修改 calc/views.py文件,再新定義一個add2 函數,原有部分不再貼出
1 2 3 |
def add2(request, a, b):
c = int (a) + int (b)
return HttpResponse( str (c))
|
接著修改 zqxt_views/urls.py 文件,再添加一個新的 url
Django 1.7.x 及以下:
1 |
url(r ‘^add/(\d+)/(\d+)/$‘ , ‘calc.views.add2‘ , name = ‘add2‘ ),
|
Django 1.8.x - Django 1.11.x:
1 |
url(r ‘^add/(\d+)/(\d+)/$‘ , calc_views.add2, name = ‘add2‘ ),
|
Django 2.0 及以上:
1 |
path( ‘add/<int:a>/<int:b>/‘ , calc_views.add2, name = ‘add2‘ ),
|
我們可以看到網址中多了 (\d+), 正則表達式中 \d 代表一個數字,+ 代表一個或多個前面的字符,寫在一起 \d+ 就是一個或多個數字,用括號括起來的意思是保存為一個子組(更多知識請參見Python 正則表達式),每一個子組將作為一個參數,被 views.py 中的對應視圖函數接收。
我們再訪問 http://127.0.0.1:8002/add/4/5/ 就可以看到和剛才同樣的效果,但是這回網址更優雅了
二、URL name詳解
教程中所有的文件,沒有特別說明的,都是以 utf8 格式編碼的,請養成這個習慣。
1. 打開 zqxt_views/urls.py
1 2 3 4 5 6 7 8 9 10 |
from django.conf.urls import url
from django.contrib import admin
from calc import views as calc_views
urlpatterns = [
url(r ‘^add/$‘ , calc_views.add, name = ‘add‘ ),
url(r ‘^add/(\d+)/(\d+)/$‘ , calc_views.add2, name = ‘add2‘ ),
url(r ‘^admin/‘ , admin.site.urls),
]
|
url(r‘^add/$‘, calc_views.add, name=‘add‘), 這裏的 name=‘add‘ 是用來幹什麽的呢?
簡單說,name 可以用於在 templates, models, views ……中得到對應的網址,相當於“給網址取了個名字”,只要這個名字不變,網址變了也能通過名字獲取到。
為了進一步弄清這個問題,我們先建一個首頁的視圖和url
2. 修改 calc/views.py
1 2 3 4 5 6 7 8 9 |
from django.http import HttpResponse
from django.shortcuts import render
def index(request):
return render(request, ‘home.html‘ )
...此處省去一些代碼
|
render 是渲染模板,不懂先照著打就好。
3. 將 ‘calc‘ 這個 app 加入到 zqxt_views/settings.py 中
1 2 3 4 5 6 7 8 9 10 |
INSTALLED_APPS = [
‘django.contrib.admin‘ ,
‘django.contrib.auth‘ ,
‘django.contrib.contenttypes‘ ,
‘django.contrib.sessions‘ ,
‘django.contrib.messages‘ ,
‘django.contrib.staticfiles‘ ,
‘calc‘ ,
]
|
這樣,使用render的時候,Django 會自動找到 INSTALLED_APPS 中列出的各個 app 下的 templates 中的文件。
小提示,DEBUG=True 的時候,Django 還可以自動找到 各 app 下 static 文件夾中的靜態文件(js,css,圖片等資源),方便開發,後面有專門的章節會講這些。
4. 我們在 calc 這個 app 中新建一個 templates 文件夾,在templates中新建一個 home.html (關於模板更詳細的可以稍後看下一節)
文件 calc/templates/home.html 中寫入以下內容(保存時用 utf8 編碼)
1 2 3 4 5 6 7 8 9 10 11 |
<!DOCTYPE html>
< html >
< head >
< title >自強學堂</ title >
</ head >
< body >
< a href = "/add/4/5/" >計算 4+5</ a >
</ body >
</ html >
|
修改 zqxt_views/urls.py
1 2 3 4 5 6 7 8 |
...此處省去一些代碼
urlpatterns = [
url(r ‘^$‘ , calc_views.index, name = ‘home‘ ),
url(r ‘^add/$‘ , calc_views.add, name = ‘add‘ ),
url(r ‘^add/(\d+)/(\d+)/$‘ , calc_views.add2, name = ‘add2‘ ),
url(r ‘^admin/‘ , admin.site.urls),
]
|
運行開發服務器,我們訪問 http://127.0.0.1:8000/ 可以看到
我們計算加法的時候用的是 /add/4/5/ ,後來需求發生變化,比如改成 /4_add_5/,但在網頁中,代碼中很多地方都寫死的 /add/4/5/,比如模板中可能是這麽寫的
1 |
< a href = "/add/4/5/" >計算 4+5</ a >
|
如果這樣寫“死網址”,會使得在改了網址(正則)後,模板(template),視圖(views.py,比如用於URL跳轉),模型(models.py,獲取記錄訪問地址等)用了此網址的,都必須進行相應的更改,修改的代價很大,一不小心,有的地方沒改過來,就不能用了。
那麽有沒有更優雅的方式來解決這個問題呢?當然答案是肯定的。
我們先說一下如何用 Python 代碼獲取對應的網址(可以用在 views.py,models.py等各種需要轉換得到網址的地方):
我們在終端上輸入(推薦安裝 bpython, 這樣Django會用 bpython的 shell)
1 |
python manage.py shell
|
1 2 3 4 5 6 7 8 |
>>> from django.core.urlresolvers import reverse # django 1.4.x - django 1.10.x
或者
>>> from django.urls import reverse # Django 1.10.x - Django 2.x 新的,更加規範了
>>> reverse( ‘add2‘ , args = ( 4 , 5 ))
u ‘/add/4/5/‘
>>> reverse( ‘add2‘ , args = ( 444 , 555 ))
u ‘/add/444/555/‘
|
reverse 接收 url 中的 name 作為第一個參數,我們在代碼中就可以通過 reverse() 來獲取對應的網址(這個網址可以用來跳轉,也可以用來計算相關頁面的地址),只要對應的 url 的name不改,就不用改代碼中的網址。
在網頁模板中也是一樣,可以很方便的使用。
1 2 3 4 5 6 7 |
不帶參數的:
{% url ‘name‘ %}
帶參數的:參數可以是變量名
{% url ‘name‘ 參數 %}
例如:
< a href = "{% url ‘add2‘ 4 5 %}" >link</ a >
|
上面的代碼渲染成最終的頁面是
1 |
< a href = "/add/4/5/" >link</ a >
|
這樣就可以通過 {% url ‘add2‘ 4 5 %} 獲取到對應的網址 /add/4/5/
當 urls.py 進行更改,前提是不改 name(這個參數設定好後不要輕易改),獲取的網址也會動態地跟著變,比如改成:
1 |
url(r ‘^new_add/(\d+)/(\d+)/$‘ , calc_views.add2, name = ‘add2‘ ),
|
註意看重點 add 變成了 new_add,但是後面的 name=‘add2‘ 沒改,這時 {% url ‘add2‘ 4 5 %} 就會渲染對應的網址成 /new_add/4/5/
用在 views.py 或 models.py 等地方的 reverse函數,同樣會根據 name 對應的url獲取到新的網址。
想要改網址的時候,修改 urls.py 中的正則表達式部分(url 參數第一部分),name 不變的前提下,其它地方都不需要修改。
另外,比如用戶收藏夾中收藏的URL是舊的,如何讓以前的 /add/3/4/自動跳轉到現在新的網址呢?
要知道Django不會幫你做這個,這個需要自己來寫一個跳轉方法:
具體思路是,在 views.py 寫一個跳轉的函數:
1 2 3 4 5 6 7 8 9 |
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse # Django 1.4.x - Django 1.10.x
# from django.urls import reverse # Django 1.10.x - Django 2.x
def old_add2_redirect(request, a, b):
return HttpResponseRedirect(
reverse( ‘add2‘ , args = (a, b))
)
|
urls.py中:
1 2 |
url(r ‘^add/(\d+)/(\d+)/$‘ , calc_views.old_add2_redirect),
url(r ‘^new_add/(\d+)/(\d+)/$‘ , calc_views.add2, name = ‘add2‘ ),
|
這樣,假如用戶收藏夾中有 /add/4/5/ ,訪問時就會自動跳轉到新的 /new_add/4/5/ 了
Django 視圖與網址進階