Django之深刻了解路由層

ORM表關係創建

換位思考,先粘在一張表上面分析, 而後再站在另外一張表上分析前端

一對一

外鍵字段建立在任意一張表均可以,簡易在查詢頻率較高的一方添加python

OneToOneField正則表達式

models.OneToOneField(to='Author_detail')  # fk + unique

一對多

外鍵字段建立在多的那一方django

ForeignKey後端

models.ForeignKey(to='Publish')  # to用來指代跟哪張表有關係 默認關聯的就是表的主鍵字段

多對多

外鍵關係須要建立第三張表來處理。瀏覽器

ManyToManyFieldapp

models.ManyToManyField(to='Author')  # django orm會自動幫你建立第三張關係表

Django 請求生命週期

url 路由層

urlpatterns = [
    url(r'^admin/', admin.site.urls),
]

url的第一個參數其實就是一個正則表達式,只要該正則表達式可以匹配到內容,就會馬上執行後面的視圖函數,再也不往下繼續匹配。函數

路由匹配

Django settings.py配置文件中默認沒有 APPEND_SLASH 這個參數,但 Django 默認這個參數爲 APPEND_SLASH = True。 其做用就是自動在網址結尾加'/'。post

咱們定義了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 時就會提示找不到頁面。

Django 路由匹配的規律:

  1. 先是不加斜槓,先匹配依次試試,
  2. 若是匹配不上,會讓瀏覽器重定向,url最後加上斜槓再次匹配一次,
  3. 若是仍是匹配不上就報錯了。
  4. 而且不會匹配GET請求?後面攜帶的參數

無名分組

將分組內正則表達式匹配到的內容當作位置參數傳遞給視圖函數

url(r'^test/([0-9]{4})/', views.test)

訪問的url:http://127.0.0.1/test/1111
        
# 當你的路由中有分組的正則表達式  那麼在匹配到內容
# 執行視圖函數的時候 會將分組內正則表達式匹配到的內容當作位置參數傳遞給視圖函數  
def test(request,xxx):
    '''
    此時url有第二個參數,會傳遞給視圖函數位置參數傳進來。
    若是不寫接收的位置參數則會報錯
    test() takes 1 positional argument but 2 were given
    '''
    print(xxx)  # 1111
    pass

有名分組

將分組內正則表達式匹配到的內容當作關鍵字參數傳遞給視圖函數

url(r'^test/(?P<year>[0-9]{4})/', views.test)

訪問的url:http://127.0.0.1/test/1111
        
# 當你的路由中有分組的正則表達式  那麼在匹配到內容
# 執行視圖函數的時候 會將分組內正則表達式匹配到的內容當作關鍵字參數傳遞給視圖函數  
def test(request,year):
    '''
    此時url有第二個參數,會傳遞給視圖函數關鍵字參數傳進來。
    若是不寫接收的關鍵字參數則會報錯
    testadd() got an unexpected keyword argument 'year'
    '''
    print(year) # 1111
    pass

利用有名個無名分組 咱們就能夠在調用視圖函數以前給函數傳遞額外的參數

注意:無名分組和有名分組不能混合使用

可是同一種分組的狀況下 可使用屢次,
無名能夠有多個

有名能夠有多個

反向解析

給路由匹配設置一個別名,

根據這個別名,動態解析出一個結果,該結果能夠直接訪問對應的url。

路由匹配條件無分組的狀況的反向解析

urls.py:
url(r'^home/',views.home,name='hm'),
url(r'^index/$',views.index),


views.py:
def home(request):
    return HttpResponse(reverse('hm'))


def index(request):
    print(reverse('hm'))    # /home/
    return render(request,'test.html')

test.html:
<body>
<div>TEST</div>
<p><a href="{% url 'hm' %}">點我點我</a></p>
</body>

當咱們在瀏覽器URL輸入http://127.0.0.1:8000/index/的時候,
python後端進行reverse反向解析成 別名爲'hm'的路徑前綴,也就是home路徑,
用test.html 頁面渲染,html也可使用反向解析

python後端使用反向解析:
    reverse('hm')
前端html使用反向解析:
        {% url 'hm' %}
        將路徑解析爲/home/。

無名分組狀況的反向解析

urls.py:
url(r'^home/(\d+)/',views.home,name='hm'),
url(r'^index/',views.index),

views.py:
def home(request,xxx):
    # 使用無名分組,視圖函數必須寫位置參數
    print(xxx)
    return HttpResponse('ok')


