科技改變生活 · 科技引領未來
1.Django請求和返回周期Django默認使用wsgiref模塊但是該模塊并發量特別小(大約1000),不適用于線上環境,所以在Django項目上線之后會使用uwsgi。1.1路由層之路由匹配主要是在ursl.py文件里書寫。1.11版
1. Django 請求和返回周期
Django默認使用wsgiref模塊但是該模塊并發量特別小(大約1000),不適用于線上環境,所以在Django項目上線之后會使用uwsgi。
1.1 路由層之路由匹配
主要是在ursl.py文件里書寫。
1.11版本: urlpatterns = [ url('^admin/', admin.site.urls), ] 3.2版本: urlpatterns = [ path('admin/', admin.site.urls), path('test/', views.test), path('testadd/', views.testadd), ] 1版本中使用url方法: url()方法: 1,第一個參數為一個正則 2,只要能匹配上就會執行后面的視圖函數 3版本中使用path path()方法 第一個參數是一個字符串 如果使用正則,則要使用 re_path() 而不是 path() 。 urlpatterns = [ re_path(r'^admin/', admin.site.urls), ] test/和testadd/ 在匹配的時候如果不寫后面的斜杠(/),發現也能匹配上,是因為Django在做的時候如果test匹配不上,它會讓瀏覽器后面自動加上斜杠(/)再試一次。 這個是用settings里面的APPEND_SLASH參數控制,默認為True,如果只想匹配一次則設置為False. APPEND_SLASH=False
在Django3.x在匹配時有了路徑轉換器:
str - 匹配除了 '/' 之外的非空字符串。如果表達式內不包含轉換器,則會默認匹配字符串。
int - 匹配 0 或任何正整數。返回一個 int 。 path('articles/
slug - 匹配任意由 ASCII 字母或數字以及連字符和下劃線組成的短標簽。比如,building-your-1st-django-site 。
uuid - 匹配一個格式化的 UUID 。為了防止多個 URL 映射到同一個頁面,必須包含破折號并且字符都為小寫。比如,075194d3-6885-417e-a8a8-6c931e272f00。返回一個 UUID 實例。
path - 匹配非空字段,包括路徑分隔符 '/' 。它允許你匹配完整的 URL 路徑而不是像 str 那樣匹配 URL 的一部分。
1.2 有名分組
命名正則表達式組的語法是 (?P
在Django3中路由匹配使用正則: ursl.py文件: from django.contrib import admin from django.urls import path,re_path #要手動導入re_path from orm import views urlpatterns = [ path('admin/', admin.site.urls), path('test/', views.test), path('testadd/', views.testadd), re_path(r'test/(?P[0-9]{4})/', views.testadd), ] 在views.py: def testadd(request,year): print(year) return HttpResponse("from test") // 分組名必須要傳給后面的視圖函數,否則會報錯。 如上面的例子,分組名為year,如果不傳給后端的views.testadd函數,報錯信息: testadd() got an unexpected keyword argument 'year' 有名分組 將括號內正則表達式匹配到的內容當做關鍵字參數傳遞給后面的視圖函數
1.3 無名分組
有命名組語法,例如 (?P
在Django3中路由匹配使用正則: ursl.py文件: from django.contrib import admin from django.urls import path,re_path #要手動導入re_path from orm import views urlpatterns = [ path('admin/', admin.site.urls), path('test/', views.test), path('testadd/', views.testadd), re_path(r'test/([0-9]{4})/#39;, views.test), ] 啟動訪問: http://127.0.0.1:8000/test/1234/ 報錯: test() takes 1 positional argument but 2 were given 解決方法: 在views.py: def test(request,what): print(what) return HttpResponse("from test") 再執行訪問成功。 控制臺打印的結果: 1234 無名分組: 將括號內正則表達式匹配到的內容當做位置參數傳遞給后面的視圖函數。
總結:
2. 反射解析
當路由頻繁變化的時候,HTML界面上的連接地址如何做到動態解析。
""" 1. 給路由與視圖函數對應關系添加一個別名(名字自己定義,名字之間不要沖突) path('show/', views.show, name='showtime'), 2. 根據這個別名動態解析出一個結果,該結果可以直接訪問到對應的路由 前端使用別名: "{% url 'showtime' %}">Hello Django
這樣不管path里面的show怎么變,只要name='showtime'不變,那么訪問就沒問題 后端使用別名: ursl.py: urlpatterns = [ path('show/', views.show, name='showtime'), ] views.py from django.shortcuts import render, HttpResponse,redirect,reverse def delete(request): ...... print(reverse('showtime')) # 打印url return redirect('showtime') # 也可以直接在重定向里寫別名 """
無名和有名分組指向解析
ursl.py """ from django.urls import path,re_path urlpatterns = [ re_path(r'test/([0-9]{4})/#39;, views.test, name='index_test'), ] """ views.py """ def delete(request): ...... print(reverse('index_test',args=(1,))) # 打印url args=(1,) args后面跟一個元組,里面這寫的是1,推薦寫主鍵的值 r'test/([0-9]{4})/([0-9]{4})/$ 如果有兩個分組,則args后面必須寫兩個值,(1,2)第二個值可以隨便寫 """ 前端也一樣: "{% url 'index_test' 123 %}">Hello Django
這里123也是隨便寫的,只要寫個數字就行 有名: 后端 reverse('index_test',kwargs={'id':123}) 前端 "{% url 'index_test' id=123 %}">Hello Django
總結
無名和有名都可以使用一種(無名)反向解析的形式
3. 路由分發
其實Django中的每一個應用都可以有自己的urls.py、static文件夾、templates文件夾,這樣使用Django做分組開發非常的簡便。每個人只需要寫息的應用即可,最后匯總到一個空的Django項目中然后使用路由分發將多個應用關聯。
示例:
創建一個項目,并創建兩個應用(app01,app02). 在每個應用里面都創建一個urls.py文件。 app01 urls.py: """ from django.urls import path from app01 import views urlpatterns = [ path('index', views.index), ] """ app01 views.py: """ from django.shortcuts import render,HttpResponse # Create your views here. def index(request): return HttpResponse("from app01 index") """ app02 urls.py: """ from django.urls import path from app02 import views urlpatterns = [ path('index', views.index), ] """ app02 views.py: """ from django.shortcuts import render,HttpResponse # Create your views here. def index(request): return HttpResponse("from app02 index") """ 項目中總的urls.py: """ from django.contrib import admin from django.urls import path,include # 導入應用的urls from app01 import urls as app01_urls from app02 import urls as app02_urls urlpatterns = [ path('admin/', admin.site.urls), path('app01/', include(app01_urls)), path('app02/', include(app02_urls)), ] """ 注意: 需要在總的urls.py里導入include from django.urls import path,include 在總的路由里面不能加$符號,否則沒辦法分發 還有一種在總的urls.py里不需要導入應用urlsr 的方法: 項目中總的urls.py: """ from django.contrib import admin from django.urls import path,include urlpatterns = [ path('admin/', admin.site.urls), path('app01/', include('app01.urls')), path('app02/', include('app02.urls')), ] """
4 名稱空間
當多個應用在反射解析的時候如果出現了別名沖突的情況,那么將會無法自動識別
示例:
app01 urls.py: """ from django.urls import path from app01 import views urlpatterns = [ path('index', views.index,name='index_name'), path('login', views.login) ] """ app01 views.py: """ from django.shortcuts import render,HttpResponse,reverse # Create your views here. def index(request): return HttpResponse("from app01 index") def login(request): print(reverse('index_name')) return HttpResponse("from app01 login") """ app02 urls.py: """ from django.urls import path from app02 import views urlpatterns = [ path('index', views.index,name='index_name'), path('login', views.login), ] """ app02 views.py: """ from django.shortcuts import render,HttpResponse,reverse # Create your views here. def index(request): return HttpResponse("from app02 index") def login(request): print(reverse('index_name')) return HttpResponse("from app02 login") """ 項目中總的urls.py: """ from django.contrib import admin from django.urls import path,include urlpatterns = [ path('admin/', admin.site.urls), path('app01/', include('app01.urls')), path('app02/', include('app02.urls')), ] """ 雖然訪問頁面: http://127.0.0.1:8000/app01/login http://127.0.0.1:8000/app02/login 的時候能正常拿到對應的頁面,但是在后端發現拿到的是同一個: /app02/index /app02/index
要解決這個問題就用到了名稱空間
解決方法一:使用名稱空間
在總路上加上namespace這個參數: 項目中總的urls.py: """ from django.contrib import admin from django.urls import path,include urlpatterns = [ path('admin/', admin.site.urls), path('app01/', include('app01.urls',namespace='app01')), path('app02/', include('app02.urls',namespace='app02')), ] """ app01 urls.py: """ from django.urls import path from app01 import views app_name='app01' urlpatterns = [ path('index', views.index,name='index_name'), path('login', views.login) ] """ app01 views.py: """ from django.shortcuts import render,HttpResponse,reverse # Create your views here. def index(request): return HttpResponse("from app01 index") def login(request): print(reverse('app01:index_name')) return HttpResponse("from app01 login") """ app02 urls.py: """ from django.urls import path from app02 import views app_name='app02' urlpatterns = [ path('index', views.index,name='index_name'), path('login', views.login), ] """ app02 views.py: """ from django.shortcuts import render,HttpResponse,reverse # Create your views here. def index(request): return HttpResponse("from app02 index") def login(request): print(reverse('app02:index_name')) return HttpResponse("from app02 login") """ 訪問頁面: http://127.0.0.1:8000/app01/login http://127.0.0.1:8000/app02/login 拿到的就是 /app01/index /app02/index 注意在Django3.2版本中使用名稱空間的時候,一定要給每個應用設置應用名,否則會報錯: '''pecifying a namespace in include() without providing an app_name is not supported. Set the app_name attribute in the included module, or pass a 2-tuple containing the list of patterns and app_name instead.''' 解決方法: app01 urls.py: ''' app_name='app01' ''' app02 urls.py: '''app_name='app02'''' 這兩個必須要設置。
前端使用名稱空間:
"{% url 'app01:index_name' %}">app01_index "{% url 'app02:index_name' %}">app02_index
注意:
雖然我們現在可以將模板文件直接放在 app01/templates 文件夾中(而不是再建立一個 app01 子文件夾),但是這樣做不太好。Django 將會選擇第一個匹配的模板文件,如果你有一個模板文件正好和另一個應用中的某個模板文件重名,Django 沒有辦法 區分 它們。我們需要幫助 Django 選擇正確的模板,最好的方法就是把他們放入各自的 命名空間 中,也就是把這些模板放入一個和 自身 應用重名的子文件夾里。(app01/templates/app01/login.html)
同理:多個應用下的靜態文件也是這樣。
所以在前端使用名稱空間的時候,HTML文件的路徑為:
app01/templates/app01/login.html app02/templates/app02/login.html 后端app01 views.py: from django.shortcuts import render,HttpResponse,reverse def login(request): print(reverse('app01:index_name')) return render(request, "app01/login.html") 后端app02 views.py: from django.shortcuts import render,HttpResponse,reverse def login(request): print(reverse('app02:index_name')) return render(request, "app02/login.html")
解決方法二:別名別沖突
寫別名的時候要加上自己應用名做前綴。
5. JsonResponse
給前端返回一個json格式的數據
方法一:自己序列化
views.py: from django.shortcuts import render,HttpResponse,reverse import json def index(request): d = {'user':'Hans', 'password':123} d_json = json.dumps(d) return HttpResponse(d_json) # json默認不能直接識別的字符直接返回對應的unicode編碼,如上面的漢字要正確返回則需要設置ensure_ascii=False d = {'user':'Hans你好', 'password':123} d_json = json.dumps(d,ensure_ascii=False)
方法二: 使用JsonResponse
views.py: from django.shortcuts import render,HttpResponse,reverse from django.http import JsonResponse def index(request): d = {'user':'Hans', 'password':123} return JsonResponse(d) # JsonResponse 對不能識別的字符也是直接返回unicode編碼,如果對漢字也能正常展示,加上json_dumps_params={'ensure_ascii':False}參數: d = {'user':'Hans你好', 'password':123} return JsonResponse(d,json_dumps_params={'ensure_ascii':False}) # 如果序列化一個非字典類型的,則需要讓safe=False 如: li = ['A','B','C'] return JsonResponse(d, safe=Fasle)
6. 上傳文件
前端頁面: # 路由層: path('upfile', views.upfile), # 視圖層 views.py: from django.shortcuts import render,HttpResponse,reverse def upfile(request): if request.method == 'POST': file_obj = request.FILES.get('files') print(file_obj.name) with open(r'./app01/templates/%s' % file_obj.name, 'wb') as f: for chunk in file_obj.chunks(): f.write(chunk) return render(request,"app01/upfile.html")
7. FBV和CBV
FBV:基于函數的視圖
CBV:基于類的視圖
上面寫的都為FBV,基于函數的視圖,現在寫一個基于類的視圖。
# views.py from django.shortcuts import render,HttpResponse,reverse from django.views import View class MyView(View): def get(self,request): return HttpResponse("GET方法") def pos(self,request): return HttpResponse("POST方法") # urls.py from django.urls import path from . import views urlpatterns = [ path('myview', views.MyView.as_view()), ] #CBV和FBV路由匹配其實是一樣的。
8. 模板語法傳值
8.1 傳基本數據類型
# 方法一:精確傳值 # urls.py """ from django.contrib import admin from django.urls import path from templateByValue import views urlpatterns = [ path('admin/', admin.site.urls), path('index/', views.index), ] """ # 前端HTML: """ {{ i }}
{{ f }}
{{ str }}
{{ Li }}
{{ set01 }}
{{ t }}
{{ bool_value }}
{{ d }}
""" views.py: """ from django.shortcuts import render # Create your views here. def index(request): i = 123 f = 12.3 str = "Hello Django" Li = [1, 2, 3] d = {'username':"Hans", "age":19} t = (1, 2, 3, 4) set01 = {1, 2, 3, 4} bool_value = True return render(request,'index.html',{"i":i,"f":f,"str":str,"Li":Li,"d":d,'t':t, "set01":set01,'bool_value':bool_value}) """ # 方法二:使用locals函數 # 在views.py 中給前端頁面傳值每個都要寫,在值特別多的時候不方便,可以使用locals函數 """ return render(request,"index.html",locals()) """ locals() 獲取全部局部變量: {'request': '/index/'>, 'i': 123, 'f': 12.3, 'str': 'Hello Django', 'Li': [1, 2, 3], 'd': {'username': 'Hans', 'age': 19}, 't': (1, 2, 3, 4), 'set01': {1, 2, 3, 4}, 'bool_value': True},然后全部傳給前端頁面。 兩者的優缺點: 方法一,可以精確傳值,不會造成資源浪費,但傳的值多的時候書寫不方便 方法二, 書寫方便,但是會造成資源浪費。
8.2 傳函數名
# 前端: """ {{ foo }}
""" # views.py: """ from django.shortcuts import render # Create your views here. # 定義函數 def index(request): def foo(): print("hello") return "Hello Django" return render(request,"index.html",{"foo":foo}) # 給前端傳遞,前面拿到的是函數的返回值。 """ 使用模板語法傳函數的時候,不支持帶參數
8.3 傳類名
# 前端: """ {{ MyClass }}
{{ obj }}
{{ obj.get_self }}
{{ obj.get_cls }}
{{ obj.get_static }}
""" #views.py """ from django.shortcuts import render # Create your views here. def index(request): class MyClass(object): def get_self(self): return "綁定給對象的方法" @classmethod def get_cls(cls): return "綁定給類的方法" @staticmethod def get_static(): return "普通的函數" obj = MyClass() print(locals()) return render(request,"index.html",{"MyClass":MyClass,"obj":obj}) """ 或直接寫: return render(request,"index.html",locals())
總結
傳遞函數名和類名都會自動加括號調用(模板語法不支持額外的傳參數)
9. 模板語法獲取值
Django中模板語法取值只用.
# views.py """ from django.shortcuts import render # Create your views here. def index(request): Li = [1, 2, 3] d = {'username':"Hans", "age":19} return render(request,'index.html',locals()) """ # 前端: """ {{ Li.1}
拿列表第二個值 {{ set01.age}}
拿年齡 """
10. 模板語法過濾器
過濾器的符號是管道符:|,將管道符左側的數據當做第一個參數。
# views.py: """ from django.shortcuts import render # Create your views here. def index(request): i = 123 str = "Hello Django" Li = [1, 2, 3] d = {'username':"Hans", "age":19} bool_value = True bool_var = False import datetime ctime = datetime.datetime.now() file_size = 409600 h = "Hello
" from django.utils.safestring import mark_safe h1 =mark_safe("Django
") #后端也可以直接寫HTML語法返回給前端了 return render(request,"index.html",locals()) """ # 前端: """ 過濾器:將管道符左側的數據當做第一個參數
統計長度:{{ str|length }}
加法:{{ i|add:10000 }}
字符串拼接:{{ str|add:"HAHA" }}
日期格式:{{ ctime|date:"Y-m-d" }}
默認值:{{ bool_value|default:"哈哈" }}
默認值:{{ bool_var|default:"哈哈" }}
文件大小:{{ file_size|filesizeformat }}
截取文本(截6個字符,包括3個點):{{ str|truncatechars:6 }}
截取文本(截1個單詞,不包括3個點):{{ str|truncatewords:1 }}
h源信息:{{ h }}
前端把后端傳過來的數據(h),格式成HTML格式: {{ h|safe }}
后端傳過來的數據(h1)直接為HTML格式顯示: {{ h1 }}
"""
11. 模板語法標簽(流程控制)
# if {% if var %} good
{% endif %} # if else {% if bool_var %} var
{% else %} valu
{% endif %} # if ... elif ... else {% if bool_var %} var
{% elif bool_value %} value
{% else %} 都沒有
{% endif %} # for {% for foo in Li %} foo
{% endfor %} # for內可以嵌套if {% for foo in Li %} foo
{% empty %} # 如果是空的時候,打印empty里的 空值
{% endfor %} # forloop 打印循環次數 {% for foo in Li %} foo
{{forloop.counter}} {% endfor %} {{}} 變量相關的用 {%%} 邏輯相關的用 # with {% with d.3.username as name %} # 給d.3.username起別名 {{ name }} {% endwith %} 這個別名只能在with里面用。
12. 自定義過濾器、標簽、inclusion_tag
類似于python里面的自定義函數
1. 在應用下創建一個名字必須叫"templatetags"文件夾 2, 在上述文件夾內創建一個任意名稱的py文件 3, 在該py文件內固定寫入: from django import template register = template.Library()
12.1 自定義過濾器:
示例:
1,在應用下創建templatetags文件夾 2,在templatetags夾里創建myFilter.py 文件內容: """ from django import template register = template.Library() @register.filter(name="My") # 過濾器名 def index(a,b): return a+b """ 3, views.py from django.shortcuts import render def index(request): i = 123 return render(request,"index.html",locals()) 4,前端頁面: {% load myFilter %} {{ i |My:100}}
5. 瀏覽器顯示結果: 223
過濾器只能接受兩個參數。
12.2 自定義標簽
1, 依然是在myFilter.py文件里: """ from django import template register = template.Library() @register.simple_tag(name='myTag') # 標簽名 def foo(a,b,c,d): return "{%s:%s %s:%s}" % (a,b,c,d) """ 2, 前端頁面: """ {% load myFilter %} {% myTag 1 "hans" 2 "Hello" %} # 標簽傳值使用空格分隔 """
標簽可以接受多個參數
12.3 自定義inclusion_tag
前面自定義的過濾器和標簽,都是自定義的過濾器函數和標簽函數直接返回給前端,自定義inclusion_tag有些不同。
在myFilter.py文件里: from django import template register = template.Library() @register.inclusion_tag('login.html', name="myInclusion") # inclusion_tag名字 def foo2(n): l =[] for i in range(1, n+1): l.append("第%s頁" % i) return locals() # login.html {% for foo in l %} - {{ foo }}
{% endfor %}
# 前端頁面: {% load myFilter %} {% myInclusion 4 %} 結果: 第1頁 第2頁 第3頁 第4頁
總結:
前端要使用自定義過濾器,標簽和inclusion_tag都先要load.
在某個區域需要反復使用并且數據不固定,適合使用inclusion_tag.
13. 模板的導入
類似于python導模塊
例如有一個頁面會經常用到,不可能每次用到就寫一份,可以使用模板導入的方法。
頁面導入模板關鍵字:{%include%}
經常用到的頁面form.html
"en"> ta charset="UTF-8"> Title cript src="https://cdn.bootcdn.net/ajax/libs/jquery/3.4.1/jquery.min.js">cript> ink href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet"> cript src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js">cript> "container"> "row"> "col-md-8 col-md-offset-2">
模板導入:
需要用到模板的頁面: index.html {% include 'form.html' %}
14. 模板的繼承
示例:
主頁home.html(模板)
"en"> ta charset="UTF-8"> Title cript src="https://cdn.bootcdn.net/ajax/libs/jquery/3.4.1/jquery.min.js">cript> ink href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet"> cript src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js">cript> "container"> "row"> {% block content %} {% endblock %}
電腦(compute.html)頁面繼承home.html
{% extends 'home.html' %} {% block content %} {% endblock %}
手機(phone.html)頁面繼承home.html
{% extends 'home.html' %} {% block content %} {% endblock %}
子模板不但能修改被標記的位置,還可以使用模板內容:
{% extends 'home.html'%} {% block content %} {{ block.super }} {% endblock %}
模板在標記區域的時候一般有三個區域
目的是為了讓繼承的子模板具有獨立的CSS和JS,增加擴展性
{% balock css %} css 樣式 {% endblock %} {% balock html %} html內容 {% endblock %} {% balock js %} js 內容 {% endblock %} 子板也可以使用模板標記的區域的內容: {{ block.super }}
王同