用戶認證是經過取表單數據根數據庫對應表存儲的值作比對,比對成功就返回一個頁面,不成功就重定向到登陸頁面。咱們本身寫的話固然也是能夠的,只不過多寫了幾個視圖,冗餘代碼多,固然咱們也能夠封裝成函數,簡單代碼。不過推薦使用Django提供的一套用戶認證組件,原理其實相似,只不過功能更強大。php
在進行用戶登陸驗證的時候,若是是本身寫代碼,就必需要先查詢數據庫,看用戶輸入的用戶名是否存在於數據庫中;若是用戶存在於數據庫中,而後在驗證用戶輸入的密碼,這樣一來,本身就須要編寫大量的代碼。html
事實上,Django已經提供了內置的用戶認證功能,在使用「python manage.py makemigrations」 和 「python manage.py migrate」 遷移完成數據庫以後,根據配置文件settings.py中的數據庫段生成的數據表中已經包含了6張進行認證的數據表,分別是:python
而要進行用戶認證的數據表示auth_user。web
要使用Django自帶的認證功能,首先導入auth模塊:ajax
# auth主認證模塊 from django.contrib.auth.models import auth # 對應數據庫,能夠建立添加記錄 from django.contrib.auth.models import User
django.contrib.auth中提供了許多方法,這裏主要介紹其中三個:算法
提供了用戶認證,即驗證用戶名以及密碼是否正確,通常須要username,password 兩個關鍵字參數。sql
若是認證信息有效,會返回一個user對象。authenticate()會在User對象上設置一個屬性標識那種認證後端認證了該用戶,且該信息在後面的登陸過程當中是須要的。當咱們試圖登陸一個從數據庫中直接取出來不通過authenticate()的User對象會報錯的!數據庫
user=authenticate(username="uaername",password="password") login(HttpResponse,user)
這個函數接受一個HTTPRequest對象,以及一個經過authenticate() 函數認證的User對象。django
該函數接受一個HttpRequest對象 ,以及一個認證了的User對象,此函數使用django的session框架給某個已認證的用戶附加上session id等信息。後端
from django.shortcuts import redirect, HttpResponse from django.contrib.auth import authenticate, login def auth_view(request): username = request.POST['username'] password = request.POST['password'] user = authenticate(username=username, password=password) if user is not None: login(request, user) # Redirect to a success page. return redirect("/index/") else: # Return an 'invalid login' error message. return HttpResponse("username or password is incorrect")
該函數實現一個用戶登陸的功能。它本質上會在後端爲該用戶生成相關session數據。
from django.contrib.auth import logout def logout_view(request): logout(request) # Redirect to a success page.
該函數接受一個HttpRequest對象,無返回值。當調用該函數時,當前請求的session信息會所有清除。該用戶即便沒有登陸,使用該函數也不會報錯。
雖然使用的logout()函數,可是其本質上仍是使用的是fulsh() 。咱們能夠查看 auth.logout()函數的源碼
從源碼中發現,驗證完以後,仍是使用 request.session.flush() 進行刪除session信息。
User對象屬性:username,password(必填項) password用哈希算法保存到數據庫。
django Auth模塊自帶User模型所包含字段
username:用戶名 email: 電子郵件 password:密碼 first_name:名 last_name:姓 is_active: 是否爲活躍用戶。默認是True is_staff: 是否爲員工。默認是False is_superuser: 是否爲管理員。默認是False date_joined: 加入日期。系統自動生成。
若是是真正的User對象,返回值恆爲True,用於檢查用戶是否已經經過了認證。經過認證並不意味着用戶認證擁有任何權限,甚至也不檢查該用戶是否處於激活狀態,這只是代表用戶成功的經過了認證。這個方法很重要,在後臺用request.user.is_authenticated()判斷用戶是否已經登陸,若是true則能夠向前臺展現request.user.name。
要求:
方法一:
def my_view(request): if not request.user.is_authenticated(): return redirect("%s?next=%s"%(settings.LOGIN_URL, request.path))
方法二:
django已經爲咱們設計好了一個用於此種狀況的裝飾器:login_required()
login_required():用來快捷的給某個視圖添加登陸校驗的裝飾器工具。
from django.contrib.auth.decorators import login_required @login_required def my_view(request): ...
若用戶沒有登陸,則會跳轉到django默認的登陸URL ‘/accounts/login/’並傳遞當前訪問url的絕對路徑(登陸成功後,會重定向到該路徑)。
若是須要自定義登陸的URL,則須要在settings.py文件中經過LOGIN_URL進行修改。
LOGIN_URL = '/login/' # 這裏配置成你項目登陸頁面的路由
使用create_user輔助函數建立用戶
from django.contrib.auth.models import User user = User.objects.create_user(username=" " , password =" ", email=" ")
使用create_superuser()建立超級用戶
from django.contrib.auth.models import User user = User.objects.create_superuser(username='用戶名',password='密碼',email='郵箱',...)
is_authenticated()用來判斷當前請求是否經過了認證。
用法:
def my_view(request): if not request.user.is_authenticated(): return redirect('%s?next=%s' % (settings.LOGIN_URL, request.path))
使用check_password(passwd)來檢查密碼是否正確,密碼正確的話返回True,不然返回False。
ok = user.check_password('密碼')
使用set_password() 來修改密碼,接受要設置的新密碼做爲參數。
用戶須要修改密碼的時候,首先讓他輸入原來的密碼,若是給定的字符串經過了密碼檢查,返回True
注意:設置完必定要調用用戶對象的save方法
user = User.objects.get(username = ' ' ) user.set_password(password ='') user.save
修改密碼示例:
from django.contrib.auth.models import User from django.shortcuts import HttpResponse def register(request): # 建立用戶 user_obj = User.objects.create_user(username='james', password='123') # 檢查密碼(通常用於修改密碼前驗證) ret = user_obj.check_password('123') print(ret) # 返回False # 修改密碼 user_obj.set_password('1234') # 修改後保存 user_obj.save() # 修改後檢查 ret = user_obj.check_password('1234') print(ret) # 返回True return HttpResponse("OK")
from django.shortcuts import render,redirect,HttpResponse from django.contrib.auth.models import User def create_user(request): msg=None if request.method=="POST": username=request.POST.get("username"," ") # 獲取用戶名,默認爲空字符串 password=request.POST.get("password"," ") # 獲取密碼,默認爲空字符串 confirm=request.POST.get("confirm_password"," ") # 獲取確認密碼,默認爲空字符串 if password == "" or confirm=="" or username=="": # 若是用戶名,密碼或確認密碼爲空 msg="用戶名或密碼不能爲空" elif password !=confirm: # 若是密碼與確認密碼不一致 msg="兩次輸入的密碼不一致" elif User.objects.filter(username=username): # 若是數據庫中已經存在這個用戶名 msg="該用戶名已存在" else: new_user=User.objects.create_user(username=username,password=password) #建立新用戶 new_user.save() return redirect("/index/") return render(request,"login.html",{"msg":msg})
from django.shortcuts import render, redirect, HttpResponse from django.contrib.auth import authenticate, login, logout from django.contrib.auth.decorators import login_required from django.contrib.auth.models import User @login_required def change_passwd(request): user = request.user # 獲取用戶名 msg = None if request.method == 'POST': old_password = request.POST.get("old_password", "") # 獲取原來的密碼,默認爲空字符串 new_password = request.POST.get("new_password", "") # 獲取新密碼,默認爲空字符串 confirm = request.POST.get("confirm_password", "") # 獲取確認密碼,默認爲空字符串 if user.check_password(old_password): # 到數據庫中驗證舊密碼經過 if new_password or confirm: # 新密碼或確認密碼爲空 msg = "新密碼不能爲空" elif new_password != confirm: # 新密碼與確認密碼不同 msg = "兩次密碼不一致" else: user.set_password(new_password) # 修改密碼 user.save() return redirect("/index/") else: msg = "舊密碼輸入錯誤" return render(request, "change_passwd.html", {"msg": msg})
1,導入AbstractUser
from django.contrib.auth.models import AbstractUser
2,建立類Userprofile並繼承 AbstractUser
class UserProfile(AbstractUser)
3,建立本身須要的UserProfile字段
class UserProfile(AbstractUser): nick_name = models.CharField(max_length=50, verbose_name=u'暱稱', default='') birthday = models.DateField(verbose_name=u'生日', null=True, blank=True) gender = models.CharField(choices=(('male', u'男'), ('female', u'女')), default='male', verbose_name=u'性別') address = models.CharField(max_length=100, verbose_name=u'地址')
4,在settings.py中重載 AUTH_USER_MODEL 方法
AUTH_USER_MODEL = 'users.UserProfile'
5,注意:若是在migrate時候報錯,刪除數據庫的表,從新遷移。
報錯內容以下:
django.db.migrations.exceptions.InconsistentMigrationHistory: Migration admin.0001_initial is applied before its dependency users.0001_initial on database 'default'.
功能就是用session記錄登陸驗證狀態,可是前提是要使用Django自帶的auth_user,因此咱們須要建立超級用戶。下面都會說到。其用戶認證組件最大的優勢就是 request.user 是一個全局變量,在任何視圖和模板中都能直接使用。
重點是下面:
if not: auth.logout(request, user) #此時返回的對象 request.user == AnonymousUser() else: request.user == 登陸對象
下面咱們完成一個登錄驗證信息存儲功能,咱們使用的就是上面講到的auth模塊的authenticate()函數。上面也說到了,authenticate()提供了用戶認證,即驗證用戶名和密碼是否正確,若是認證信息有效的話,會返回一個user對象。
因爲User表不是咱們建立的,並且人家密碼是加密的,如何加密咱們並不知道。因此提取認證方式只能使用人家設置的認證方式。
咱們要的是,建立一個超級用戶,而後寫一個登陸驗證函數,若是驗證成功,進入索引界面。若是驗證失敗,則繼續留在驗證頁面。
首先,咱們建立一個超級用戶:
python manage.py createsuperuser
名稱爲 james, 密碼爲 123。咱們在數據庫的 auth_user中查看:
而後咱們完成登陸驗證函數和簡單的索引函數。
from django.shortcuts import render, HttpResponse, redirect # Create your views here. from django.contrib import auth from django.contrib.auth.models import User from auth_demo import models def login(request): if request.method == 'POST': user = request.POST.get('user') pwd = request.POST.get('pwd') # if 驗證成功返回 user 對象,不然返回None user = auth.authenticate(username=user, password=pwd) if user: # request.user : 當前登陸對象 auth.login(request, user) # return HttpResponse("OK") return redirect('/auth/index') return render(request, 'auth/login.html') def index(request): print('request.user', request.user.username) print('request.user', request.user.id) # 下面是判斷是是不是匿名 print('request.user', request.user.is_anonymous) if request.user.is_anonymous: # if not request.user.authenticated(): return redirect('/auth/login') username = request.user.username return render(request, 'auth/index.html', locals())
下面寫一個簡單的login頁面 和 index頁面。
login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>login page</h1> <form action="" method="post"> {% csrf_token %} <p>username:<input type="text" name="user" ></p> <p>password:<input type="password" name="pwd"></p> <p><input type="submit" value="提交"></p> </form> </body> </html>
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>this is index page</h1> <p>{{ username }}</p> </body> </html>
下面進入登陸界面,若是登陸成功,則進入索引界面,咱們輸入正確的帳號和密碼。
點擊提交以下:
上面咱們也說了,用戶註銷的話,咱們可使用request.session.fulsh()。可是Django自帶了auth.logout()函數,爲何使用這個呢?其實咱們前面也看了源碼。在進行驗證後,使用request.session.fulsh(),可是他最後使用了匿名函數 AnonymousUser()。
下面咱們寫一個註銷函數:
def logout(request): auth.logout(request) return redirect(request, '/auth/login/')
其實很是簡單,註銷後,將頁面重定向到登陸頁面。咱們在前臺索引頁面,加上一個註銷的功能:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>this is index page</h1> <p>{{ username }}</p> <a href="/auth/login">註銷</a> </body> </html>
點進去索引界面以下:
咱們點擊註銷,則返回到登陸頁面,以下:
點擊註銷後,咱們能夠去數據庫查看 django_session 的內容,會發現,註銷後,一條記錄就沒有了。
在上面用戶登陸的時候,咱們會發現有一行代碼,是
user = auth.authenticate(username=user, password=pwd)
也就是說,數據庫已經存在了數據,那麼要是沒數據的話,咱們怎麼辦?
下面咱們演示一個註冊用戶的功能。
咱們將數據從前臺拿過來,咱們下面就是插入數據到User表中,這裏注意的是,咱們不能直接插入,好比下面:
user = User.objects.create(username=user, password=pwd)
上面插入時是明文插入,可是咱們不能這樣,咱們須要插入的是加密後的密碼,因此使用下面代碼:
user = User.objects.create_user(username=user, password=pwd)
OK,說完注意點,咱們寫註冊視圖函數:
def register(request): if request.method == 'POST': user = request.POST.get('user') pwd = request.POST.get('pwd') user = User.objects.create_user(username=user, password=pwd) return HttpResponse("OK") return render(request, 'auth/register.html')
當咱們註冊成功後,顯示OK便可(簡單方便)。
註冊頁面和登陸相似,咱們展現以下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>register page</h1> <form action="" method="post"> {% csrf_token %} <p>username:<input type="text" name="user" ></p> <p>password:<input type="password" name="pwd"></p> <p><input type="submit" value="提交"></p> </form> </body> </html>
註冊頁面以下:
咱們註冊一個 durant,咱們在 auth_user 數據庫中查看結果:
註冊成功以下:
auth_user 數據表以下:
這代表咱們註冊成功了。
爲何要用認證裝飾器呢?
在之後的代碼中,咱們確定須要不少認證登陸,若是驗證成功,則進入登陸頁面,若是驗證不成功,則返回到登陸頁面。那麼爲了不代碼的冗餘,咱們能夠寫一個裝飾器的東西,不過Django已經爲咱們準備好了,咱們只須要用就行。
驗證裝飾器:看那些頁面須要登陸才能訪問,若是沒有登陸跳轉設置的頁面去。
注意:在settings.py中設置以下:
# 用於auth模塊 裝飾器校驗返回頁面 LOGIN_URL = '/login/'
在django項目中,常常看有下面的代碼:
from django.contrib.auth.decorators import login_required from django.contrib.auth.models import User @login_required def my_view(request): pass
裏面有一個@login_required標籤。其做用就是告訴程序,使用這個方法是要求用戶登陸的。
下面舉個例子:
def index(request): username = request.user.username return render(request, 'auth/index.html', locals())
咱們訪問上面已經完成的 index頁面,咱們會發現:
當咱們給 index 視圖函數加上裝飾器,代碼以下:
from django.contrib.auth.decorators import login_required @login_required def index(request): username = request.user.username return render(request, 'auth/index.html', locals())
咱們再來訪問:
下面說一下,URL是什麼意思呢?
1,若是用戶尚未登陸,默認會跳轉到'/accounts/login/'。這個值能夠在settings文件中經過LOGIN_URL參數來設定。(後面還會自動加上你請求的url做爲登陸後跳轉的地址,如:/accounts/login/?next=/auth/index/ 登陸完成以後,會去請求)
# 若是不添加該行,則在未登錄狀態打開頁面的時候驗證是否登錄的裝飾器跳轉到/accounts/login/下面 # 第一張方法就是修改settings.py 中的 LOGIN_URL LOGIN_URL = "/login/"
以下:
爲何會報錯呢?由於咱們沒有設置其路徑。
咱們在settings.py中設置登陸URL(固然這是我本身的路由地址):
# 這裏配置成項目登陸頁面的路由 LOGIN_URL = '/auth/login'
下面訪問 index則以下:
沒有用戶沒有登陸的話,則直接跳轉到登陸頁面。
咱們不能講登陸視圖函數的代碼寫死,這裏改進一下,以下:
def login(request): if request.method == 'POST': user = request.POST.get('user') pwd = request.POST.get('pwd') # if 驗證成功返回 user 對象,不然返回None user = auth.authenticate(username=user, password=pwd) if user: # request.user : 當前登陸對象 auth.login(request, user) next_url = request.GET.get('next', 'auth/index') return redirect(next_url) return render(request, 'auth/login.html')
若是驗證成功,咱們跳轉到 next_url,若是獲取不到,則跳轉到index頁面。
2,若是用戶登陸了,那麼該方法就能夠正常執行
若是LOGIN_URL使用默認值,那麼在urls.py中還須要進行以下設置:
# 第二種解決方法是在 url 中匹配該url (r'^accounts/login/$', 'django.contrib.auth.views.login'),
這樣的話,若是未登陸,程序會默認跳轉到「templates/registration/login/html」 這個模板。
若是想換個路徑,那就在加個template_name參數,以下:
(r'^accounts/login/$', 'django.contrib.auth.views.login', {'template_name': 'myapp/login.html'}),
這樣程序就會跳轉到 template/myapp/login.html 中。
我在以前的Django學習筆記(2):模板後臺管理和視圖的學習 中學習了視圖層,並對其有了大概的理解。如今再進一層的學習視圖層中request屬性和HttpResponse對象的方法。
一個視圖函數,簡稱視圖,是一個簡單的Python函數,它接受Web請求並返回Web響應。響應能夠是一張網頁的HTML內容,一個重定向,一個404錯誤,一個XML文檔,或者一張圖片...是任何東西均可以。不管視圖自己包含什麼邏輯,都要返回響應。爲了將代碼放在某處,約定是將視圖放置在項目或者應用程序目錄中的名爲 views.py 的問卷中。
下面是一個返回當前日期和時間做爲HTML 文檔的視圖:
from django.shortcuts import render, HttpResponse, HttpResponseRedirect, redirect import datetime def current_datetime(request): now = datetime.datetime.now() html = "<html><body>It is now %s.</body></html>" % now return HttpResponse(html)
咱們來逐行閱讀上面的代碼:
而視圖層中,熟練掌握兩個對象:請求對象(request)和響應對象(HttpResponse)
django將請求報文中的請求行,首部信息,內容主體封裝成 HttpRequest 類中的屬性。除了特殊說明以外,其餘的均爲只讀。
1.HttpRequest.GET 一個相似於字典的對象,包含 HTTP GET 的全部參數。詳情請參考 QueryDict 對象。 2.HttpRequest.POST 一個相似於字典的對象,若是請求中包含表單數據,則將這些數據封裝成 QueryDict 對象。 POST 請求能夠帶有空的 POST 字典 —— 若是經過 HTTP POST 方法發送一個表單,可是 表單中沒有任何的數據,QueryDict 對象依然會被建立。 所以,不該該使用 if request.POST 來檢查使用的是不是POST 方法; 應該使用 if request.method == "POST" 另外:若是使用 POST 上傳文件的話,文件信息將包含在 FILES 屬性中。 注意:鍵值對的值是多個的時候,好比checkbox類型的input標籤,select標籤,須要用: request.POST.getlist("hobby") 3.HttpRequest.body 一個字符串,表明請求報文的主體。在處理非 HTTP 形式的報文時很是有用, 例如:二進制圖片、XML,Json等。 可是,若是要處理表單數據的時候,推薦仍是使用 HttpRequest.POST 。 4.HttpRequest.path 一個字符串,表示請求的路徑組件(不含域名)。 例如:"/music/bands/the_beatles/" 5.HttpRequest.method 一個字符串,表示請求使用的HTTP 方法。必須使用大寫。 例如:"GET"、"POST" 6.HttpRequest.encoding 一個字符串,表示提交的數據的編碼方式(若是爲 None 則表示使用 DEFAULT_CHARSET 的設置,默認爲 'utf-8')。 這個屬性是可寫的,你能夠修改它來修改訪問表單數據使用的編碼。 接下來對屬性的任何訪問(例如從 GET 或 POST 中讀取數據)將使用新的 encoding 值。 若是你知道表單數據的編碼不是 DEFAULT_CHARSET ,則使用它。 7.HttpRequest.META 一個標準的Python 字典,包含全部的HTTP 首部。具體的頭部信息取決於客戶端 和服務器,下面是一些示例: CONTENT_LENGTH —— 請求的正文的長度(是一個字符串)。 CONTENT_TYPE —— 請求的正文的MIME 類型。 HTTP_ACCEPT —— 響應可接收的Content-Type。 HTTP_ACCEPT_ENCODING —— 響應可接收的編碼。 HTTP_ACCEPT_LANGUAGE —— 響應可接收的語言。 HTTP_HOST —— 客服端發送的HTTP Host 頭部。 HTTP_REFERER —— Referring 頁面。 HTTP_USER_AGENT —— 客戶端的user-agent 字符串。 QUERY_STRING —— 單個字符串形式的查詢字符串(未解析過的形式)。 REMOTE_ADDR —— 客戶端的IP 地址。 REMOTE_HOST —— 客戶端的主機名。 REMOTE_USER —— 服務器認證後的用戶。 REQUEST_METHOD —— 一個字符串,例如"GET" 或"POST"。 SERVER_NAME —— 服務器的主機名。 SERVER_PORT —— 服務器的端口(是一個字符串)。 從上面能夠看到,除 CONTENT_LENGTH 和 CONTENT_TYPE 以外,請求中的任何 HTTP 首部轉換爲 META 的鍵時,都會將全部字母大寫並將鏈接符替換爲下劃線最後加上 HTTP_ 前綴。 因此,一個叫作 X-Bender 的頭部將轉換成 META 中的 HTTP_X_BENDER 鍵。 8.HttpRequest.FILES 一個相似於字典的對象,包含全部的上傳文件信息。 FILES 中的每一個鍵爲<input type="file" name="" /> 中的name,值則爲對應的數據。 注意,FILES 只有在請求的方法爲POST 且提交的<form> 帶有 enctype="multipart/form-data" 的狀況下才會包含數據。不然,FILES 將爲一個空的 相似於字典的對象。 9.HttpRequest.COOKIES 一個標準的Python 字典,包含全部的cookie。鍵和值都爲字符串。 10.HttpRequest.session 一個既可讀又可寫的相似於字典的對象,表示當前的會話。只有當Django 啓用會話的支持時纔可用。 完整的細節參見會話的文檔。 11.HttpRequest.user(用戶認證組件下使用) 一個 AUTH_USER_MODEL 類型的對象,表示當前登陸的用戶。 若是用戶當前沒有登陸,user 將設置爲 django.contrib.auth.models.AnonymousUser 的一個實例。你能夠經過 is_authenticated() 區分它們。 例如: if request.user.is_authenticated(): # Do something for logged-in users. else: # Do something for anonymous users. user 只有當Django 啓用 AuthenticationMiddleware 中間件時纔可用。 -------------------------------------------------------------------------------------
匿名用戶 class models.AnonymousUser django.contrib.auth.models.AnonymousUser 類實現了 django.contrib.auth.models.User 接口,但具備下面幾個不一樣點: id 永遠爲None。 username 永遠爲空字符串。 get_username() 永遠返回空字符串。 is_staff 和 is_superuser 永遠爲False。 is_active 永遠爲 False。 groups 和 user_permissions 永遠爲空。 is_anonymous() 返回True 而不是False。 is_authenticated() 返回False 而不是True。 set_password()、check_password()、save() 和delete() 引起 NotImplementedError。 New in Django 1.8: 新增 AnonymousUser.get_username() 以更好地模擬 django.contrib.auth.models.User。
HTTP的應用信息是經過請求報文和響應報文傳遞的。
其中請求報文由客戶端發送,其中包含和許多的信息,而Django將這些信息封裝成了HTTPRequest對象,該對象由HTTPRequest類建立,每個請求都會生成一個HttpRequest對象,Django會將這個對象自動傳遞給響應的視圖函數,通常視圖函數約定俗成使用request參數承接這個對象。
固然,你也可使用其餘參數來承接這個對象,並無硬性規定必定要使用什麼名稱。
1.HttpRequest.get_full_path() 返回 path,若是能夠將加上查詢字符串。 例如:"/music/bands/the_beatles/?print=true" 2.HttpRequest.is_ajax() 若是請求是經過XMLHttpRequest 發起的,則返回True,方法是檢查 HTTP_X_REQUESTED_WITH 相應的首部是不是字符串'XMLHttpRequest'。 大部分現代的 JavaScript 庫都會發送這個頭部。若是你編寫本身的 XMLHttpRequest 調用(在瀏覽器端),你必須手工設置這個值來讓 is_ajax() 能夠工做。 若是一個響應須要根據請求是不是經過AJAX 發起的,而且你正在使用某種形 式的緩存例如Django 的 cache middleware, 你應該使用 vary_on_headers('HTTP_X_REQUESTED_WITH') 裝飾你的視圖 以讓響應可以正確地緩存。
補充:HttpRequest.path_info
一個字符串,在某些Web服務器配置下,主機名後的URL部分被分紅腳本前綴部分和路徑信息部分。path_info屬性將始終包含路徑信息部分,不管使用的什麼Web服務器,使用它代替path 可讓代碼在測試和開發環境中更容易的切換。
例如,若是應用的WSGIScriptAlias 設置爲"/minfo",那麼當 path 是"/minfo/music/bands/the_beatles/" 時path_info 將是"/music/bands/the_beatles/"。
響應對象主要有三種形式:
對於HttpRequest對象來講,是由Django自動建立的,可是HttpResponse對象就必須由咱們本身建立,每一個view請求處理方法必須返回一個HttpRequest對象。
可是須要注意的是,不管是下面的render() 仍是 redirect() 最終仍是調用HttpResponse,只不過在過程當中使用Django的模板語法封裝了內容。
咱們下面展現一下render() redirect()的源碼:
下面直接看着兩個響應對象。
在實際運用中,加載模板,傳遞參數,返回HttpResponse對象是一整套再長不過的操做了,爲了節省力氣,Django提供了一個快捷方式:render()函數,一步到位。
render(request, template_name[, context]) 結合一個給定的模板和一個給定的上下文字典,並返回一個渲染後的 HttpResponse 對象。
參數意義:
總之,render方法就是將一個模板頁面中的模板語法進行渲染,最終渲染成一個HTML頁面做爲響應體。render() 函數第一個位置參數就是請求對象(就是view函數的第一個參數),第二個位置參數是模板,還能夠有一個可選擇的第三個參數,一個字典,包含須要傳遞給模板的數據,最後render函數返回一個通過字典數據渲染過的模板封裝而成的HttpResponse對象。
舉個例子:
def logout(request): # del request.session['is_login'] request.session.flush() return render(request, 'session/a.html')
傳遞要重定向的一個硬編碼的URL
def my_view(request): ... return redirect('/some/url/')
也能夠是一個完整的URL:
def my_view(request): ... return redirect('http://example.com/')
key:兩次請求
1)301和302的區別。 301和302狀態碼都表示重定向,就是說瀏覽器在拿到服務器返回的這個狀態碼 後會自動跳轉到一個新的URL地址,這個地址能夠從響應的Location首部中獲取 (用戶看到的效果就是他輸入的地址A瞬間變成了另外一個地址B)——這是它們的共同點。 他們的不一樣在於。301表示舊地址A的資源已經被永久地移除了(這個資源不可訪問了), 搜索引擎在抓取新內容的同時也將舊的網址交換爲重定向以後的網址; 302表示舊地址A的資源還在(仍然能夠訪問),這個重定向只是臨時地從舊地址A跳轉 到地址B,搜索引擎會抓取新的內容而保存舊的網址。 SEO302好於301 2)重定向緣由: (1)網站調整(如改變網頁目錄結構); (2)網頁被移到一個新地址; (3)網頁擴展名改變(如應用須要把.php改爲.Html或.shtml)。 這種狀況下,若是不作重定向,則用戶收藏夾或搜索引擎數據庫中舊地址只能讓訪問 客戶獲得一個404頁面錯誤信息,訪問流量白白喪失;再者某些註冊了多個域名的 網站, 也須要經過重定向讓訪問這些域名的用戶自動跳轉到主站點等。
舉個例子:
def order(request): if not request.user.is_authenticated: return redirect('auth/login/') return render(request, 'auth/order.html')
1,if render 的頁面須要模板語言渲染,須要的將數據庫的數據加載到HTML,那麼全部的這一部分除了寫在視圖函數中,必須還要寫在login中,沒有解耦。
2,render()是渲染變量到模板中,而redirect是HTTP中的1個跳轉的函數,通常會生成302 狀態碼。
下面咱們編寫一個具體問卷文本內容的視圖,若是訪問的話,請求問卷的ID不存在,那麼會彈出一個Http404 錯誤。
from django.http import Http404 from django.shortcuts import render from .models import Question # ... def detail(request, question_id): try: question = Question.objects.get(pk=question_id) except Question.DoesNotExist: raise Http404("Question does not exist") return render(request, 'polls/detail.html', {'question': question})
固然,就像render函數同樣,Django一樣爲咱們提供一個偷懶的方式,替代上面的多行代碼。
快捷方式 : get_object_or_404()
修改代碼入下:
from django.shortcuts import get_object_or_404, render from .models import Question # ... def detail(request, question_id): question = get_object_or_404(Question, pk=question_id) return render(request, 'polls/detail.html', {'question': question})
首先,get_object_or_404()方法也是須要從Django內置的快捷方式模塊中導出。
其次,get_object_or_404()方法將一個Django模型做爲第一個位置參數,後面能夠跟上任意個數的關鍵字參數,若是對象不存在則彈出Http404錯誤。
一樣,還有一個 get_list_or_404() 方法,和上面的 get_object_or_404() 相似,只不過用來替代 filter() 函數,當查詢列表爲空時彈出404錯誤。
Django ORM用到三個類:Manager,QuerySet,Model。
Manager定義表級方法(表級方法就是影響一條或多條記錄的方法),咱們能夠以model.Manager爲父類,定義本身的manager,增長表級方法;
QuerySet:Manager類的一些方法會返回QuerySet實例,QuerySet是一個可遍歷結構,包含一個或多個元素,每一個元素都是一個Model實例,它裏面的方法也是表級方法;
Model是指django.db.models模塊中的Model類,咱們定義表的model時,就是繼承它,它的功能很強大,經過自定義model的instance能夠獲取外鍵實體等,它的方法都是基類級方法,不要在裏面定義類方法,好比計算記錄的總數,查看全部記錄,這些應該放在自定義的manager類中。
每一個Model都有一個默認的manager實例,名爲objects,QuerySet有兩種來源:經過manager的方法獲得,經過QuerySet的方法獲得。manager的方法和QuerySet的方法大部分同名,贊成思,如 filter(),update()等,可是也有些不一樣,如 manager有 create(),get_or_create(),而QuerySet有delete() 等,看源碼就能夠很容易的清楚Manager類和QuerySet類的關係,Manager類的絕大部分方法是基於QuerySet的。一個QuerySet包含一個或多個model instance。QuerySet相似於Python中的list,list的一些方法QuerySet也有,好比切片,遍歷。
QuerySet是查詢集,就是傳到服務器上的url裏面的內容,Django會對查詢返回的結果集QuerySet進行緩存,這是爲了提升查詢效率。也就是說,在建立一個QuerySet對象的時候,Django不會當即向數據庫發出查詢命令,只有在你須要用到這個QuerySet的時候纔會去數據庫查詢。
object是Django實現的MCV框架中的數據層(model)M,django中的模型類都有一個object對象,他是django中定義的QuerySet類型的對象,他包含了模型對象的實例。
簡單來講,object是單個對象,QuerySet是多個對象。
在內部,建立,過濾,切片和傳遞一個QuerySet不會真實操做數據庫,在你對查詢集提交以前,不會發生任何實際的數據庫操做。
可使用下列方法對QuerySet提交查詢操做。
QuerySet是可迭代的,在首次迭代查詢集中執行的實際數據庫查詢。例如,下面的語句會將數據庫中全部entry的headline打印出來。
for e in Entry.objects.all(): print(e.headline)
切片:若是使用切片的」step「參數,Django 將執行數據庫查詢並返回一個列表。
Pickling/緩存
repr()
len():當你對QuerySet調用len()時, 將提交數據庫操做。
list():對QuerySet調用list()將強制提交操做entry_list = list(Entry.objects.all())
bool()
測試布爾值,像這樣:
if Entry.objects.filter(headline="Test"): print("There is at least one Entry with the headline Test")
注:若是你須要知道是否存在至少一條記錄(而不須要真實的對象),使用exists() 將更加高效。
下面對QuerySet正式定義:
class QuerySet(model=None, query=None, using=None)[source]
所謂惰性機制:Publisher.objects.all() 或者.filter()等都只是返回了一個QuerySet(查詢結果集對象),它並不會立刻執行sql,而是當調用QuerySet的時候才執行。
QuerySet特色:
#objs=models.Book.objects.all()#[obj1,obj2,ob3...] #QuerySet: 可迭代 # for obj in objs:#每一obj就是一個行對象 # print("obj:",obj) # QuerySet: 可切片 # print(objs[1]) # print(objs[1:4]) # print(objs[::-1])
<1>Django的queryset是惰性的 Django的queryset對應於數據庫的若干記錄(row),經過可選的查詢來過濾。 例如,下面的代碼會獲得數據庫中名字爲‘Dave’的全部的人:person_set = Person.objects.filter(first_name="Dave")上面的代碼並無運行任何的數據庫查詢。 你可使用person_set,給它加上一些過濾條件,或者將它傳給某個函數,這些操做都不 會發送給數據庫。這是對的,由於數據庫查詢是顯著影響web應用性能的因素之一。 <2>要真正從數據庫得到數據,你能夠遍歷queryset或者使用if queryset,總之你用到 數據時就會執行sql.爲了驗證這些,須要在settings里加入 LOGGING(驗證方式) obj=models.Book.objects.filter(id=3) # for i in obj: # print(i) # if obj: # print("ok") <3>queryset是具備cache的 當你遍歷queryset時,全部匹配的記錄會從數據庫獲取,而後轉換成Django的model。 這被稱爲執行(evaluation).這些model會保存在queryset內置的cache中,這樣若是你 再次遍歷這個queryset, 你不須要重複運行通用的查詢。 obj=models.Book.objects.filter(id=3) # for i in obj: # print(i) ## models.Book.objects.filter(id=3).update(title="GO") ## obj_new=models.Book.objects.filter(id=3) # for i in obj: # print(i) #LOGGING只會打印一次 <4> 簡單的使用if語句進行判斷也會徹底執行整個queryset而且把數據放入cache,雖然你並 不須要這些數據!爲了不這個,能夠用exists()方法來檢查是否有數據: obj = Book.objects.filter(id=4) # exists()的檢查能夠避免數據放入queryset的cache。 if obj.exists(): print("hello world!") <5>當queryset很是巨大時,cache會成爲問題 處理成千上萬的記錄時,將它們一次裝入內存是很浪費的。更糟糕的是,巨大的queryset 可能會鎖住系統 進程,讓你的程序瀕臨崩潰。要避免在遍歷數據的同時產生queryset cache, 可使用iterator()方法 來獲取數據,處理完數據就將其丟棄。 objs = Book.objects.all().iterator() # iterator()能夠一次只從數據庫獲取少許數據,這樣能夠節省內存 for obj in objs: print(obj.name) #BUT,再次遍歷沒有打印,由於迭代器已經在上一次遍歷(next)到最後一次了,沒得遍歷了 for obj in objs: print(obj.name) #固然,使用iterator()方法來防止生成cache,意味着遍歷同一個queryset時會重複執行 查詢。因此使用iterator()的時候要小心,確保你的代碼在操做一個大的queryset時沒有重複執行查詢 總結: queryset的cache是用於減小程序對數據庫的查詢,在一般的使用下會保證只有在須要的時候 纔會查詢數據庫。使用exists()和iterator()方法能夠優化程序對內存的使用。不過,因爲它們並 不會生成queryset cache,可能會形成額外的數據庫查詢。
如下的方法不會返回QuerySets,可是做用很是強大,尤爲是粗體顯示的方法,須要背下來。
get(**kwargs)
返回按照查詢參數匹配到的單個對象,參數的格式應該符合Field lookups的要求。
若是匹配到的對象個數不止一個的話,觸發MultipleObjectsReturned異常
若是根據給出的參數匹配不到對象的話,觸發DoesNotExist異常。例如:
Entry.objects.get(id='foo') # raises Entry.DoesNotExist
DoesNotExist異常從django.core.exceptions.ObjectDoesNotExist 繼承,能夠定位多個DoesNotExist異常,例如:
from django.core.exceptions import ObjectDoesNotExist try: e = Entry.objects.get(id=3) b = Blog.objects.get(id=1) except ObjectDoesNotExist: print("Either the entry or blog doesn't exist.")
若是但願查詢器只返回一行,則可使用get() 而不使用任何參數來返回該行的對象:
entry = Entry.objects.filter(...).exclude(...).get()
create(**kwargs)
在一步操做中同時建立而且保存對象的便捷方法:
p = Person.objects.create(first_name="Bruce", last_name="Springsteen")
等於:
p = Person(first_name="Bruce", last_name="Springsteen") p.save(force_insert=True)
參數force_insert表示強制建立對象。若是model中有一個你手動設置的主鍵,而且這個值已經存在了數據庫中,調用create()將會失敗,而且觸發IntegrityError由於主鍵必須是惟一的。若是你手動設置了主鍵,作好異常處理的準備。
get_or_create(defaults=None, **kwargs)
經過kwargs來查詢對象的便捷方法(若是模型中的全部字段都有默認值,能夠爲空),若是該對象不存在則建立一個新對象。
該方法返回一個由(object,created)組成的元組,元組中的object是一個查詢到的或者是被建立的對象,created是一個表示是否建立了新的對象的布爾值。
對於下面的代碼:
try: obj = Person.objects.get(first_name='John', last_name='Lennon') except Person.DoesNotExist: obj = Person(first_name='John', last_name='Lennon', birthday=date(1940, 10, 9)) obj.save()
若是模型的字段數量較大的話,這種模式就變的很是不易使用了。上面的示例能夠用get_or_create()重寫:
obj, created = Person.objects.get_or_create( first_name='John', last_name='Lennon', defaults={'birthday': date(1940, 10, 9)}, )
任何傳遞給get_or_create()的關鍵字參數,除了一個可選的defaults,都將傳遞給get()調用,若是查到一個對象,返回一個包含匹配到的對象以及False組成的元組。若是查到的對象超過一個以上,將引起MultipleObjectsReturned。若是查找不到對象,get_or_create() 將會實例化並保存一個新的對象,返回一個由新的對象以及True組成的元組。新的對象將會按照如下的邏輯建立:
params = {k: v for k, v in kwargs.items() if '__' not in k} params.update({k: v() if callable(v) else v for k, v in defaults.items()}) obj = self.model(**params) obj.save()
它表示從非'defaults' 且不包含雙下劃線的關鍵字參數開始。而後將defaults的內容添加進來,覆蓋必要的鍵,並使用結果做爲關鍵字參數傳遞給模型類。
若是有一個名爲defaults_exact 的字段,而且想在 get_or_create() 時用它做爲精確查詢,只須要使用defaults,像這樣:
Foo.objects.get_or_create(defaults__exact='bar', defaults={'defaults': 'baz'})
當你使用手動指定的主鍵時,get_or_create()方法與create()方法有類似的錯誤行爲。若是須要建立一個對象而該對象的主鍵早已存在於數據庫中,IntergrityError異常將會被觸發。
這個方法假設進行的是原子操做,而且正確地配置了數據庫和正確的底層數據庫行爲。若是數據庫級別沒有對get_or_create
中用到的kwargs強制要求惟一性(unique和unique_together),方法容易致使競態條件,可能會有相同參數的多行同時插入。(簡單理解,kwargs必須指定的是主鍵或者unique屬性的字段才安全。)
最後建議只在Django視圖的POST請求中使用get_or_create(),由於這是一個具備修改性質的動做,不該該使用在GET請求中,那樣不安全。
能夠經過ManyToManyField屬性和反向關聯使用get_or_create()
。在這種狀況下,應該限制查詢在關聯的上下文內部。 不然,可能致使完整性問題。
例以下面的模型:
class Chapter(models.Model): title = models.CharField(max_length=255, unique=True) class Book(models.Model): title = models.CharField(max_length=256) chapters = models.ManyToManyField(Chapter)
能夠經過Book的chapters字段使用get_or_create(),可是它只會獲取該Book內部的上下文:
>>> book = Book.objects.create(title="Ulysses") >>> book.chapters.get_or_create(title="Telemachus") (<Chapter: Telemachus>, True) >>> book.chapters.get_or_create(title="Telemachus") (<Chapter: Telemachus>, False) >>> Chapter.objects.create(title="Chapter 1") <Chapter: Chapter 1> >>> book.chapters.get_or_create(title="Chapter 1") # Raises IntegrityError
發生這個錯誤是由於嘗試經過Book 「Ulysses」獲取或者建立「Chapter 1」,可是它不能,由於它與這個book不關聯,但由於title 字段是惟一的它仍然不能建立。
在Django1.11在defaults中增長了對可調用值的支持。
update_or_create(defaults=None, **kwargs)
相似於上面的 get_or_create()
經過給出的kwargs來更新對象的便捷方法, 若是沒找到對象,則建立一個新的對象。defaults是一個由 (field, value)對組成的字典,用於更新對象。defaults中的值能夠是可調用對象(也就是說函數等)。
該方法返回一個由(object, created)組成的元組,元組中的object是一個建立的或者是被更新的對象, created是一個標示是否建立了新的對象的布爾值。
update_or_create
方法嘗試經過給出的kwargs 去從數據庫中獲取匹配的對象。 若是找到匹配的對象,它將會依據defaults 字典給出的值更新字段。
像下面的代碼:
defaults = {'first_name': 'Bob'} try: obj = Person.objects.get(first_name='John', last_name='Lennon') for key, value in defaults.items(): setattr(obj, key, value) obj.save() except Person.DoesNotExist: new_values = {'first_name': 'John', 'last_name': 'Lennon'} new_values.update(defaults) obj = Person(**new_values) obj.save()
若是模型的字段數量較大的話,這種模式就變的很是不易用了。上面的示例能夠用update_or_create()重寫:
obj, created = Person.objects.update_or_create( first_name='John', last_name='Lennon', defaults={'first_name': 'Bob'}, )
和get_or_create()
同樣,這個方法也容易致使競態條件,若是數據庫層級沒有前置惟一性會讓多行同時插入。
在Django1.11在defaults中增長了對可調用值的支持。
bulk_create(objs , batch_size = None)
以高效的方式(一般只有一個查詢,不管有多少對象)將提供的對象列表插入到數據庫中:
>>> Entry.objects.bulk_create([ ... Entry(headline='This is a test'), ... Entry(headline='This is only a test'), ... ])
注意事項:
pre_save
和post_save
信號。batch_size參數控制在單個查詢中建立的對象數。
count()
返回在數據庫中對應的QuerySet對象的個數,count()永遠不會引起異常。
例如:
# 返回總個數. Entry.objects.count() # 返回包含有'Lennon'的對象的總數 Entry.objects.filter(headline__contains='Lennon').count()
in_bulk(id_list = None)
獲取主鍵值的列表,並返回將每一個主鍵值映射到具備給定ID的對象的實例的字典。 若是未提供列表,則會返回查詢集中的全部對象。
例如:
>>> Blog.objects.in_bulk([1]) {1: <Blog: Beatles Blog>} >>> Blog.objects.in_bulk([1, 2]) {1: <Blog: Beatles Blog>, 2: <Blog: Cheddar Talk>} >>> Blog.objects.in_bulk([]) {} >>> Blog.objects.in_bulk() {1: <Blog: Beatles Blog>, 2: <Blog: Cheddar Talk>, 3: <Blog: Django Weblog>}
若是向in_bulk()傳遞一個空列表,會獲得一個空的字典。
在舊版本中,id_list是必須的參數,如今是一個可選參數。
iterator()
提交數據庫操做,獲取QuerySet,並返回一個迭代器。
Q uerySet一般會在內部緩存其結果,以便在重複計算時不會致使額外的查詢。而iterator()將直接讀取結果,不在QuerySet級別執行任何緩存。對於返回大量只須要訪問一次的對象的QuerySet,這能夠帶來更好的性能,顯著減小內存使用。
請注意,在已經提交了的iterator()上使用QuerySet會強制它再次提交數據庫操做,進行重複查詢。此外,使用iterator()會致使先前的prefetch_related()
調用被忽略,由於這兩個一塊兒優化沒有意義。
latest(field_name=None)
使用日期字段field_name,按日期返回最新對象。
下例根據Entry的'pub_date'字段返回最新發布的entry:
Entry.objects.latest('pub_date')
若是模型的Meta指定了get_latest_by
,則能夠將latest()參數留給earliest()或者field_name
。 默認狀況下,Django將使用get_latest_by
中指定的字段。
earliest()和latest()可能會返回空日期的實例,可能須要過濾掉空值:
Entry.objects.filter(pub_date__isnull=False).latest('pub_date')
earliest(field_name = None)
類同latest()
first()
返回結果集的第一個對象,當沒有找到時候,返回None,若是QuerySet沒有設置排序,則將自動按主鍵進行排序。例如:
p = Article.objects.order_by('title', 'pub_date').first()
first()是一個簡便方法,下面的例子和上面的代碼效果是同樣:
try: p = Article.objects.order_by('title', 'pub_date')[0] except IndexError: p = None
last()
工做方式相似於first() ,只是返回的是查詢集中最後一個對象。
aggregate(*args, **kwargs)
返回彙總值的字典(平均值,總和等),經過QuerySet進行計算。每一個參數指定返回的字典中將要包含的值。
使用關鍵字參數指定的聚合將使用關鍵字參數的名稱做爲Annotation 的名稱。 匿名參數的名稱將基於聚合函數的名稱和模型字段生成。 複雜的聚合不可使用匿名參數,必須指定一個關鍵字參數做爲別名。
例如,想知道Blog Entry 的數目:
>>> from django.db.models import Count >>> q = Blog.objects.aggregate(Count('entry')) {'entry__count': 16}
經過使用關鍵字參數來指定聚合函數,能夠控制返回的聚合的值的名稱:
>>> q = Blog.objects.aggregate(number_of_entries=Count('entry')) {'number_of_entries': 16}
exists()
若是QuerySet包含任何結果,則返回True,不然返回False。
查找具備惟一性字段(例如primary_key)的模型是否在一個QuerySet中的最高效的方法是:
entry = Entry.objects.get(pk=123) if some_queryset.filter(pk=entry.pk).exists(): print("Entry contained in queryset")
它將比下面的方法快不少,這個方法要求對QuerySet求值並迭代整個QuerySet:
if entry in some_queryset: print("Entry contained in QuerySet")
若要查找一個QuerySet是否包含任何元素:
if some_queryset.exists(): print("There is at least one object in some_queryset")
將快於:
if some_queryset: print("There is at least one object in some_queryset")
update(**kwargs)
對指定的字段執行批量更新操做,並返回匹配的行數(若是某些行已具備新值,則可能不等於已更新的行數)。
例如,要對2010年發佈的全部博客條目啓用評論,能夠執行如下操做:
>>> Entry.objects.filter(pub_date__year=2010).update(comments_on=False)
能夠同時更新多個字段 (沒有多少字段的限制)。 例如同時更新comments_on和headline字段:
>> Entry.objects.filter(pub_date__year=2010).update(comments_on=False, headline='This is old')
update()方法無需save操做。惟一限制是它只能更新模型主表中的列,而不是關聯的模型,例如不能這樣作:
>>> Entry.objects.update(blog__name='foo') # Won't work!
仍然能夠根據相關字段進行過濾:
>>> Entry.objects.filter(blog__id=1).update(comments_on=True)
update()方法返回受影響的行數:
>>> Entry.objects.filter(id=64).update(comments_on=True) 1 >>> Entry.objects.filter(slug='nonexistent-slug').update(comments_on=True) 0 >>> Entry.objects.filter(pub_date__year=2010).update(comments_on=False) 132
若是你只是更新一下對象,不須要爲對象作別的事情,最有效的方法是調用update(),而不是將模型對象加載到內存中。 例如,不要這樣作:
e = Entry.objects.get(id=10) e.comments_on = False e.save()
建議以下操做:
Entry.objects.filter(id=10).update(comments_on=False)
用update()還能夠防止在加載對象和調用save()之間的短期內數據庫中某些內容可能發生更改的競爭條件。
若是想更新一個具備自定義save()方法的模型的記錄,請循環遍歷它們並調用save(),以下所示:
for e in Entry.objects.filter(pub_date__year=2010): e.comments_on = False e.save()
delete()
批量刪除QuerySet中的全部對象,並返回刪除的對象個數和每一個對象類型的刪除次數的字典。
elete()動做是當即執行的。
不能在QuerySet上調用delete()。
例如,要刪除特定博客中的全部條目:
>>> b = Blog.objects.get(pk=1) # Delete all the entries belonging to this Blog. >>> Entry.objects.filter(blog=b).delete() (4, {'weblog.Entry': 2, 'weblog.Entry_authors': 2})
默認狀況下,Django的ForeignKey使用SQL約束ON DELETE CASCADE,任何具備指向要刪除的對象的外鍵的對象將與它們一塊兒被刪除。 像這樣:
>>> blogs = Blog.objects.all() # This will delete all Blogs and all of their Entry objects. >>> blogs.delete() (5, {'weblog.Blog': 1, 'weblog.Entry': 2, 'weblog.Entry_authors': 2})
這種級聯的行爲能夠經過的ForeignKey的on_delete參數自定義。(何時要改變這種行爲呢?好比日誌數據,就不能和它關聯的主體一併被刪除!)
delete()會爲全部已刪除的對象(包括級聯刪除)發出pre_delete
和post_delete
信號。
classmethod as_manager()
一個類方法,返回Manager的實例與QuerySet的方法的副本。
詳情參考django官方文檔:https://django-chinese-docs-14.readthedocs.io/en/latest/ref/models/options.html
https://www.cnblogs.com/yuanchenqi/articles/8876856.html
https://www.cnblogs.com/feixuelove1009/p/8425054.html
(這裏主要是作了本身的學習筆記,用來記錄於此)