def index(request):
    print(reverse('hm',args=(1,)))  # 無名分組須要手動給別名傳遞一個參數才能匹配上,這個參數會傳遞到視圖函數當作位置參數。
    return render(request,'test.html')

test.html:
<body>
<div>TEST</div>
<p><a href="{% url 'hm' 1 %}">點我點我</a></p>
</body>

當一個無名分組使用別名的時候,在瀏覽器URL輸入http://127.0.0.1:8000/index/,python後端進行reverse反向解析成 別名爲'hm'的路徑前綴,也就是home路徑,須要手動給解析出來的路徑加參數,否則匹配不成功
用test.html 頁面渲染,而且html使用反向解析,也須要手動傳入參數
        
python後端使用反向解析:
    reverse('hm',args=(1,))
前端html使用反向解析:
        {% url 'hm' 1 %}
        將路徑解析爲/home/1。

有名分組的反向解析

urls.py:
url(r'^home/(?P<year>\d+)/',views.home,name='hm'),
url(r'^index/',views.index),

views.py:
def home(request,year): 
    # 使用有名分組,視圖函數必須寫關鍵字參數
    print(year)
    return HttpResponse('ok')

def index(request):
    print(reverse('hm',args=(1,)))          # 這樣也能夠,但不正規
    print(reverse('hm',kwargs={"year":1}))  # 最正規的寫法,由於有名分組傳入的是關鍵字參數,用kwargs參數來表示
    return render(request,'test.html')

test.html:
<body>
<div>TEST</div>
<p><a href="{% url 'hm' 1 %}">點我點我</a></p>      # 這樣也能夠,但不正規
<p><a href="{% url 'hm' year=1 %}">點我點我</a></p> # 最正規的寫法,由於有名分組傳入的是關鍵字參數
</body>

當一個無名分組使用別名的時候,在瀏覽器URL輸入http://127.0.0.1:8000/index/,python後端進行reverse反向解析成 別名爲'hm'的路徑前綴,也就是home路徑,須要手動給解析出來的路徑加參數,否則匹配不成功。
用test.html 頁面渲染,而且html使用反向解析,也須要手動傳入參數
        
python後端使用反向解析:
    reverse('hm',kwargs={"year":1})
前端html使用反向解析:
        {% url 'hm' year=1 %}
        將路徑解析爲/home/1。

例子

僞代碼以編輯用戶信息爲例,演示具體用法:

urls.py:

url(r'^edit_user/(\d+)',views.edit_user,name='edit'),

views.py:
def edit_user(request,edit_id):
    # 查出來全部 user_list 
    # edit_id 就是用戶想要編輯數據的主鍵值
    print(edit_id)
    return render(request,'edit_user.html',{"user_list":user_list})

edit_user.hmtl:
{% for user_obj in user_list %}
    <a href="/edit_user/{{ user_obj.id }}">編輯</a>
    <a href="{% url 'edit' user_obj.id %}">編輯</a>
{% endfor %}

路由分發

前言,在django中全部的app均可以有本身的獨立的urls.py、templates、static文件夾

那麼正是因爲這個特色,你的django項目能夠由多我的一塊兒開發。小組長最後只須要把全部的開發的app整合到一個空的django項目裏面,在settings配置文件裏註冊就能夠了。

路由分發解決的就是項目的總路由匹配關係過多的狀況,使用路由分發 會將總路由再也不作匹配的活 而僅僅是作任務分發

咱們新建一個app02,如今個人目錄結構是:

python3 manage.py startapp app02
  • app01
  • app02
  • mysite
  • static
  • templates
  • manage.py

好了以後

咱們在app01目錄中建立urs.py

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

app01/views.py:
def reg(request):
    return HttpResponse("app01 reg")


-----------------------------------
app02/urls.py:
from app02 import views
from django.conf.urls import url
urlpatterns = [
    url(r'^reg/',views.reg),
]

app02/views.py:
def reg(request):
    return HttpResponse("app02 reg")


總urls:mysite/urls.py:
from app01 import urls as app01_urls
from app02 import urls as app02_urls
urlpatterns = [
    url(r'^app01/',include(app01_urls)),
    url(r'^app02/',include(app02_urls)),
]

或者更省事的寫法,不用把兩個app的urls.py導過來:
urlpatterns = [
    url(r'^app01/',include('app01.urls')),
    url(r'^app02/',include('app02.urls')),
]

