Django學習路線之URL與視圖

經過前一天得學習(Django學習路線之從虛擬環境開始Django初體驗),相信你已經對Django已經有了基本的認識,今天來具體學習一下MVC思想的V(視圖)

  1. Django視圖
  2. URL映射
  3. URL中添加參數
  4. URL模塊化
  5. URL命名與反轉
  6. 指定默認的參數
  7. re_path函數

在這裏插入圖片描述

Django視圖

新建一個名爲django_view的項目:
在這裏插入圖片描述
並新建一個名爲news的app:
在這裏插入圖片描述
在新建的app裏的 views.py文件裏補充如下代碼:

from django.http import HttpResponse

# Create your views here.

def news(request):
    return HttpResponse("新聞首頁")

回到項目的urls.py文件做映射:

from news import views

剛導入的時候可能會有如下報錯:
在這裏插入圖片描述
這時候只需要對項目文件夾進行如下操作即可:
在這裏插入圖片描述
把映射寫完後:

from news import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('news/', views.news)
]

進入網站界面:
在這裏插入圖片描述
因爲沒有定義首頁,所以首頁會報錯,但是進入news的界面已經定義了,所以直接進入即可:
在這裏插入圖片描述

方法裏的request參數

接下來我們回到news的views.py文件,看一下request的類型:

def news(request):
    print(type(request))
    return HttpResponse("新聞首頁")

看一下request的類型:

<class ‘django.core.handlers.wsgi.WSGIRequest’>

可以看出這是一個類,下面看一下這個類的源碼:

from django.core.handlers.wsgi import WSGIRequest

點進去看一下:
在這裏插入圖片描述
看一下這個類裏的方法:
在這裏插入圖片描述
既然是類,我們就可以調用裏面的方法,下面來試一下調用method方法:
在這裏插入圖片描述
清空輸出再刷新一下頁面:
在這裏插入圖片描述
發現這是一個GET請求

注意事項

  • request參數是必須的
  • 返回的HttpResponse()也是必須的

如果直接返回字符串會報錯,如下圖所示:
在這裏插入圖片描述
‘str’ object has no attribute ‘get’

視圖的總結

視圖一般都寫在app的views.py中

並且視圖的第一個參數永遠都是request(一個HttpRequest)對象。這個對象存儲了請求過來的所有信息,包括攜帶的參數以及一些頭部信息等。在視圖中,一般是完成邏輯相關的操作。

比如這個請求是添加一篇博客,那麼可以通過request來接收到這些數據,然後存儲到數據庫中,最後再把執行的結果返回給瀏覽器。

視圖函數的返回結果必須是HttpResponseBase對象或者子類的對象。示例代碼如下:

news/views.py:

from django.http import HttpResponse
def news(request):
    return HttpResponse("這是新聞首頁")

urls.py:

from news import views

urlpatterns = [
    path("news",views.news)
]

URL映射

基本原理

視圖寫完後,要與URL進行映射,也即用戶在瀏覽器中輸入什麼url的時候可以請求到這個視圖函數

向瀏覽器輸入:http://127.0.0.1:8000/news/

且urls.py裏添加了映射:

urlpatterns = [
    path('admin/', admin.site.urls),
    path('news/', views.news)
]

因此才能訪問到如下界面:
在這裏插入圖片描述
換句話說,在用戶輸入了某個url,請求網站的時候,django會從項目的urls.py文件中尋找對應的視圖

另外,打開settings.py文件,找到ROOT_URLCONF:
在這裏插入圖片描述
如果把這一行註釋,那麼就無法找到對應的視圖了

問題的引入

假設我多了一個book的界面:

from django.contrib import admin
from django.urls import path
from news import views
from book import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('news/', views.news)
    path('books/', views.news)
]

這時會出現兩個views,這時因爲不知道使用哪一個views,所以會報錯

要解決這一問題,請繼續往下看

URL中添加參數

有時候,url中包含了一些參數需要動態調整。

比如我的某篇CSDN文章的url:
https://blog.csdn.net/zbp_12138/article/details/105962054

後面的105962054就是我這篇文章的id

那麼主頁的url就可以寫成:
https://blog.csdn.net/zbp_12138/article/details/

那麼如何在django中實現這種需求呢。這時候我們可以在path函數中,使用尖括號的形式來定義一個參數

