Django--路由層、檢視層、模版層
路由層:
路由匹配
url(正則表示式,檢視函式記憶體地址)
只要正則匹配到了內容,就不再往下匹配,而是直接執行後面的檢視函式
匹配首頁)
url(r'^&', home)
匹配尾頁
url(r'', errors)
路由匹配的規律
第一次是按照使用者輸入什麼就匹配什麼
第二次是Django會讓瀏覽器自動在尾部加上斜槓再次匹配,要是還是匹配不上就不報錯
上述的規律不是瀏覽器的特點,而是Django的功能
Django settings資料夾中
APPEND_SALSH = True # 這個是自動加斜槓的
無名分組
路由匹配成功之後,如果正則表示式中有分組,那麼在呼叫檢視函式的時候會將括號內正則表示式匹配到的內容當作位置引數傳遞給後面的檢視函式
url(r'^index/(\d+)/', index)
def index(request, args):
return Httpresponse物件
有名分組
路由匹配成功之後,如果正則表示式中有有名分組並且起了別名的話,那麼在呼叫檢視函式的時候會將括號內正則表示式匹配到的內容當作關鍵字引數傳遞給後面的檢視函式
url(r'^index/(?P<year>\d+)/', index)
def index(request, index):
return Httpresponse物件
有名分組和無名分組不能混合使用,但是兩者可以單獨使用多個
反向解析
由來是很多html頁面和後端程式碼中頻繁使用了指名道姓的url(/index/)
那麼這個時候如果一旦發生了變化,前後端所有的地方都需要修改,造成了可維護性比較差
反向解析的本質就是通過某個方法獲取的一個能夠訪問對應的url的結果
沒有涉及到正則表示式符號的情況下
url(r'^index/', index, name='index')
前端:
{% url 'aaa' %}
後端:
from django.shortcuts import HttpResponse,render,redirect,reverse
url = reverse('index')
無名分組和有名分組的情況
無名:
url(r'^index/(\d+)/',index,name='bbb')
前端:
{% url 'bbb' 1 %}
後端:
url = reverse('aaa',args=(1,))
有名:
url(r'^index/(?P<edit_id>\d+)/',index,name='bbb')
前端:
{% url 'bbb' 1 %}
{% url 'bbb' year=1 %}
後端:
url = reverse('aaa',args=(1,))
url = reverse('aaa',kwargs={'year':1})
路由分發
include()
django中的每一個app都可以是獨立的urls.py, static靜態檔案, templates模版資料夾
真是基於上面的特點,Django就可以很好的支援多人分組開發,每個人只需要開發各自的app就可以了,最後只需要建立一個空的Django專案將所有人的app註冊進來,總路由中來一個路由分發就可以將多個人的功能整合到一個專案中了
當專案比較巨大的時候,如果不使用路由分發,那麼路由中的url程式碼太多了,不利於維護和更新
from app01 import urls as app01_urls
from app02 import urls as app02_urls
url(r'應用名字首/',include(app01_urls))
url(r'應用名字首/',include(app02_urls))
# 推薦使用下面的方式
url(r'應用名字首/',include('app01.urls'))
url(r'應用名字首/',include('app02.urls'))
路由分發要注意的點:
總路由中的最後千萬不要加$符號
名稱空間
你在起別名的時候,雖然能夠同一個應用下不重名,但是你沒辦法實現多個人多個app不重名的情況
一旦重名在進行反向解析的話就不能識別所在的應用了
在總路由中 給每一個應用開設一個名稱空間
url(r'應用名字首/',include('app01.urls',namespace='app01'))
url(r'應用名字首/',include('app02.urls',namespace='app02'))
子路由:
url(r'^index/',index,name='aaa')
url(r'^index/',index,name='aaa')
反向解析的時候:
reverse('app01:aaa')
reverse('app02:aaa')
{% url 'app01:aaa' %}
{% url 'app02:aaa' %}
其實你可以完全不使用名稱空間 只需要保證別名不重複就行,也就是你在起別名的時候給每個別名加上所在應用名的字首
偽靜態
就是在url後面加上一個.html
虛擬環境
不同的專案應該有屬於該專案獨有的python直譯器環境,這樣的話就可以避免資源的浪費以及版本相容的問題
注意⚠️:
虛擬環境雖然好用,但是不要無限制的進行建立 你機器上的虛擬環境可以手動進行刪除
Django版本之間的區別:
路由2.X裡面的是path第一個引數也不再支援正則表示式,而是寫什麼就匹配什麼,如果這個時候你還是想要使用正則進行匹配的話,這裡面提供了一個re_path模組就是1.X裡面的url
雖然path不再支援正則,但是他提供了五種轉化器(可以將路由匹配到的內容自動轉換成對應的資料格式然後再傳遞給後面的檢視函式)
檢視層
如果一個一個應用下面的檢視函式特別多,這時候你就可以新建一個views資料夾,然後根據不同的功能起不同的名字的py檔案存放不同的功能,實現持續的解耦合 和增加可讀性
views
user.py
account.py
order.py
...
前後端分離
JsonResponse(data,safe=False,json_dumps_params={'ensure_ascii':False})
如果在son_dumps_params裡面你傳遞的不是字典資料,那麼需要手動指定safe=False
form表單上傳檔案
需要注意的事項:
1、method必須是post
2、enctype必須要制定成formdata
3、目前需要考慮的 需要將settings配置檔案中的csrf中介軟體註釋掉
後端不再通過request.POST獲取檔案資料,而是通過request.FILES
file_obj = request.FILES.get('myfile')
file_obj.name # 檔名
with open(file_obj.name,'wb') as f:
for chunk in file_obj.chunks():
f.write(chunk)
request
request.method
request.GET
request.POST
request.FILES
request.body # 獲得是原生的二進位制的資料
request.path # 只拿url
request.get_full_path() # url+?後面的引數
render能夠返回一個html頁面,並且還能夠為該頁面傳遞資料
render的內部原理
from django.template import Template,Context
def index(request):
temp = Template('<h1>{{ user }}</h1>')
con = Context({"user":{"name":'jason',"password":'123'}})
res = temp.render(con)
print(res)
return HttpResponse(res)
FBV和CBV
檢視函式並不是指函式 同樣也可以指類
FBV:基於函式的檢視(面向函數語言程式設計)
CBV:基於類的檢視 (面向物件式程式設計)
# 問題:基於CBV的檢視函式 """get請求來就會走類裡面get方法,post請求來就會走類裡面post方法 為什麼???""" urls.py中 url(r'^login/',views.MyLogin.as_view()) # 由於函式名加括號的執行優先順序最高,所以這一句話寫完就會立刻執行as_view()方法 views.py中 from django.views import View class MyLogin(View): def get(self,request): print("from MyLogin get方法") return render(request,'login.html') def post(self,request): return HttpResponse("from MyLogin post方法")
研究方向 1.從url入手 url(r'^login/',views.MyLogin.as_view()) 由於函式名加括號執行優先順序最高,所以這一句話一寫完會立刻執行as_view()方法 @classonlymethod def as_view(cls, **initkwargs): # cls就是我們自己的寫的類 MyLogin def view(request, *args, **kwargs): self = cls(**initkwargs) # 例項化產生MyLogin的物件 self = MyLogin(**ininkwargs) if hasattr(self, 'get') and not hasattr(self, 'head'): self.head = self.get self.request = request self.args = args self.kwargs = kwargs # 上面的幾句話都僅僅是在給物件新增屬性 return self.dispatch(request, *args, **kwargs) # dispatch返回什麼 瀏覽器就會收到什麼 # 物件在查詢屬性或者方法的時候 你一定要默唸 先從物件自己這裡找 然後從產生物件的類裡面找 最後類的父類依次往後 return view 通過原始碼發現url匹配關係可以變形成 url(r'^login/',views.view) # FBV和CBV在路由匹配上是一致的 都是url後面跟函式的記憶體地址 2.當瀏覽器中輸入login 會立刻觸發view函式的執行 def dispatch(self, request, *args, **kwargs): # Try to dispatch to the right method; if a method doesn't exist, # defer to the error handler. Also defer to the error handler if the # request method isn't on the approved list. # 我們先以GET為例 if request.method.lower() in self.http_method_names: # 判斷當前請求方法是否在預設的八個方法內 # 反射獲取我們自己寫的類產生的物件的屬性或者方法 # 以GET為例 handler = getattr(self,'get','取不到報錯的資訊') # handler = get(request) handler = getattr(self, request.method.lower(), self.http_method_not_allowed) else: handler = self.http_method_not_allowed return handler(request, *args, **kwargs) # 直接呼叫我們自己的寫類裡面的get方法 # 原始碼中先通過判斷請求方式是否符合預設的八個請求方法 然後通過反射獲取到自定義類中的對應的方法執行
Django settings中的原始碼
前提:
1、Django除了暴露給使用者一個settings.py配置檔案之外 自己內部還有一個全域性的配置檔案
2、我們在使用配置檔案的時候,可以直接匯入暴露給使用者的settings.py也可以使用Django全域性的配置檔案 並且一般都是後者使用居多
from django.conf import settings
3、Django的啟動入口是manage.py
import os import sys if __name__ == "__main__": # django在啟動的時候 就會往全域性的大字典中設定一個鍵值對 值是暴露給使用者的配置檔案的路徑字串 os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day54.settings") class Settings(object): def __init__(self, settings_module): # settings_module = 'day54.settings' # update this dict from global settings (but only for ALL_CAPS settings) for setting in dir(global_settings): # django全域性配置檔案 # dir獲取django全域性配置檔案中所有的變數名 if setting.isupper(): # 判斷檔案中的變數名是否是大寫 如果是大寫才會執行/生效 setattr(self, setting, getattr(global_settings, setting)) # 給settings物件設定鍵值對 # 給settings物件設定鍵值對 settings[配置檔案中大寫的變數名] = 配置檔案中大寫的變數名所對應的值 # store the settings module in case someone later cares self.SETTINGS_MODULE = settings_module # 'day54.settings' mod = importlib.import_module(self.SETTINGS_MODULE) # mod = 模組settings(暴露給使用者的配置檔案) for setting in dir(mod): # for迴圈獲取暴露給使用者的配置檔案中所有的變數名 if setting.isupper(): # 判斷變數名是否是大寫 setting_value = getattr(mod, setting) # 獲取大寫的變數名所對應的值 setattr(self, setting, setting_value) # 給settings物件設定鍵值對 """ d = {} d['username'] = 'jason' d['username'] = 'egon' 使用者如果配置了就用使用者的 使用者如果沒有配置就用系統預設的 其實本質就是利用字典的鍵存在就是替換的原理 實現了使用者配置就用使用者的使用者沒配置就用預設的 """ class LazySettings(LazyObject): def _setup(self, name=None): # os.environ你可以把它看成是一個全域性的大字典 settings_module = os.environ.get(ENVIRONMENT_VARIABLE) # 從大字典中取值鍵為DJANGO_SETTINGS_MODULE所對應的值:day54.settings # settings_module = 'day54.settings' self._wrapped = Settings(settings_module) # Settings('day54.settings') settings = LazySettings() # 單例模式
模版層
模版語法
{{}}變數相關
{%%}邏輯相關
都是為模版來傳值的
過濾器:|
前後端取消轉義
前端:
|safe
後端:
from django.utils.safestring import mark_safe
zzz = mark_safe('<h1>阿薩德搜啊第三款垃圾袋</h1>')
|safe
|filesizeformat
|date
標籤{%%}
{#{% for foo in l %}#} {# <p>{{ forloop }}</p>#} {#{% endfor %}#} {##} {##} {#{% if '' %}#} {#<p>xxx條件為true</p>#} {# {% else %}#} {# <p>xxx條件為false</p>#} {#{% endif %}#} {##} {#{% for foo in '' %}#} {# {% if forloop.first %}#} {# <p>這是我的第一次</p>#} {# {% elif forloop.last %}#} {# <p>這是最後一次了啊</p>#} {# {% else %}#} {# <p>來啊來啊!!!</p>#} {# {% endif %}#} {# {% empty %}#} {# <p>當for迴圈的物件為空的時候 會走empty</p>#} {#{% endfor %}#} {##} {#<p>#} {# django模板語法在取值的時候 統一使用句點符(大白話就是 點號 .)#} {# {% with l.6.3.name as ttt %} 可以給一個比較複雜的取值操作取一個別名 之後在with語句中 就可以使用該別名#} {# {{ ttt }}#} {# {{ l.6.3.name }}#} {# {% endwith %}#} {#</p>#} {% for foo in d.keys %} <p>{{ foo }}</p> {% endfor %} {% for foo in d.values %} <p>{{ foo }}</p> {% endfor %} {% for foo in d.items %} <p>{{ foo }}</p>
{% endfor %}
for迴圈裡面的forloop物件
if判斷
自定義標籤、過濾器、inclusion_tag
自定義固定的三步走戰略:
1、必須在你的應用下新建一個叫templatetags資料夾
2、在該資料夾內新建一個任意名稱的py檔案
3、在該py檔案中固定先寫入下面兩句程式碼
from django import template
register = template.Library()
自定義過濾器
@register.filter(name='baby') def index(a,b): # 該過濾器只做一個加法運算 是|add建議版本 """ |length |add |default |filesizeformat |truncatewords |truncatechars |safe |slice :param a: :param b: :return: """ print('下午剛起床 一臉懵逼') return a + b
自定義標籤,支援傳多個值
@register.simple_tag(name='jason') def xxx(a,b,c,year): return '%s?%s|%s{%s'%(a,b,c,year)
自定義inclusion_tag
接受使用者傳入的引數 然後作用於一個html頁面,在該頁面上渲染資料,之後將渲染好的頁面放到使用者呼叫inclusion_tag的地方
@register.inclusion_tag('bigplus.html') def bigplus(n): l = [] for i in range(n): l.append('第%s項'%i) return {'l':l}
模版的繼承
在多個頁面整體樣式都差不多的情況下,這時候就可以設定一個模版檔案了,在該模版檔案中,使用block劃分多個區域,之後子版本在使用模版的時候 可以通過block塊的名字來選定到底需要修改哪一部分割槽域
模版一般情況下 都應該至少有三個可以被修改的地方
1、子頁面自己的css程式碼
2、子頁面自己的html程式碼
3、子頁面自己的js程式碼
一般情況下 模版上的block越多,頁面的可擴充套件性就越強
模版的匯入
{% include 'beautiful.html' %} 當你寫了一個特別好看的form表單/列表標籤等 可以將它當成一個模組 哪個地方需要 就直接匯入使用即可