Django建立表關係,Django生命週期圖之路由層和視圖層簡介

一.Django框架中rom的表關係建立

1).表與表的關係

  • 一對一html

  • 一對多前端

  • 多對多python

    具體sql語句及關係請查看數據庫基礎連接git

2)以圖書管理系統爲例,建立幾張關係表

注意:**在models.py中書寫表關係的時候,要先寫好基表,再寫外鍵關係正則表達式

 1 # 先創建好三張基表
 2 from django.db import models  3  4 # Create your models here.
 5 #書表
 6 class Book(models.Model):  7     # id 是自動建立的,除非id名自定義
 8     title = models.CharField(max_length=255)  9     # price爲小數字段,表示總共8位數,其中小數佔2位
10     price = models.DecimalField(max_digits=8, decimal_places=2) 11 12 13 #出版社表
14 class Publsh(models.Model): 15     name = models.CharField(max_length=64) 16     addr = models.CharField(max_length=64) 17 18 19 # 做者表
20 class Author(models.Model): 21     name = models.CharField(max_length=64) 22     # 比Int類型長一些
23     phone = models.BigIntegerField()
  • 圖書表sql

    圖書和出版社是一對多的關係數據庫

    外鍵字段應該創建在多的一方django

 1 #書表
 2 class Book(models.Model):  3     # id 是自動建立的,除非id名自定義
 4     title = models.CharField(max_length=255)  5     # price爲小數字段,表示總共8位數,其中小數佔2位
 6     price = models.DecimalField(max_digits=8, decimal_places=2)  7     # 書籍表和出版社表是一對多關係,外鍵字段要創建在書籍表中
 8     Publish = models.ForeignKey(to='Publish') # 而且默認關聯字段就是出版社表的主鍵字段,裏面有個to_field參數可以修改關聯的字段
 9     # 補充:to後面須要加引號,若Publish表在Book表上方,則不須要加引號
10     # Publish = models.ForeignKey(to=Publish)
11 12     # 書籍與做者是多對多關係,外鍵字段創建在較爲頻繁的書籍表中,經過ManytoManyField方法就能創建第三張表
13     Authors = models.ManyToManyField(to='Author') 14     # 補充注意:Authors字段只是一個虛擬字段,並不會在Book表中建立出來,只是告訴Django ORM爲多對多關係建立一個第三方表
15
  • 出版社表json

1 #出版社表
2 class Publish(models.Model): 3     name = models.CharField(max_length=64) 4     addr = models.CharField(max_length=64) 5
  • 做者表後端

    圖書表與做者表是多對多的關係

    理論上外鍵字段在哪張表都同樣,可是推薦外鍵字段創建在查詢較爲頻繁的表中

做者表與做者詳情表是一對一關係

一樣,仍是推薦將外鍵字段創建在使用頻率較高的表中

緣由:就是爲了方便後面咱們基於ORM查詢

# 做者表
class Author(models.Model): name = models.CharField(max_length=64) # 比Int類型長一些
    phone = models.BigIntegerField() ​ # 做者表與做者詳情表一對一關係
    Author_detail = models.OneToOneField(to='AuthorDetail') 
  • 做者詳情表
1 # 做者詳情表
2 class AuthorDetail(models.Model): 3     age = models.IntegerField() 4     addr = models.CharField(max_length=64)
 

3)開始建立

4)查看

 

下載以後點一下apply,將新出現的數據庫連接刪除,從新點一下db.sqlite3,就會出現數據庫了

 

二.Django請求生命週期流程圖

 

 

三.路由層

通常咱們剛建立好的Django框架中的urls.py裏面存放的是總路由,即總的路由和視圖函數或app的對應關係

由上面咱們能夠獲得:

url方法第一個參數是一個正則表達式,路由匹配按照正則匹配, 一旦正則可以匹配到內容 會馬上執行對應的視圖函數,不會再繼續匹配了

注意:用戶輸入url尾綴路由若沒有加最後的斜槓,django會默認將斜槓加上,看下圖(將urls中的路由加上/)

補充:你能夠在配置文件settings中指定是否開啓該功能APPEND_SLASH = True/False(默認是True處於打開狀態,改成False則失效)

 

 

 

補充:網站首頁一打開是很是不友好的,能夠經過正則(^$)設置一下

1.無名分組和有名分組

基於路由層的路由是由正則表達式寫的,咱們能夠在表達式後面來一個數字,以下,表示test後面必須加上四個一到九的數字才能訪問

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

 