指定參數

urls.py:

urlpatterns = [
    path('admin/', admin.site.urls),
    path('news/', views.news),
    # news_id 必須和視圖函數中的參數保持一致
    path('news/<news_id>', views.news_detail)
]

news_id 必須和視圖函數中的參數保持一致

在views.py裏補充對應的方法:

def news_detail(request,news_id):
    return HttpResponse("新聞詳情頁-{}".format(news_id))

效果如下:
在這裏插入圖片描述
但是這樣的寫法不夠安全,我們隨意打開百度搜索一個頁面:
在這裏插入圖片描述
可以看到後面的id都是亂碼,那麼怎麼實現這一功能呢?

查詢字符串的方式傳遞參數

下面的部分把剛剛的代碼註釋,便於大家比對

urls.py:

urlpatterns = [
    path('admin/', admin.site.urls),
    path('news/', views.news),
    # news_id 必須和視圖函數中的參數保持一致
    # path('news/<news_id>', views.news_detail)
    # 查詢字符串的方式傳遞參數
    path('news/news_detail/', views.news_detail)
]

裏面就不使用尖括號了,改動主要在views.py裏:

# def news_detail(request,news_id):
#     return HttpResponse("新聞詳情頁-{}".format(news_id))

def news_detail(request):
    news_id = request.GET.get("news_id")
    return HttpResponse("新聞詳情頁-{}".format(news_id))

這次把參數放在方法裏

來看一下效果:http://127.0.0.1:8000/news/news_detail/?news_id=2
在這裏插入圖片描述

URL模塊化

回到URL映射裏提到的那個問題,這裏我們新建一個名爲book的app:
在這裏插入圖片描述
在urls.py裏添加如下代碼:

from news import views
from book import views

直接運行會發生報錯:
在這裏插入圖片描述
AttributeError: module ‘book.views’ has no attribute ‘news’

也就是說當前有兩個views,程序不知道使用哪個views,而且現在只有兩個app,到後期app更多時,這樣的寫法肯定會有問題

因此我們可以用as語句來優化一下:

from news import views
from book import views as bviews

不過這也有個問題,雖然不報錯了,但是如果把所有的app的views中的視圖都放在urls.py中進行映射,肯定會讓代碼顯得非常亂

因此django給我們提供了一個方法,可以在app內部包含自己的url匹配規則,而在項目的urls.py中再統一包含這個app的urls。使用這個技術需要藉助include函數。下面是示例代碼

django_views/urls.py文件:
在這裏插入圖片描述

from django.contrib import admin
from django.urls import path,include

urlpatterns = [
    path("news/",include("news.urls"))
]

在news裏新建一個urls.py:

from django.urls import path
from . import views #從當前目錄導入views

urlpatterns = [
    path("",views.news),
    path('news_detail/', views.news_detail)
]

如此一來,在django_views/urls.py文件中把所有的和news這個app相關的url都移動到news/urls.py中了,django_first/urls.py中,通過include函數包含news.urls,以後在請求news相關的url的時候都需要加一個news的前綴

那麼在book這個app裏也是同樣的做法

在django_view\urls.py裏補充book的url映射:

urlpatterns = [
    path("news/",include("news.urls")),
    path("book/",include("book.urls"))
]

在book\views裏補充方法:

from django.http import HttpResponse

# Create your views here.

def book(request):
    return HttpResponse("圖書首頁")

def book_detail(request, book_id):
    return HttpResponse("圖書詳情頁-{}".format(book_id))

在book裏新建urls.py:

from django.urls import path
from . import views

urlpatterns = [
    path("",views.book),
    path("book_detail/<book_id>",views.book_detail)
]

效果是一樣的:
在這裏插入圖片描述
這裏不知道大家有沒有注意到,book_id應該爲int類型:

def book_detail(request, book_id):
    print(type(book_id))
    return HttpResponse("圖書詳情頁-{}".format(book_id))

打印一下:
在這裏插入圖片描述
很明顯,並不是int類型,傳一個字符串也是可以的:
在這裏插入圖片描述
那麼這裏就出現一個問題,我無法用這裏的id做SQL查詢,或者是在列表里根據id取值

這時試一下強制類型轉換:

