Web框架之Django_03 路由層瞭解(路有層 無名分組、有名分組、反向解析、路由分發 視圖層 JsonResponse,FBV、CBV、文件上傳)

摘要:

  • 路由層

    • 無名分組

    • 有名分組

    • 反向解析

    • 路由分發

    • 名稱空間

  • 僞靜態網頁、虛擬環境

  • 視圖層

    • JsonResponse

    • FBV 與 CBV(function base views與class base views)

    • 文件上傳

1、路由層:(Django的路由系統)

  1. URL配置(Django項目urls.py路由文件):
    就像Django所支撐網站的目錄,它的本質是URL與要爲該URL調用的視圖函數之間的映射表。
    以這種方式告訴Django,對於這個URL調用這段代碼,對於那個URL調用那段代碼。
     urls.py配置基本格式:
    from django.conf.urls import url
    from django.contrib import admin
    from app01 import views
    
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^$', views.index),
        url(r'^index/', views.index, name='index'),
    ]
    
    url(正則表達式, views視圖函數,參數,別名)

    參數說明:

    • 正則表達式:一個正則表達式字符串
    • views視圖函數:一個可調用對象,一般爲一個視圖函數或一個指定視圖函數路徑的字符串
    • 參數:可選的要傳遞給視圖函數的默認參數(字典形式)
    • 別名:一個可選的name參數
  2. 正則表達式詳解:
    from django.conf.urls import url
    from . import views
    urlpatterns = [
        url(r'^articles/2003/$', views.special_case_2003),
        url(r'^articles/([0-9]{4})/$', views.year_archive),
        url(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive),
        url(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.article_detail),
    ]

    注意事項

    1. urlpatterns中的元素按照書寫順序從上往下逐一匹配正則表達式,一旦匹配成功則再也不繼續。
    2. 若要從URL中捕獲一個值,只須要在它周圍放置一對圓括號(分組匹配)。
    3. 不須要添加一個前導的反斜槓,由於每一個URL 都有。例如,應該是^articles 而不是 ^/articles。
    4. 每一個正則表達式前面的'r' 是可選的可是建議加上。

    補充說明

    # 是否開啓URL訪問地址後面不爲/跳轉至帶有/的路徑的配置項
    APPEND_SLASH=True
    
    Django settings.py配置文件中默認沒有 APPEND_SLASH 這個參數,但 Django 默認這個參數爲 APPEND_SLASH = True。 其做用就是自動在網址結尾加'/'。
    
    其效果就是:
    
    咱們定義了urls.py:
    from django.conf.urls import url
    from app01 import views
    
    urlpatterns = [
            url(r'^blog/$', views.blog),
    ]
    
    訪問 http://www.example.com/blog 時,默認將網址自動轉換爲 http://www.example.com/blog/ 。
    
    若是在settings.py中設置了 APPEND_SLASH=False,此時咱們再請求 http://www.example.com/blog 時就會提示找不到頁面。
  3. 無名分組html

    將加括號的正則表達式匹配到的內容當作位置參數自動傳遞給對應的視圖函數
    url(r'^test/(\d+)/',views.test),  # 匹配一個或多個數字
    def test(request,xxx):
        print(xxx)
        return HttpResponse('test')
    這裏的xxx就是test/路徑後面的數字(固然正則匹配到的字符串類型的數字)
  4. 有名分組
    將加括號的正則表達式匹配到的內容當作關鍵字參數自動傳遞給對應的視圖函數)
    url(r'^test/(?P<year>\d+)/',views.test),  # 匹配一個或多個數字
    def test(request,year):
        print(year)
        return HttpResponse('test')

    須要注意:無名分組和有名分組不能混着用前端

    url(r'^test/(\d+)/(?P<year>\d+)/',views.test)
    這是錯誤的

    可是支持用一類型多個形式匹配
    無名分組多個python

    url(r'^test/(\d+)/(\d+)/',views.test),

    有名分組多個git

    url(r'^test/(?P<year>\d+)/(?P<xxx>\d+)/',views.test)
  5. 反向解析(根據名字動態後去到相對路徑)
    from django.shortcuts import reverse  # 導入一個reverse模塊用於解析名字
            
    url(r'^index6668888/$',views.index,name='index')

    # 能夠給每個路由與視圖函數對應關係起一個名字
    # 這個名字可以惟一標識出對應的路徑
    # 注意這個名字不能重複是惟一的正則表達式

    使用情景:
    前端使用
        {% url 'index' %}
        {% url '你給路由與視圖函數對應關係起的別名' %}
        
    後端使用
        reverse('index')
        reverse('你給路由與視圖函數對應關係起的別名')

    無名分組反向解析:django

    url(r'^test/(\d+)/',views.test,name='list')

    使用情景:json

    後端使用
        url = reverse('name',args=(10,)) 
    # 這裏的url就是前端進入後端的url路由,可是這個路由有點特別,由於他加入了無名分組參數,name就是對應路由設置的name=‘’,二後面的args=(10,)是路由正則裏面匹配到的分組數字(該數字是前端傳過來的),這裏寫10仍是其它數字均可以,只要能讓正則匹配到。
    
    前端使用
        {% url 'name' 10 %}
    # 固定格式,路由名字,分組參數
    
    user_list = models.User.objects.all()
                  
    url(r'^edit/(\d+)/',views.edit,name='edit')
    前端模板語法
    {%for user_obj in user_list%}
        <a href='edit/{{ user_obj.pk }}/'></a>
    {% endfor %}
    能夠替換爲:
    {%for user_obj in user_list%}
        <a href='{% url 'edit' {{ user_obj.pk }} %}'></a>
    {% endfor %}

    有名分組反向解析:後端

    後端使用
        # 後端有名分組和無名分組均可以用這種形式
        print(reverse('list',args=(10,)))
        # 下面這個瞭解便可
        print(reverse('list',kwargs={'year':10}))
    
    前端使用
        # 前端有名分組和無名分組均可以用這種形式
        {% url 'list' 10 %}
        # 下面這個瞭解便可
        {% url 'list' year=10 %}

    總結:
    針對有名分組與無名分組的反向解析統一採用一種格式便可瀏覽器

    後端 reverse('list',args=(10,)) # 這裏的數字一般都是數據的主鍵值 前端 {% url 'list' 10 %}

    反向解析的實質:就是獲取到一個可以訪問名字所對應的視圖函數(用這個名字在映射路由字符串與視圖函數的關係,同時無論路由內容怎麼變,都不會改變對應的視圖函數。至關於將url配置中的一條url映射關係打上標籤,無論裏面的內容怎麼變,我值會找到路由與視圖函數之間的指向關係。app

  6. 路由分發
    須要知道:
    Django項目每個app下面均可以有本身的urls.py路由層,templates文件夾,static文件夾
    當項目過多的時候,把全部的路由所有放在項目的url.py文件是不明智的,由於太多容易出現混亂狀況
    因此能夠將:
    項目名下urls.py咱們統稱爲總路由,它再也不作路由與視圖函數的匹配關係而是作理由的分配工做
    而後分配到各個app中的url.py文件去作路由與視圖函數關係的工做。
    # 路由分發  注意路由分發總路由千萬不要$結尾
    from django.conf.urls import url, include  # 額外導入一個include模塊
    from django.contrib import admin
        url(r'^app01/',include('app01.urls')),  # 注意是字符串格式
        url(r'^app02/',include('app02.urls')),  # 注意是字符串格式
        
    # 在應用下新建urls.py文件,在該文件內寫路由與視圖函數的對應關係便可
        from django.conf.urls import url
        from app01 import views
        urlpatterns = [
            url(r'^index/',views.index)
        ]

    名稱空間:當多個app下面的urls.py中存在相同name的url的時候,若是不作任何處理,Django是不會識別到的,因此須要引用一個名稱空間的概念。
    在路由分發的時候爲每個app的路由分配一個名稱空間:

    url(r'^app01/',include(app01_urls,namespace='app01')),
    url(r'^app02/',include(app02_urls,namespace='app02'))

    各app中的路由照常添加:

    app01.urls.py
        from django.conf.urls import url
        from app01 import views
        urlpatterns = [
            url(r'^index/',views.index,name='index')
        ]
        
    app02.urls.py
        from django.conf.urls import url
        from app02 import views
        urlpatterns = [
            url(r'^index/',views.index,name='index')
        ]

    是視圖界面和前端使用的時候,就會跳出選擇項讓你選擇哪一個app中的路由對應的name(同時表現形式會改變)

    app01.views.py
        reverse('app01:index')
        
    app02.views.py
        reverse('app02:index')

    注意:名稱空間的概念只作瞭解,由於真實咱們在項目中,路由中的name=''通常都會在前綴加上app01_index,app02_index來區分,因此不會出現重複混亂狀況,這時候再用名稱空間的操做就很是的雞肋,因此只做瞭解。

 2、僞靜態網頁和虛擬環境:

  1. 僞靜態網頁:
    顧名思義,假裝成靜態網頁,其實是動態頁面,只不過末尾加上了一串.html的字符串,而不是靜態頁面文件的後綴名。
    目的:搜索優化SEO(實際沒卵用,不如RMB玩家)
    實現:
    url(r'^index.html',views.index,name='app01_index')
  2. 虛擬環境:
    爲不一樣的項目配置不一樣的版本Python解釋器以適應不一樣項目的Python解釋器版本需求以及其支持的第三方模塊,提升項目的效率,去除沒必要要的模塊致使項目的加載的速度減慢。
    方法:

    新建項目>>Virtualenv>>選擇Python版本>>
    獲得了一個不帶任何第三方包的「乾淨」的Python虛擬環境,已經安裝到系統Python環境中的全部第三方包都不會複製過來。

    當須要安裝該環境所須要的包時,在設置—項目—Project Interpreter中點右上角的加號,搜索包名稱後點擊Install Package便可。

    注意:這裏安裝的python包只在這個虛擬環境中生效,其餘的虛擬環境,該怎麼裝還怎麼裝。
    補充:用虛擬環境實現不一樣版本的Django共存,好比1.0版和2.0版,因此提一下兩個版本的區別:

    django1.0與django2.0之間的區別

    django2.0裏面的path第一個參數不支持正則,你寫什麼就匹配,100%精準匹配
    
    django2.0裏面的re_path對應着django1.0裏面的url
    
    雖然django2.0裏面的path不支持正則表達式,可是它提供五個默認的轉換器
    
    str,匹配除了路徑分隔符(/)以外的非空字符串,這是默認的形式
    int,匹配正整數,包含0。
    slug,匹配字母、數字以及橫槓、下劃線組成的字符串。
    uuid,匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00。
    path,匹配任何非空字符串,包含了路徑分隔符(/)(不能用?)
    
    自定義轉換器
    1.正則表達式
    2.類
    3.註冊
    
    # 自定義轉換器
    class FourDigitYearConverter:
      regex = '[0-9]{4}'
       def to_python(self, value):
          return int(value)
       def to_url(self, value):
          return '%04d' % value # 佔四位,不夠用0填滿,超了則就按超了的位數來!
    register_converter(FourDigitYearConverter, 'yyyy')
    PS:路由匹配到的數據默認都是字符串形式

 3、FBV與CBV、JsonResponse、文件上傳

  • FBV:基於函數的視圖,function base views
    基於函數的視圖咱們在學習Django的時候就已經在使用了:
    from django.conf.urls import url
    from django.contrib import admin
    from app01 import views
    
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^$', views.index),
        url(r'^index/', views.index, name='index'),
    
    def test(request):
        if request.method == 'POST':
            print(request.POST)
            print(request.POST.get('name'), request.POST.get('pwd'))
            return HttpResponse('nice')
        return render(request, 'test.html')
  • CBV:基於類的視圖與,class base views
    須要重點理解並掌握的是CBV的理解與使用:
    路由層urls.py
    url(r'^mycls/', views.MyCls.as_view())
    
    視圖層views.py
    class MyCls(View):
        def get(self, request):
            return render(request, 'index.html')
        def post(self, request):
            return render HttpResponse('post')

    源代碼解析:

    路由層urls.py
    url(r'^mycls/', views.MyCls.as_view())中:

    MyCls類中定義了2個方法get和post
    進入MyCls繼承的類View源碼看看:


    首先在url路由層視圖那裏:
    views.MyCls.as_view()
    咱們在源碼中看到as_view()返回的是一個view的函數名,那麼上面的能夠寫成:
    views.view
    也就是這樣:
    url(r'^mycls/',views.view)
    也就是說變成了一個視圖函數,全部接下來咱們還得看看View類中的view方法的內容
    當瀏覽器發出路由請求,匹配到前面的
    mycls就會調用後面的視圖函數view(request)

    繼續查看dispatch函數代碼:


    因此最好view(request)函數的調用最後變成了這樣:MyCls類實例化對象self.post(request),咱們再看看MyCls類的內容:

    這裏就實現了經過類視圖,建立的相對於類來經過post和get(或者其它更多請求方式)來判斷請求方式進行不一樣的操做,達到和函數視圖方式同樣的結果。
    補充一個http請求方法列表:

    http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']

    至此基於類的視圖實現過程分析完成,整個過程走下來,發現實現的目的和函數視圖一模一樣,只不過用類來實現會多走基本路。條條道路通羅馬,只有有想法,什麼方法都是方法,能達到目的就行,๑乛◡乛๑

  • JsonResponse(將相應的數據自動轉換成json格式,而後直接發送回瀏覽器)

    導入模塊
    from django.http import JsonResponse
    import json
    
    原始方法:
    def index(request):
        res = {'name':'sgt','password':18}
        return HttpResponse(json.dumps(res))
    
    JsonResponse方法:
    def index(request):
    return JsonResponse(
        {'name':'sgt','password':'1888888'},
        json_dumps_params={'ensure_ascii':False}
       )       
    
    注意:json_dumps_params={'ensure_ascii':False}這個的做用是,將Django默認轉碼功能取消,這樣就能顯示漢字了。         
  • 文件上傳
    實現過程:
    前端:
    <body>
    <h1>index</h1>
    <form action="" method="post" enctype="multipart/form-data">
        <input type="file" name="my_file">
        <input type="submit">
    </form>
    </body>

    前端須要注意的點:
    1.method須要指定成post
    2.enctype須要改成multipart/form-data格式
    後端:

    def index(request):
        if request.method == 'POST':    
            file_obj = request.FILES.get('my_file')
                print(file_obj.name)
                with open(file_obj.name,'wb') as f:
                    for line in file_obj.chunks():
                        f.write(line)
            return HttpResponse('成功')
        return render(request, 'index.html')

    後端須要注意:
    1.配置文件中註釋掉csrfmiddleware
    2.經過request.FILES獲取用戶上傳的post文件數據

    補充:request中有不少屬性,這裏說說裏面的2個屬性:path和full_path的區別:

    def login(request):
        print('path:',request.path)
        print('full_path:',request.get_full_path())
        if request.method == 'POST':
            return HttpResponse('登陸成功')
        return render(request, 'login.html')
            
        path: /login/
        full_path: /login/?name=jason   若是路由後面有拼接字符串,會顯示
相關文章
相關標籤/搜索