可是,若是給正則表達式加一個括號,就表示分組的意思

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

會產生下面的報錯信息

解決方法:

在視圖函數中給一個形參接收這個數字就能夠了

def testadd(request, xxx): return HttpResponse('testadd')

什麼叫無名分組?

在路由匹配的時候給某段正則表達式加了括號,匹配的時候會將括號內的正則表達式匹配到的內容看成位置參數傳遞給對應的視圖函數,所以視圖函數就要多準備一個形參去接收這個位置參數

什麼叫有名分組?

給一段正則表達式起一個別名 匹配的時候會將括號內正則表達式匹配到的內容當作關鍵字參數傳遞給對應的視圖函數

#有名分組 # \d+表示一個或多個數字,?P<ttt>固定寫法,表示給正則表達式命名爲ttt
    url(r'^testadd/(?P<ttt>\d+)', views.testadd)

 

解決方法:在視圖函數中添加關鍵字形參去接收關鍵字實參

# ttt就是接收關鍵字參數的
def testadd(request, ttt): return HttpResponse('testadd')

注意:

  • 無名有名不能混合使用

url(r'^test/(\d+)/(?P<year>\d+)/', views.test) # 會報找不到位置參數的錯誤
  • 無名有名雖然不能混合使用,可是同一種命名方式 可使用多個

1 #無名疊加
2 url(r'^test/(\d+)/(\d+)/',views.test) 3 4 #有名疊加
5 url(r'^test/(?P<xxx>\d+)/(?P<year>\d+)/',views.test),

2.反向解析

經過一些方法 可以獲得一個結果 該結果能夠訪問到對應的url

例如:能夠經過html頁面訪問另外一個url頁面

可是,若是將urls中的test名修改了,後面的html全部關於這個的url連接都將失效

如何解決此問題?

  • 第一步:

    先給路由與視圖函數對應關係 起一個名字(至關於賦予一個令牌)

url(r'^test/',views.testadd,name='add')

  • 第二步:如果前端解析,將{% url ‘add’ %}添加html中

<a href="{% url 'add' %}">2222</a>

  • 第三不:如果後端解析,先在view.py導入reverse模塊

 1 url(r'^test/',views.testadd,name='add')  2  3  4 from django.shortcuts import reverse  5  6 def test(request):  7     _url = reverse('add')  8     print(_url)  9     
10 # 經過reverse 模塊就能拿到test路由名 
11 # /test/
12 # 注意:必定要提早命名name,不然reverse會找不到

3.有名無名反向解析

分析:先在urls.py文件中創建有名無名分組

# 無名分組
url(r'^testadd/([0-9]{4})', views.testadd, name='add'), #有名分組
url(r'^test/(?P<ttt>\d+)', views.test),

而後views中導入reverse模塊,運行home函數

  • 無名後端反向解析

 1 from django.shortcuts import render, HttpResponse, redirect, reverse  2  3 def test(request, ttt):  4     return HttpResponse('test')  5  6  7 def testadd(request, xxx):  8     return HttpResponse('testadd')  9 10 11 def home(request): 12     _url = reverse('add') 13     print(_url) 14     return HttpResponse('歡迎歡迎')

運行後會報錯,表示查找add時缺一個參數

報錯緣由: 訪問這條url:url(r'^testadd/([0-9]{4})', views.testadd, name='add')

必須以/testadd/1234/這種形式,不然沒法訪問,所以結果的不肯定性讓Django沒法作判斷,所以須要人爲的給予數字

解決方案:在_url = reverse('add')中隨意給一個數字,例如

_url = reverse('add', args=(1234,)) # 注意:此處要以元組的形式添加 #補充:這個數字一般就是數據的主鍵值

再次啓動訪問home的url就能獲得/testadd/1234,經過他就能訪問testadd這條url了

  • 無名前端反向解析

def home(request): return render(request, 'home.html')
1 <!--一樣須要根據正則分組規則添加相應的數字-->
2 3 <a href="{% url 'add' 1234 %}">2222</a>
4 <a href="{% url 'add' 5628 %}">2222</a>
5 <a href="{% url 'add' 3637 %}">2222</a>
6 <a href="{% url 'add' 3839 %}">2222</a>

同上能夠得到

# 有名分組
url(r'^test/(?P<ttt>\d+)', views.test)
  • 有名後端反向解析