def book_detail(request, book_id):
    print(type(book_id))
    new_book_id = int(book_id)
    print(type(new_book_id))
    return HttpResponse("圖書詳情頁-{}".format(new_book_id))

保存並運行一下:
在這裏插入圖片描述
直接就報錯了: invalid literal for int() with base 10: ‘zbp’

Django內置轉換器

這裏有一個優化的方法,在book\urls.py裏:

path("book_detail/<int:book_id>",views.book_detail

尖括號裏由<book_id>改成 <int: book_id>

這時再試一下輸入字符串:
在這裏插入圖片描述
報錯信息變成了:“沒有找到頁面”,這樣的效果是比直接報錯要好的

另外,輸入一個數字,輸出一下id的類型:
在這裏插入圖片描述
可以看到已經變成了int類型

除了int和str,還有其他的類型可以輸出:

from django.urls import converters

來看一下Django內置的5個類型:
在這裏插入圖片描述
UUID:https://www.cnblogs.com/franknihao/p/7307224.html

URL命名與反轉

  1. 爲什麼需要URL命名
    因爲在項目開發的過程中URL地址可能經常變動,如果寫死會經常去修改

  2. 如何給一個URL指定名稱
    path("",views.index,name=“index”)

  3. 應用命名空間
    在多個app之間可能產生同名的URL,這時候爲了避免這種情況,可以使用命名空間來加以區分。在urls.py中添加app_name即可。

下面新建兩個app,分別是前臺和後臺:
在這裏插入圖片描述
接下來是代碼補充,跟前面講到的方法是一樣的,我們再一起來過一遍,加深印象:
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
來測試一下:
在這裏插入圖片描述
在這裏插入圖片描述

重定向

接下來要實現的功能是判斷用戶是否登陸,如果沒有登陸,就跳轉到登陸界面

拿知乎的官網舉例:https://www.zhihu.com/

直接訪問時,會直接跳轉到登陸界面:https://www.zhihu.com/signin?next=%2F
在這裏插入圖片描述
下面來到front\views.py實現這一功能,我們一步步來:

def index(request):
    # url是否傳遞username
    username = request.GET.get("username")
    if username:
        return HttpResponse("前臺首頁")
    else:
        # 跳轉到登陸界面
        pass

下面我們需要redirect(),進行重定向,完整代碼如下:

from django.shortcuts import render,redirect
from django.http import HttpResponse

# Create your views here.

def index(request):
    # url是否傳遞username
    username = request.GET.get("username")
    if username:
        return HttpResponse("前臺首頁")
    else:
        # 跳轉到登陸界面
        return redirect("login/")

def login(request):
    return HttpResponse("前臺登錄界面")

下面來看一下效果: http://127.0.0.1:8000/front/
在這裏插入圖片描述
自動跳轉到了: http://127.0.0.1:8000/front/login/
在這裏插入圖片描述
但這時有個問題,這裏的映射都必須對應:
在這裏插入圖片描述
在這裏插入圖片描述
這樣一來,改起來就會很麻煩,app一多就更難改了,這時就要用到反轉

反轉

在front\urls.py裏增加一個參數name:

from django.urls import path
from . import views

urlpatterns = [
    path("",views.index),
    path("login/",views.login, name = "login")
]

這樣一來,前面的映射寫什麼地址都行,都不影響頁面的跳轉

在front\views.py裏多導入一個reverse庫:

from django.shortcuts import render,redirect,reverse
from django.http import HttpResponse

# Create your views here.

def index(request):
    # url是否傳遞username
    username = request.GET.get("username")
    if username:
        return HttpResponse("前臺首頁")
    else:
        # 跳轉到登陸界面
        return redirect(reverse("login"))

def login(request):
    return HttpResponse("前臺登錄界面")

跳轉時使用反轉,參數對應於front\urls.py裏參數name的值

而這時也有一個問題,前臺和後臺的name一樣的話:
在這裏插入圖片描述
在這裏插入圖片描述
在多個app之間可能產生同名的URL,這時候爲了避免這種情況,可以使用命名空間來加以區分

在cms\urls.py里加上app_name:

app_name = "cms"

在這裏插入圖片描述
在cms\views.py裏:
在這裏插入圖片描述
這時來看一下效果,輸入http://127.0.0.1:8000/cms/:
在這裏插入圖片描述
得到的返回:http://127.0.0.1:8000/cms/login/
在這裏插入圖片描述

應用命名空間和實例命名空間

一個app,可以創建多個實例。可以使用多個URL映射同一個App。在做反轉的時候,如果使用應用命名空間,就會發生混淆,爲了避免這個問題,可以使用實例命名空間,實例命名空間使用,namespace=‘實例命名空間’

urls.py:

from django.contrib import admin
from django.urls import path,include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('cms1/', include("cms.urls",namespace='cms1')),
    path('cms2/', include("cms.urls",namespace='cms2')),
    path('front/', include("front.urls")),
]