最終效果,在瀏覽器url中輸入
http://127.0.0.1:8000/app01/reg     # app01 reg
http://127.0.0.1:8000/app02/reg     # app02 reg

名稱空間

當多個app中出現了起別名衝突的狀況 你在作路由分發的時候 能夠給每個app建立一個名稱空間,而後在反向解析的時候 能夠選擇到底去哪一個名稱空間中查找別名

url(r'^app01/',include('app01.urls',namespace='app01')),
url(r'^app02/',include('app02.urls',namespace='app02'))
        
# 後端
print(reverse('app01:reg'))
print(reverse('app02:reg'))
# 前端
<a href="{% url 'app01:reg' %}"></a>
<a href="{% url 'app02:reg' %}"></a>

其實不用這麼麻煩,
參考建議
起別名的時候統一加上應用名前綴,這樣你的別名就不會重複了。
urlpatterns = [url(r'^reg/',views.reg,name='app01_reg')]
urlpatterns = [url(r'^reg/',views.reg,name='app02_reg')]

僞靜態

將一個動態網頁假裝成一個靜態的網頁,來提升搜索引擎SEO的查詢頻率,提升網站的曝光度!

怎麼作呢,好比博客園是一個動態網站,可是看起來像一個靜態網站,是由於每篇文章都有後綴名.html

那麼咱們也能夠寫,在路由匹配規則中寫上

# https://www.cnblogs.com/qinyujie/p/11394671.html

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

虛擬環境

虛擬環境就至關於從新下載了一個純淨的python解釋器,以後項目用這個虛擬環境,你須要什麼就安裝什麼,與系統環境上存在的軟件不衝突。

Django版本區別

urls.py路由匹配的方法有區別。

django 1.X

導入的模塊是

from django.conf.urls import url

urlpatterns 中 url 對應的是正則表達式,

以下:

from django.conf.urls import url
urlpatterns = [
    url = ('test',view.test)
]

django 2.X

導入的模塊是from django.urls import re_path,path

path中第一個參數不支持正則表達式,寫了什麼就只能匹配什麼,匹配不到就報錯。
django 2.X 以爲你習慣了以前的正則表達式來匹配,特別人性化 ,

提供一個re_path方法,這個方法就是1.X版本中的url()

以下:

from django.urls import path,re_path
urlpatterns = [
    path = ('test',view.test),
    re_path = (r'^test/(\d+)',view.test)
]

django 2.X的版本中還提供了五種轉換器,

  • str:匹配除路徑分隔符/外的字符串
  • int:匹配天然數
  • slug:匹配字母,數字,橫槓及下劃線組成的字符串
  • uuid:匹配uuid形式的數據
  • path:匹配任何字符串,包括路徑分隔符/
urlpatterns = [
    path = ('test/<str:\d+>',view.test)
    path = ('test/<int:\d+>',view.test)
    path = ('test/<slug:\d+>',view.test)
    path = ('test/<uuid:\d+>',view.test)
    path = ('test/<path:\d+>',view.test)
]

除了內置的五種轉換器外,還能夠自定義本身的轉換器。

  • 在應用文件夾下建立converter.py文件
  • 在converter.py文件中建立自定義類
  • 在類中定義regex 正則,to_python方法,to_url方法
class CVT185Phone:
    regex = '185\d{8}'
    def to_python(self, value):
        return int(value)
    def to_url(self, value):
        return '%11d' % value
    

from django.urls import register_converter
from app.converter import CVT185phone
register_converter(CVT185phone,'phone185')

path('page/<phone185:msg>/',views.page,name="pages")

小例子

經過路由分發,使用app01上傳文件的功能上傳一個文件;

mysite/urls.py
urlpatterns = [
    url(r'^app01/',include('app01.urls')),
    url(r'^app02/',include('app02.urls')),
]

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

app01/views.py
from django.shortcuts import reverse,render,HttpResponse,redirect
def upload(request):
    if request.method == "POST":
        print(request.FILES)
        file_obj = request.FILES.get("myfile")
        with open(file_obj.name,"wb") as f:
            for i in file_obj:
                f.write(i)
    return render(request,'upload.html')


templates/upload.html
<body>
<form action="" method="post" enctype="multipart/form-data">
    請選擇文件
    <input type="file" name="myfile">
    <input type="submit" class="btn btn-primary">提交
</form>
相關文章
相關標籤/搜索