1 # 在reverse中添加kwargs={‘名字’:符合正則的符號}
2 def home(request): 3     _url = reverse('add', kwargs={'year':11})  # 標準寫法
4     _url = reverse('add', args=(11,) 5     print(_url) 6     return HttpResponse('歡迎歡迎')
  • 有名前端反向解析

{% url 'add' 1 %}  # 推薦使用
{% url 'add' year= 1 %}  # 標準的

總結:

1 # 無名分組反向解析:
2 3 url(r'^testadd/(\d+)/',views.testadd,name='add') 4         #前端解析
5             {% url 'add' 1 %} 6         #後端解析
7             reverse('add',args=(12,))
#
 1 有名分組反向解析:  2  3 url(r'^testadd/(\d+)/',views.testadd,name='add')  4  5 #前端解析
 6 {% url 'add' 1 %}  # 推薦使用
 7 {% url 'add' year = 1} # 標準使用
 8 #後端解析
 9 _url = reveerse('add', args=(11,) # 推薦使用
10 _url = reveerse('add', kwargs={'year':11} # 標準使用     
#僞代碼
url(r'^edit_user/(\d+)/',views.edit_user,names='edit') ​ {% for user_obj in user_queryset %} <a href="edit_user/{{ user_obj.id }}/">編輯</a>
        <a href="{% url 'edit' user_obj.id %}">編輯</a> {% endfor %} def edit_user(request,edit_id): reverse('edit',args=(edit_id,)) ​ """

4.路由分發

1).什麼是路由分發?

 當django項目比較龐大的時候 路由與視圖函數對應關係較多
總路由代碼太多冗長
考慮到總路由代碼很差維護 django支持每一個app均可以有本身的urls.py
而且總路由再也不作路由與視圖函數的對應關係 而僅僅只作一個分發任務的操做

根據請求的不一樣 識別出當前請求須要訪問的功能屬於哪一個app而後自動
下發到對應app裏面的urls.py中 而後由app裏面的urls.py作路由與視圖函數的匹配

不只如此每一個app除了能夠有本身的urls.py以外 還能夠有本身的static文件夾 templates模板文件
基於上面的特色 基於django分小組開發 會變得額外的簡單
每一個人只須要開發本身的app便可 以後只須要建立一個空的django項目
將多我的的app所有拷貝項目下 配置文件註冊
總路由分發一次

2).如何實現一個簡單的路由分發

第一步:在控制檯經過命令行建立多個app文件夾,並將app在settings中註冊

第二步:創建urls,導入include模塊,各類導入,看下圖

 

注意事項:

urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^app01/', include(app01_urls)), url(r'^app02/', include(app02_urls)), ] # 在總路由^app01/後面千萬不能添加$,不然沒法找到

補充:簡寫寫法

# 將導入和路由對應關係所有拿掉,用app.urls的方法
    # from app01 import urls as app01_urls
    # from app02 import urls as app02_urls
    # url(r'^app01/',include(app01_urls)),
    # url(r'^app02/',include(app02_urls))
    
    # 簡寫
    url(r'^app01/',include('app01.urls')), url(r'^app02/',include('app02.urls'))

5.名稱空間(瞭解)

問題:若是在不一樣的app中的路由對應關係中添加了相同的name,那麼在反向解析的時候可否準確找到

結果是必定的,不能準確找到,那如何解決這個問題?

能夠再主路由對應列表中添加namespace屬性,以下所示

url(r'^app01/', include('app01.urls', namespace='app01')) url(r'^app02/', include('app02.urls', namespace='app02'))
後端解析
reverse('app01:id') reverse('app02:id') ​
前端解析
{% url 'app01:index' %} {% url 'app02:index' %}

如何避免使用這種方法?

1 # 在給路由與視圖函數起別名(name的名字)的時候只須要保證永遠不出現衝突的狀況便可
2 # 一般狀況下咱們推薦期別名的時候加上當前應用的應用名前綴
3 #例如
4 url(r'^index/',views.index,name='app01_index') 5 url(r'^index/',views.index,name='app02_index')

6.虛擬環境

爲何使用虛擬環境?

咱們想作到針對不一樣的項目 只安裝項目所須要的功能模塊 項目用不到的一律不裝 來避免加載資源時的消耗

注意:此次建立須要用之前創立好的虛擬環境來創立

虛擬環境就相似於一個純淨的python解釋器環境
每建立一個虛擬幻境就相似於你從新下載一個python解釋器
另外虛擬環境不推薦使用太多,能夠建立幾個玩一玩,不然佔用資源

7.DJango版本區別

路由層:

  • 用的是url,第一個參數是正則表達式

  • 2.x和3.x用的path,第一個參數不是正則表達式,寫的是什麼就匹配什麼,精確匹配

1.x版本 urlpatterns = [ url(r'^admin/', admin.site.urls), ] 2.x和3.x版本 urlpatterns = [ path('admin/', admin.site.urls), ]

若是以爲path很差用 2.x、3.x給你提供了一個跟url同樣的功能 re_path 等價於1.x裏面的url功能

雖然path不支持正則表達式 可是它給你提供了五種默認的轉換器

  • str:匹配除了路徑分隔符(/)以外的非空字符串,這是默認的形式

  • int:匹配正整數,包含0。

  • slug,匹配字母、數字以及橫槓、下劃線組成的字符串。

  • uuid,匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00。

  • path,匹配任何非空字符串,包含了路徑分隔符(/)(不能用?)

除了默認的五種轉換器以外 還支持你自定義轉換器

1 class MonthConverter: 2         regex='\d{2}' # 屬性名必須爲regex
3     
4     def to_python(self, value): 5         return int(value) 6 7     def to_url(self, value): 8         return value # 匹配的regex是兩個數字,返回的結果也必須是兩個數字

8.僞靜態

僞靜態 url以.html結尾 給人的感受好像是這個文件是寫死的 內容不會輕易的改變 僞靜態

爲了提升你的網站被搜索引擎收藏的力度 提供網站的SEO查詢效率

可是 不管你怎麼作優化 都抗不過RMB玩家

四.視圖層

1)JsonResponse

視圖函數必需要返回一個HttpResponse對象,由於好比這三板斧都是直接或間接的繼承HTTresponse

  • HttpResponse

 

 

  • render

  • redirect

JsonResponse

先後端數據交互

舉例:

from django.http import JsonResponse def xxx(request): user_dict = {'username':'jason','password':'123'} json_str=json.dumps(user_dict) return HttpResponse(json_str)

若添加中文進去,就會發生自動轉碼的事情,以下圖

from django.http import JsonResponse def xxx(request): user_dict = {'username':'jason好帥哦 我好喜歡!','password':'123'} json_str=json.dumps(user_dict) return HttpResponse(json_str)

緣由:

因此只要將ensure_ascii修改爲False便可

json_str = json.dumps(user_dict,ensure_ascii=False)

也可使用JsonResponse

return JsonResponse(user_dict,json_dumps_params={'ensure_ascii':False})

如果一個列表

l = [1,2,3,4,5,6,7,8,9,] return JsonResponse(l,safe=False)  # 序列化非字典格式數據 須要將safe改成False

forml表單

1.必須作的事
  method必須是post
  enctype必須是formdata
2.暫時須要作的
  提交post請求須要將中間件裏面的一個csrfmiddleware註釋掉

後端如何獲取用戶上傳的文件
  file_obj = request.FILES.get('前端input框name屬性值')
  file_obj.name # 文件名
  for line in file_obj:
  print(line)

# django中推薦如下寫法
  for chunk in file_obj.chunks():
  print(chunk)

render原理

def ab_render(request):   temp = Template("<h1>{{ user_dict }}{{ user_dict.username }}{{ user_dict.password }}</h1>")   user_dict = Context({'user_dict':{'username':'jason','password':123}})   res = temp.render(user_dict)   return HttpResponse(res)

補充:視圖函數不必定是函數,也有多是類

FBV:基於函數的視圖
CBV:基於類的視圖

CBV基本寫法

from django.views import View class MyLogin(View): def get(self,request): return render(request,'login.html') def post(self,request): return HttpResponse('我是類裏面的post方法') url(r'^login/',views.MyLogin.as_view())

朝login提交get請求會自動執行MyLogin裏面的get方法
而提交post請求也會自動執行MyLogin裏面的post方法
爲何MyLogin針對不一樣的請求方法可以自動執行對應的方法

研究源碼的突破口
url(r'^login/',views.MyLogin.as_view())


猜測
as_view要麼是類裏面定義的普通函數 @staticmethod
要麼是類裏面定義的綁定給類的方法 @classmethod

看源碼發現是綁定給類的方法

相關文章
相關標籤/搜索