URL反轉傳遞參數

如果這個url中需要傳遞參數,那麼可以通過kwargs來傳遞參數

front\views.py裏的reverse加入kwargs:

from django.shortcuts import render,redirect,reverse
from django.http import HttpResponse

# Create your views here.

def index(request):
    # url是否傳遞username
    username = request.GET.get("username")
    if username:
        return HttpResponse("前臺首頁")
    else:
        # 跳轉到登陸界面
        return redirect(reverse("front:login", kwargs={"uid":1}))

def login(request, uid):
    return HttpResponse("前臺登錄界面-接收到的uid爲{}".format(uid))

front\views.py:

from django.urls import path
from . import views

app_name = "front"

urlpatterns = [
    path("",views.index),
    path("login/<uid>",views.login, name = "login")
]

下面是效果:

訪問http://127.0.0.1:8000/front/
在這裏插入圖片描述
如果要傳入字符串:

把front\urls.py裏的path改成path(“login/”,views.login, name = “login”),即去掉< uid >

然後到front\views.py,把代碼改成:

from django.shortcuts import render,redirect,reverse
from django.http import HttpResponse

# Create your views here.

def index(request):
    # url是否傳遞username
    username = request.GET.get("username")
    if username:
        return HttpResponse("前臺首頁")
    else:
        # 跳轉到登陸界面
        login_url = reverse("front:login") + "?name=zbp"
        return redirect(login_url)

def login(request):
    uid = request.GET.get("name")
    return HttpResponse("前臺登錄界面-接收到的uid爲{}".format(uid))

因爲django中的reverse反轉url的時候不區分GET請求和POST請求,因此不能在反轉的時候添加查詢字符串的參數

效果如下:
在這裏插入圖片描述

指定默認的參數

在book\views.py裏添加圖書列表以及對應方法:


book_list = ["a", "b", "c", "d"]

def book_lists(request):
    return HttpResponse(book_list[0])

在book\urls.py補充映射:

urlpatterns = [
    path("",views.book),
    path("book_detail/<int:book_id>",views.book_detail),
    path("book_lists/",views.book_lists)
]

效果如下:
在這裏插入圖片描述
圖書詳情頁的功能也可以進行完善:

book\views.py:

def book_detail(request, book_id = 1):
    print(type(book_id))
    return HttpResponse("圖書詳情頁-{}".format(book_list[book_id]))

給book_id指定一個默認參數1

book\urls.py:

urlpatterns = [
    path("",views.book),
    path("book_detail/<int:book_id>",views.book_detail),
    path("book_detail/",views.book_detail),
    path("book_lists/",views.book_lists)
]

看一下效果:
在這裏插入圖片描述
默認的參數是1,傳一個其他的參數:
在這裏插入圖片描述

re_path函數

有時候在寫url匹配的時候,想要寫使用正則表達式來實現一些複雜的需求,那麼這時候我們可以使用re_path來實現

re_path的參數和path參數一模一樣,只不過第一個參數也就是route參數可以爲一個正則表達式

在news\urls.py裏添加re_path:

urlpatterns = [
    path("",views.news),
    path('news_detail/', views.news_detail),
    # r是原生字符串
    re_path(r'news_list/(?P<year>\d{4})', views.news_list)
]

年份是4位的,問號大P用於命名< year >, 匹配4個長度

在news\viewss.py裏補充news_list方法:

def news_list(request, year):
    return HttpResponse("新聞列表頁面-{}年".format(year))

來看一下效果:
在這裏插入圖片描述
這裏需要注意的是,如果傳入5位的參數,也只會匹配前4位:
在這裏插入圖片描述

如果要匹配月份,寫法如下:

re_path(r"^article_list/(?P<mouth>\d{2})/",views.article_mouth)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章