檢查版本css
python -m django --version
建立第一個項目html
django-admin startproject mysite
運行前端
python manage.py runserver
更改端口python
python manage.py runserver 8080
更改IPmysql
python manage.py runserver 0:8000
建立投票應用jquery
python manage.py startapp polls
polls/views.pygit
from django.shortcuts import render from django.http import HttpResponse def index(request): return HttpResponse("Hello, world. You're at the polls index.")
在polls裏面建立一個urls.py文件,代碼以下程序員
from django.conf.urls import url from . import views urlpatterns = [ url(r'^$',views.index,name='index'), ]
在mysite/urls.py中添加includegithub
from django.conf.urls import url,include from django.contrib import admin urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'polls/',include('polls.urls')), ]
訪問:http://127.0.0.1:8000/polls/,就能夠看到信息「Hello, world. You're at the polls index.」web
設置爲Mysql數據庫
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'django', #數據庫名字 'USER': 'root', #帳號 'PASSWORD': '123456', #密碼 'HOST': '127.0.0.1', #IP 'PORT': '3306', #端口 } }
polls/init.py
import pymysql pymysql.install_as_MySQLdb()
polls/models.py
from django.db import models class Question(models.Model): question_text = models.CharField(max_length=200) pub_date = models.DateTimeField('date published') def __str__(self): return self.question_text class Choice(models.Model): question = models.ForeignKey(Question, on_delete=models.CASCADE) choice_text = models.CharField(max_length=200) votes = models.IntegerField(default=0) def __str__(self): return self.choice_text
而後執行下面的命令
python manage.py makemigrations 建立遷移文件
python manage.py migrate 更新到數據庫中。
(1)建立管理員用戶,設置用戶名,郵箱,密碼
python manage.py createsuperuser
登陸:http://127.0.0.1:8000/admin/
(2)把polls應用添加到後臺管理站點
編輯polls/admin.py
from django.contrib import admin from polls.models import Question admin.site.register(Question)
刷新就能夠看到polls
(1)polls/urls.py
from django.conf.urls import url from . import views urlpatterns = [ # ex: /polls/ url(r'^$', views.index, name='index'), # ex: /polls/5/ url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'), # ex: /polls/5/results/ url(r'^(?P<question_id>[0-9]+)/results/$', views.results, name='results'), # ex: /polls/5/vote/ url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'), ]
(2)建立polls/templates/polls/index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {% if latest_question_list %} <ul> {% for question in latest_question_list %} <li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li> {% endfor %} </ul> {% else %} <p>No polls are available.</p> {% endif %} </body> </html>
(3)polls/views.py
from django.shortcuts import render,HttpResponse from polls.models import Question def index(request): latest_question_list = Question.objects.order_by('-pub_date')[:5] context = {'latest_question_list': latest_question_list,} return render(request,'polls/index.html',context) def detail(request, question_id): return HttpResponse("You're looking at question %s." % question_id) def results(request, question_id): response = "You're looking at the results of question %s." return HttpResponse(response % question_id) def vote(request, question_id): return HttpResponse("You're voting on question %s." % question_id)
(4)拋出404異常
from django.shortcuts import render,HttpResponse,Http404 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})
detail.html
{{ question }}
(5)快捷方式 get_object_or_404
更改detail()視圖以下
from django.shortcuts import get_object_or_404
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()
函數。 若是對象不存在,它就引起一個 Http404
異常。
還有一個get_list_or_404()
函數,它的工做方式相似get_object_or_404()
—— 差異在於它使用filter()
而不是get()
。 若是列表爲空則引起Http404
。
(6)使用模板系統
編輯detail.html
<h1>{{ question.question_text }}</h1> <ul> {% for choice in question.choice_set.all %} <li>{{ choice.choice_text }}</li> {% endfor %} </ul>
方法調用發生在{% for %}
循環中:Choice
被解釋爲Python的代碼question.choice_set.all
,它返回一個由question.choice_set.all()
對象組成的可迭代對象,並將其用於{% for %}
標籤。
最終:
http://127.0.0.1:8000/polls/ 訪問index頁面,顯示question列表
點任意一個擊question,而後跳轉到detail頁面
(7)移除模板中硬編碼的URL
前面在index.html中跳轉到detail頁面的編碼
<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
這種硬編碼、緊耦合的方法有一個問題,就是若是咱們想在擁有許多模板文件的項目中修改URLs,那將會變得頗有挑戰性。 然而,由於你在polls.urls
模塊的url()
函數中定義了name 參數,你能夠經過使用{% url %}
模板標籤來移除對你的URL配置中定義的特定的URL的依賴:修改以下
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
若是你想把polls應用中detail視圖的URL改爲其它樣子好比polls/specifics/12/
,就能夠沒必要在該模板(或者多個模板)中修改它,只須要修改polls/urls.py
:
url(r'^specifics/(?P<question_id>[0-9]+)/$', views.detail, name='detail'),
(8)命名空間URL名稱
在真實的Django項目中,可能會有五個、十個、二十個或者更多的應用。 Django如何區分它們URL的名字呢?
答案是添加命名空間到你的URLconf。 在polls/urls.py
文件中,繼續添加app_name
來設置應用程序命名空間:
添加 app_name='polls'
from django.conf.urls import url from . import views app_name = 'polls' urlpatterns = [ url(r'^$', views.index, name='index'), # ex: /polls/5/ url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'), # ex: /polls/5/results/ url(r'^(?P<question_id>[0-9]+)/results/$', views.results, name='results'), # ex: /polls/5/vote/ url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'), ]
而後修改polls/index.html
由
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
改成
<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>
(1)更新一下polls/details.html
<h1>{{ question.question_text }}</h1> {% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %} <form action="{% url 'polls:vote' question.id %}" method="post"> {% csrf_token %} {% for choice in question.choice_set.all %} <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}" /> <label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label><br /> {% endfor %} <input type="submit" value="Vote" /> </form>
簡要說明:
在detail網頁模板中,咱們爲Question對應的每一個Choice都添加了一個單選按鈕用於選擇。每一個單選按鈕的value
屬性是對應的各個Choice的ID。 每一個單選按鈕的name
是"choice"
。 這意味着,當有人選擇一個單選按鈕並提交表單提交時,它將發送一個POST數據choice=#
,其中# 爲選擇的Choice的ID
因爲咱們建立一個POST表單(它具備修改數據的做用),因此咱們須要當心跨站點請求僞造。簡而言之,全部針對內部URL的POST表單都應該使用{% csrf_token %}
模板標籤。
(2)vote.py處理投票
如今,咱們來建立一個處理提交的數據的Django視圖,並用它來處理
from django.shortcuts import get_object_or_404, render from django.http import HttpResponseRedirect, HttpResponse from django.urls import reverse from .models import Choice, Question # ... def vote(request, question_id): question = get_object_or_404(Question, pk=question_id) try: selected_choice = question.choice_set.get(pk=request.POST['choice']) except (KeyError, Choice.DoesNotExist): # 從新顯示該問題的表單 return render(request, 'polls/detail.html', { 'question': question, 'error_message': "You didn't select a choice.", }) else: selected_choice.votes += 1 selected_choice.save() # 始終在成功處理 POST 數據後返回一個 HttpResponseRedirect , # (合併上句) 這樣能夠防止用戶點擊「後退」按鈕時數據被髮送兩次。 # (合併至上一句) return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))
這個例子中,request.POST['choice']
以字符串形式返回選擇的Choice的ID。 request.POST
的值永遠是字符串。
在增長Choice的得票數以後,代碼返回一個 HttpResponseRedirect
而不是經常使用的HttpResponse
。 HttpResponseRedirect
只接收一個參數:用戶將要被重定向的URL
咱們在HttpResponseRedirect
的構造函數中使用reverse()
函數。 這個函數避免了咱們在視圖函數中硬編碼URL。 它須要咱們給出咱們想要跳轉的視圖的名字和該視圖所對應的URL模式中須要給該視圖提供的參數。重定向的URL將調用'results'
視圖來顯示最終的頁面。
(3)results()
當有人對Question進行投票後,vote()
視圖將請求重定向到Question的結果界面。
def results(request, question_id): question = get_object_or_404(Question, pk=question_id) return render(request, 'polls/results.html', {'question': question})
(4)results.html
<h1>{{ question.question_text }}</h1> <ul> {% for choice in question.choice_set.all %} <li>{{ choice.choice_text }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li> #pluralize複數 {% endfor %} </ul> <a href="{% url 'polls:detail' question.id %}">Vote again?</a>
如今,在你的瀏覽器中訪問/polls/1/
而後爲Question投票。 你應該看到一個投票結果頁面,而且在你每次投票以後都會更新。 若是你提交時沒有選擇任何Choice,你應該看到錯誤信息。
(1)建立style.css
-->>目錄 polls/static/polls/style.css
添加下面代碼到style.css
li a {
color: green;
}
在index.html添加以下
{% load static %} <link rel="stylesheet" type="text/css" href="{% static 'polls/style.css' %}" />
{% static %}
模板標籤生成靜態文件的絕對URL。
再訪問index界面,會發現question連接是綠色的
(2)添加背景圖片
在polls/static/polls/
目錄中建立一個 images
子目錄。在這個目錄中,放入一張圖片background.gif-->>(polls/static/polls/images/background.gif)
添加樣式
body {
background: white url("images/background.gif") no-repeat right bottom;
}
(1)從新排序編輯表單上的字段
from django.contrib import admin from polls.models import Question class QuestionAdmin(admin.ModelAdmin): fields = ['pub_date','question_text'] admin.site.register(Question,QuestionAdmin)
任什麼時候候你須要更改模型的管理選項,你將遵循此模式 — 建立一個模型管理類,而後將其做爲第二個參數傳遞給admin.site.register()
(2)添加關聯對象
在建立Question
對象的同時能夠直接添加一組Choice
from django.contrib import admin from .models import Choice, Question class ChoiceInline(admin.StackedInline): model = Choice extra = 3 class QuestionAdmin(admin.ModelAdmin): fieldsets = [ (None, {'fields': ['question_text']}), ('Date information', {'fields': ['pub_date'], 'classes': ['collapse']}), ] inlines = [ChoiceInline] admin.site.register(Question, QuestionAdmin)
這告訴Django:「Choice
對象在Question
的管理界面中編輯。 默認提供足夠3個Choice的空間。
打開「Add question」頁面:
它這樣工做:有三個所關聯的Choice —— 由extra
指定 —— 每次你回到已經存在對象的"Change"頁面時,都會額外地得到三個空白Choice。
在現有的三個Choice的底部,你會發現一個「Add another Choice」的連接。 若是你點擊它,就會增長一個新的空白Choice。
還有個小問題。 顯示全部關聯的Choice
對象的字段佔用大量的屏幕空間。 爲此,Django提供了一種顯示內聯相關對象的表格方式;你只需將ChoiceInline
聲明更改成:
class ChoiceInline(admin.TabularInline): model = Choice extra = 3
使用 TabularInline
(而不是StackedInline
),這些相關聯的對象顯示成緊湊的、基於表格的形式:
(3)自定義管理變動清單
class QuestionAdmin(admin.ModelAdmin): list_display = ('question_text','pub_date')
(4)使用list_filter
來添加過濾器
class QuestionAdmin(admin.ModelAdmin): list_display = ('question_text','pub_date') list_filter = ['pub_date']
(5)添加搜索功能
class QuestionAdmin(admin.ModelAdmin): list_display = ('question_text','pub_date') list_filter = ['pub_date'] search_fields = ['question_text']
很明顯,每一個管理頁面的頂部都有「Django administration」不太合適。它能夠用Django的模板系統輕鬆改變。 Django的管理站點是用Django本身製做出來的,它的界面代碼使用的是Django本身的模板系統。
定製項目的模板
在你項目的文件夾內(包含 manage.py
的目錄)建立一個templates
目錄。 打開你的配置文件(記住是mysite/settings.py
)在TEMPLATES
設置中添加一個DIRS
選項:
TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates')], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ]
如今,在templates
下建立一個名爲admin
的文件夾,而後從Django安裝的原目錄下(目錄爲django/contrib/admin/templates
)將模板頁面的源文件admin/base_site.html
拷貝到這個文件夾裏。
C:\Users\Administrator\AppData\Local\Programs\Python\Python36\Lib\site-packages\django\contrib\admin\templates\admin
若是不知道Django源文件路徑,運行以下命令
$ python -c "import django; print(django.__path__)"
而後,只需編輯該文件並替換{{ site_header|default:_('Django administration') }}
(包括花括號)爲你認爲合適的本身站點的名稱。 編輯完成後應該相似下面的代碼片斷:
{% extends "admin/base.html" %} {% block title %}{{ title }} | {{ site_title|default:_('Django site admin') }}{% endblock %} {% block branding %} <h1 id="site-name"><a href="{% url 'admin:index' %}">{{ site_header|default:_('Django administration') }}</a></h1> {% endblock %} {% block nav-global %}{% endblock %}
{% extends "admin/base.html" %} {% block title %}{{ title }} | Polls Administration{% endblock %} {% block branding %} <h1 id="site-name"><a href="{% url 'admin:index' %}">Polls Administration</a></h1> {% endblock %} {% block nav-global %}{% endblock %}
改變以後效果:
若是爲True
,Django將在數據庫中把空值存儲爲NULL
。 默認爲False
。
若是爲
True
,該字段容許爲空值, 默認爲False
。
要注意,這與 null
不一樣。 null
純粹是數據庫範疇,指數據庫中字段內容是否容許爲空,而 blank
是表單數據輸入驗證範疇的。 若是一個字段的blank=True
,表單的驗證將允 許輸入一個空值。 若是字段的blank=False
,該字段就是必填的。
由二項元組構成的一個可迭代對象(例如,列表或元組),用來給字段提供選擇項。 若是設置了choices ,默認的表單將是一個選擇框而不是標準的文本框,並且這個選擇框的選項就是choices 中的選項。
這是一個關於 choices 列表的例子:
每一個元組中的第一個元素是將被存儲在數據庫中的值。 第二個元素將由默認窗體小部件或ModelChoiceField
顯示。 給定一個模型實例,能夠使用get_FOO_display()
方法來訪問選項字段的顯示值。 例如:
from django.db import models class Person(models.Model): SHIRT_SIZES = ( ('S', 'Small'), ('M', 'Medium'), ('L', 'Large'), ) name = models.CharField(max_length=60) shirt_size = models.CharField(max_length=1, choices=SHIRT_SIZES)
>>> p = Person(name="Fred Flintstone", shirt_size="L") >>> p.save() >>> p.shirt_size 'L' >>> p.get_shirt_size_display() 'Large'
字段的默認值。 能夠是一個值或者可調用對象。 若是可調用 ,每一個新對象建立時它都會被調用。
表單部件額外顯示的幫助內容。 即便字段不在表單中使用,它對生成文檔也頗有用。
若是爲True
,那麼這個字段就是模型的主鍵。
若是爲True
, 則這個字段在整張表中必須是惟一的。
除ForeignKey
、ManyToManyField
和 OneToOneField
以外,每一個字段類型都接受一個可選的位置參數(在第一的位置) — 字段的自述名。 若是沒有給定自述名,Django 將根據字段的屬性名稱自動建立自述名 —— 將屬性名稱的下劃線替換成空格。
在這個例子中,自述名是 "person's first name":
first_name = models.CharField("person's first name", max_length=30)
在這個例子中,自述名是 "first name"
:
first_name = models.CharField(max_length=30)
ForeignKey
、ManyToManyField
和 OneToOneField
都要求第一個參數是一個模型類,因此要使用 verbose_name
關鍵字參數才能指定自述名:
poll = models.ForeignKey( Poll, on_delete=models.CASCADE, verbose_name="the related poll", ) sites = models.ManyToManyField(Site, verbose_name="list of sites") place = models.OneToOneField( Place, on_delete=models.CASCADE, verbose_name="related place", )
習慣上,verbose_name
的首字母不用大寫。 Django 在必要的時候會自動大寫首字母。
模型繼承
在Django 中有3種風格的繼承。
當你想將一些共有信息放進其它一些model的時候,抽象化類是十分有用的。 你編寫完基類以後,在 Meta類中設置 abstract=True
, 這個模型就不會被用來建立任何數據表。 取而代之的是,當它被用來做爲一個其餘model的基類時,它的字段將被加入那些子類中。 若是抽象基類和它的子類有相同的字段名,那麼將會出現error(而且Django將拋出一個exception)。
一個例子:
from django.db import models class ConmonInfo(models.Model): name = models.CharField(max_length=100) age = models.PositiveIntegerField() class Meta: abstract = True class Student(ConmonInfo): home_group = models.CharField(max_length=5)
Student
模型將有三個字段:name
、age
和 home_group
。 CommonInfo
模型沒法像通常的Django模型同樣使用,由於它是一個抽象基類。 它沒法生成一張數據表或者擁有一個管理器,而且不能實例化或者直接儲存。
當一個抽象基類被建立的時候, Django把你在基類內部定義的 Meta 類做爲一個屬性使其可用。 若是子類沒有聲明本身的Meta類, 它將會繼承父類的Meta。 若是子類想要擴展父類的Meta類,它能夠子類化它。 例如:
from django.db import models class ConmonInfo(models.Model): name = models.CharField(max_length=100) age = models.PositiveIntegerField() class Meta: abstract = True ordering = ['name'] class Student(ConmonInfo): home_group = models.CharField(max_length=5) class Meta(ConmonInfo.Meta): db_table = 'student_info' #更改表名
這是 Django 支持的第二種繼承方式。使用這種繼承方式時,每個層級下的每一個 model 都是一個真正意義上完整的 model 。 每一個 model 都有專屬的數據表,均可以查詢和建立數據表。 繼承關係在子 model 和它的每一個父類之間都添加一個連接 (經過一個自動建立的 OneToOneField
來實現)。 例如:
from django.db import models class Place(models.Model): name = models.CharField(max_length=50) address = models.CharField(max_length=80) class Restaurant(Place): serves_hot_dogs = models.BooleanField(default=False) serves_pizza = models.BooleanField(default=False)
Place
裏面的全部字段在 Restaurant中也是有效的,只不過沒有保存在數據庫中的Restaurant
表中。 因此下面兩個語句都是能夠運行的:
>>> Place.objects.filter(name="Bob's Cafe") >>> Restaurant.objects.filter(name="Bob's Cafe")
使用 多表繼承時,model 的每一個子類都會建立一張新數據表, 一般狀況下,這正是咱們想要的操做。這是由於子類須要一個空間來存儲不包含在基類中的字段數據。 但有時,你可能只想更改 model 在 Python 層的行爲實現。好比:更改默認的 manager ,或是添加一個新方法。
而這,正是代理繼承要作的:爲原始模型建立一個代理 。 你能夠建立,刪除,更新代理 model 的實例,並且全部的數據均可以像使用原始 model 同樣被保存。 不一樣之處在於:你能夠在代理 model 中改變默認的排序設置和默認的 manager ,更不會對原始 model 產生影響。
聲明代理 model 和聲明普通 model 沒有什麼不一樣。 設置Meta
類中 proxy
的值爲 True
,就完成了對代理 model 的聲明。
舉個例子,假設你想給 Person
模型添加一個方法。 你能夠這樣作:
from django.db import models class Person(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=30) class MyPerson(Person): class Meta: proxy = True def do_something(self): # ... pass
Person
類和它的父類 Person
操做同一個數據表。 特別的是,Person
的任何實例也能夠經過 MyPerson
訪問,反之亦然:
>>> p = Person.objects.create(first_name="foobar") >>> MyPerson.objects.get(first_name="foobar")
一個博客應用
from django.db import models class Blog(models.Model): name = models.CharField(max_length=100) tagline = models.TextField() def __str__(self): # __unicode__ on Python 2 return self.name class Author(models.Model): name = models.CharField(max_length=200) email = models.EmailField() def __str__(self): # __unicode__ on Python 2 return self.name class Entry(models.Model): blog = models.ForeignKey(Blog) headline = models.CharField(max_length=255) body_text = models.TextField() pub_date = models.DateField() mod_date = models.DateField() authors = models.ManyToManyField(Author) n_comments = models.IntegerField() n_pingbacks = models.IntegerField() rating = models.IntegerField() def __str__(self): # __unicode__ on Python 2 return self.headline
(1)使用過濾器檢索特定對象
all()
方法返回了一個包含數據庫表中全部記錄QuerySet
。 但在一般狀況下,你每每想要獲取的是完整數據集的一個子集。
要建立這樣一個子集,你須要在原始的的QuerySet
上增長一些過濾條件。 QuerySet
兩個最廣泛的途徑是:
每次你篩選一個QuerySet
,獲得的都是全新的另外一個QuerySet
,它和以前的QuerySet
之間沒有任何綁定關係。 每次篩選都會建立一個獨立的QuerySet
,它能夠被存儲及反覆使用。
QuerySets
是惰性執行的 —— 建立QuerySet
不會帶來任何數據庫的訪問。 你能夠將過濾器保持一成天,直到QuerySet
須要求值時,Django 纔會真正運行這個查詢。
(2)使用get()檢索單個對象
filter()
始終給你一個QuerySet
,即便只有一個對象知足查詢條件 —— 這種狀況下,QuerySet
將只包含一個元素。
若是你知道只有一個對象知足你的查詢,你能夠使用Manager
的get()
方法,它直接返回該對象:
>>> one_entry = Entry.objects.get(pk=1)
能夠對get()
使用任何查詢表達式,和filter()
同樣 —— 一樣請查看下文的字段查詢。
注意,使用get()
和使用filter()
的切片[0]
有一點區別。 若是沒有結果知足查詢,get()
將引起一個DoesNotExist
異常。 這個異常是正在查詢的模型類的一個屬性 —— 因此在上面的代碼中,若是沒有主鍵(pk) 爲1 的Entry
對象,Django 將引起一個Entry.DoesNotExist
。
相似地,若是有多條記錄知足get()
的查詢條件,Django 也將報錯。 這種狀況將引起MultipleObjectsReturned
,它一樣是模型類自身的一個屬性。
(3)限制QuerySet
能夠使用Python 的切片語法來限制QuerySet
記錄的數目 。 它等同於SQL 的OFFSET
和LIMIT
子句。
例如,下面的語句返回前面5 個對象(LIMIT 5
):
>>> Entry.objects.all()[:5]
下面這條語句返回第6 至第10 個對象(OFFSET 5 LIMIT 5
):
>>> Entry.objects.all()[5:10]
不支持負的索引(例如Entry.objects.all()[-1]
)。
(3)字段查找
>>> Entry.objects.filter(pub_date__lte='2006-01-01') # <=
查詢條件中指定的字段必須是模型字段的名稱。 但有一個例外,對於ForeignKey
你能夠使用字段名加上_id
後綴。 在這種狀況下,該參數的值應該是外鍵的原始值。 像這樣:
>>> Entry.objects.filter(blog_id=4)
iexact
大小寫不敏感的匹配。 因此,查詢:
>>> Blog.objects.get(name__iexact="beatles blog")
將匹配標題爲"Beatles Blog"
、"beatles blog"
甚至"BeAtlES blOG"
的Blog
。
contains
大小寫敏感的包含關係測試。 像這樣:
Entry.objects.get(headline__contains='Lennon')
startswith
,
endswith
istartswith
和 iendswith
。
(4)跨關聯關係的查詢
Django 提供一種強大而又直觀的方式來「處理」查詢中的關聯關係,它在後臺自動幫你處理JOIN
。 若要跨越關聯關係,只需使用關聯的模型字段的名稱,並使用雙下劃線分隔,直至你想要的字段:
下面這個例子獲取全部Blog
的name
爲'Beatles Blog'
的Entry
對象:
>>> Entry.objects.filter(blog__name='Beatles Blog')
這種跨越能夠是任意的深度。
它還能夠反向工做。 若要引用一個「反向」的關係,只須要使用該模型的小寫的名稱。
下面的示例獲取全部的Blog
對象,它們至少有一個Entry
的headline
包含'Lennon'
:
>>> Blog.objects.filter(entry__headline__contains='Lennon')
選擇全部包含同時知足兩個條件的entry的blog,這兩個條件是headline 包含Lennon 和發表時間是2008 (同一個entry 知足兩個條件),咱們的代碼是:
Blog.objects.filter(entry__headline__contains='Lennon', entry__pub_date__year=2008)
要選擇全部這樣的blog,有一個entry的headline包含「Lennon」和有一個entry發表時間是2008,咱們將這樣編寫:
Blog.objects.filter(entry__headline__contains='Lennon').filter(entry__pub_date__year=2008)
(5)F()
若是你想將模型的一個字段與同一個模型的另一個字段進行比較該怎麼辦?
Django 提供F表達式
來容許這樣的比較。 F()
返回的實例用做查詢內部對模型字段的引用。 這些引用能夠用於查詢的filter 中來比較相同模型實例上不一樣字段之間值的比較。
例如,爲了查找comments 數目多於pingbacks 的Entry,咱們將構造一個F()
對象來引用pingback 數目,並在查詢中使用該F()
對象:
>>> from django.db.models import F >>> Entry.objects.filter(n_comments__gt=F('n_pingbacks'))
Django 支持對F()
對象使用加法、減法、乘法、除法、取模以及冪計算等算術操做,兩個操做數能夠都是常數和其它F()
對象。 爲了查找comments 數目比pingbacks 兩倍還要多的Entry,咱們將查詢修改成:
>>> Entry.objects.filter(n_comments__gt=F('n_pingbacks') * 2)
爲了查詢rating 比pingback 和comment 數目總和要小的Entry,咱們將這樣查詢:
>>> Entry.objects.filter(rating__lt=F('n_comments') + F('n_pingbacks'))
你還能夠在F()
對象中使用雙下劃線標記來跨越關聯關係。 帶有雙下劃線的F()
對象將引入任何須要的join 操做以訪問關聯的對象。 例如,如要獲取author 的名字與blog 名字相同的Entry,咱們能夠這樣查詢:
>>> Entry.objects.filter(authors__name=F('blog__name'))
對於date 和date/time 字段,你能夠給它們加上或減去一個timedelta
對象。 下面的例子將返回發佈超過3天后被修改的全部Entry:
>>> from datetime import timedelta >>> Entry.objects.filter(mod_date__gt=F('pub_date') + timedelta(days=3))
(6)PK查找快捷方式
爲了方便,Django 提供一個查詢快捷方式pk
,它表示「primary key」 的意思。
在示例Blog
模型中,主鍵pk是id
字段,因此這三個語句是等價的:
>>> Blog.objects.get(id__exact=14) # Explicit form >>> Blog.objects.get(id=14) # __exact is implied >>> Blog.objects.get(pk=14) # pk implies id__exact
(7)緩存和QuerySet
每一個QuerySet
都包含一個緩存來最小化對數據庫的訪問。 理解它是如何工做的將讓你編寫最高效的代碼。
在一個新建立的QuerySet
中,緩存爲空。 首次對QuerySet
進行求值 —— 同時發生數據庫查詢 ——Django 將保存查詢的結果到QuerySet
的緩存中並返回明確請求的結果(例如,若是正在迭代QuerySet
,則返回下一個結果)。 接下來對該QuerySet
的求值將重用緩存的結果。
請牢記這個緩存行爲,由於對QuerySet
使用不當的話,它會坑你的。 例如,下面的語句建立兩個QuerySet
,對它們求值,而後扔掉它們:
>>> print([e.headline for e in Entry.objects.all()]) >>> print([e.pub_date for e in Entry.objects.all()])
這意味着相同的數據庫查詢將執行兩次,顯然倍增了你的數據庫負載。 同時,還有可能兩個結果列表並不包含相同的數據庫記錄,由於在兩次請求期間有可能有Entry
被添加進來或刪除掉。
爲了不這個問題,只需保存QuerySet
並從新使用它:
>>> queryset = Entry.objects.all() >>> print([p.headline for p in queryset]) # Evaluate the query set. >>> print([p.pub_date for p in queryset]) # Re-use the cache from the evaluation.
(8)使用Q對象進行復雜查找
在filter()
中的關鍵字參數查詢 — — 是「AND」的關係。 若是你須要執行更復雜的查詢(例如OR
語句),你能夠使用Q對象
。
Q object
(django.db.models.Q
) 對象用於封裝一組關鍵字參數。 這些關鍵字參數就是上文「字段查詢」 中所說起的那些。
例如,下面的LIKE
對象封裝一個Q
查詢:
from django.db.models import Q Q(question__startswith='What')
Q
對象能夠使用&
和|
操做符組合起來。 當一個操做符在兩個Q
對象上使用時,它產生一個新的Q
對象。
例如,下面的語句產生一個"question__startswith"
對象,表示兩個Q
查詢的「OR」 :
Q(question__startswith='Who') | Q(question__startswith='What')
它等同於下面的SQL WHERE
子句:
WHERE question LIKE 'Who%' OR question LIKE 'What%'
你能夠組合&
和|
操做符以及使用括號進行分組來編寫任意複雜的Q
對象。 同時,~
對象能夠使用NOT
操做符取反,這容許組合正常的查詢和取反(Q
) 查詢:
Q(question__startswith='Who') | ~Q(pub_date__year=2005)
每一個接受關鍵字參數的查詢函數(例如filter()
、exclude()
、get()
)均可以傳遞一個或多個Q
對象做爲位置(不帶名的)參數。 若是一個查詢函數有多個Q
對象參數,這些參數的邏輯關係爲「AND"。 像這樣:
Poll.objects.get( Q(question__startswith='Who'), Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)) )
查詢函數能夠混合使用Q
和關鍵字參數。 全部提供給查詢函數的參數(關鍵字參數或Q
對象)都將"AND」在一塊兒。 可是,若是出現Q
對象,它必須位於全部關鍵字參數的前面。 像這樣:
Poll.objects.get( Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)), question__startswith='Who', )
錯誤例子:
# INVALID QUERY Poll.objects.get( question__startswith='Who', Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)) )
(9)反向查詢
若是模型有一個ForeignKey
,那麼該ForeignKey所指的模型實例能夠經過一個Manager
返回第一個模型的全部實例。 默認狀況下,這個Manager
的名字爲FOO_set
,其中FOO
是源模型的小寫名稱。 該Manager
返回QuerySets
,能夠用上一節提到的方式進行過濾和操做。
例如:
>>> b = Blog.objects.get(id=1) >>> b.entry_set.all() # Returns all Entry objects related to Blog. # b.entry_set is a Manager that returns QuerySets. >>> b.entry_set.filter(headline__contains='Lennon') >>> b.entry_set.count()
你能夠在ForeignKey
定義時設置related_name
參數來覆蓋FOO_set
的名稱。 例如,若是Entry
模型更改成blog = ForeignKey(Blog, on_delete=models.CASCADE, related_name='entries')
,上述示例代碼以下所示:
>>> b = Blog.objects.get(id=1) >>> b.entries.all() # Returns all Entry objects related to Blog. # b.entries is a Manager that returns QuerySets. >>> b.entries.filter(headline__contains='Lennon') >>> b.entries.count()
(10)多對多關係
多對多關係的兩端都會自動得到訪問另外一端的API。 這些API 的工做方式與上面提到的「方向」一對多關係同樣。
惟一的區別在於屬性的命名:定義 ManyToManyField
的模型使用該字段的屬性名稱,而「反向」模型使用源模型的小寫名稱加上'_set'
(和一對多關係同樣)。
一個例子可讓它更好理解:
e = Entry.objects.get(id=3) e.authors.all() # Returns all Author objects for this Entry. e.authors.count() e.authors.filter(name__contains='John') a = Author.objects.get(id=5) a.entry_set.all() # Returns all Entry objects for this Author.
(11)一對一關係
對一關係與多對一關係很是類似。 若是你在模型中定義一個OneToOneField
,該模型的實例將能夠經過該模型的一個簡單屬性訪問關聯的模型。
像這樣:
class EntryDetail(models.Model): entry = models.OneToOneField(Entry, on_delete=models.CASCADE) details = models.TextField() ed = EntryDetail.objects.get(id=2) ed.entry # Returns the related Entry object.
在「反向」查詢中有所不一樣。 一對一關係中的關聯模型一樣具備一個Manager
對象,可是該Manager
表示一個單一的對象而不是對象的集合:
e = Entry.objects.get(id=2) e.entrydetail # returns the related EntryDetail object
反向關聯的關係是如何實現的
其它對象關係映射要求你在關聯關係的兩端都要定義。 Django 的開發人員相信這是對DRY(不要重複你本身的代碼)原則的違背,因此Django 只要求你在一端定義關聯關係。
可是這怎麼可能?由於一個模型類直到其它模型類被加載以後才知道哪些模型類是關聯的。
答案在app registry
中。 當Django 啓動時,它導入INSTALLED_APPS
中列出的每一個應用,而後導入每一個應用中的models
模塊。 每建立一個新的模型時,Django 添加反向的關係到全部關聯的模型。 若是關聯的模型尚未導入,Django 將保存關聯關係的記錄並在最終關聯的模型導入時添加這些關聯關係。
因爲這個緣由,你使用的全部模型都定義在INSTALLED_APPS
列出的應用中就顯得特別重要。 不然,反向的關聯關係將不能正確工做。
Django抽象的數據庫API描述使用Django查詢來增刪查改單個對象的方法。 然而,有時候你須要獲取的值須要根據一組對象聚合後才能獲得。 這份指南描述經過Django 查詢來生成和返回聚合值的方法。
整篇指南咱們都將引用如下模型。 這些模型用來記錄多個網上書店的庫存。
from django.db import models class Author(models.Model): name = models.CharField(max_length=100) age = models.IntegerField() class Publisher(models.Model): name = models.CharField(max_length=300) num_awards = models.IntegerField() class Book(models.Model): name = models.CharField(max_length=300) pages = models.IntegerField() price = models.DecimalField(max_digits=10, decimal_places=2) rating = models.FloatField() authors = models.ManyToManyField(Author) publisher = models.ForeignKey(Publisher) pubdate = models.DateField() class Store(models.Model): name = models.CharField(max_length=300) books = models.ManyToManyField(Book) registered_users = models.PositiveIntegerField()
如下是在上述模型的基礎上,進行通常的聚合查詢的方法:
# Total number of books. >>> Book.objects.count() 2452 # Total number of books with publisher=BaloneyPress >>> Book.objects.filter(publisher__name='BaloneyPress').count() 73 # Average price across all books. >>> from django.db.models import Avg >>> Book.objects.all().aggregate(Avg('price')) {'price__avg': 34.35} # Max price across all books. >>> from django.db.models import Max >>> Book.objects.all().aggregate(Max('price')) {'price__max': Decimal('81.20')} # Difference between the highest priced book and the average price of all books. >>> from django.db.models import FloatField >>> Book.objects.aggregate( ... price_diff=Max('price', output_field=FloatField()) - Avg('price'))) {'price_diff': 46.85} # All the following queries involve traversing the Book<->Publisher # foreign key relationship backwards. # Each publisher, each with a count of books as a "num_books" attribute. >>> from django.db.models import Count >>> pubs = Publisher.objects.annotate(num_books=Count('book')) >>> pubs <QuerySet [<Publisher: BaloneyPress>, <Publisher: SalamiPress>, ...]> >>> pubs[0].num_books 73 # The top 5 publishers, in order by number of books. >>> pubs = Publisher.objects.annotate(num_books=Count('book')).order_by('-num_books')[:5] >>> pubs[0].num_books 1323
(1)經過QuerySet生成聚合
在QuerySet
.對象上計算出總價格。 這能夠經過在aggregate()
後面附加QuerySet
子句來完成。
>>> Book.objects.aggregate(Avg('price')) {'price__avg': 34.35}
aggregate()
是QuerySet
的一個終止子句,意思是說,它返回一個包含一些鍵值對的字典。 該名稱是總值的標識符;該值是計算的聚合。 鍵的名稱是按照字段和聚合函數的名稱自動生成出來的。 若是你想要爲聚合值指定一個名稱,能夠向聚合子句提供它。
>>> Book.objects.aggregate(average_price=Avg('price')) {'average_price': 34.35}
若是你但願生成不止一個聚合,你能夠向aggregate()
子句中添加另外一個參數。 因此,若是你也想知道全部圖書價格的最大值和最小值,能夠這樣查詢:
>>> from django.db.models import Avg, Max, Min >>> Book.objects.aggregate(Avg('price'), Max('price'), Min('price')) {'price__avg': 34.35, 'price__max': Decimal('81.20'), 'price__min': Decimal('12.99')}
(2)位QuerySet中每一個項目生成聚合
生成彙總值的第二種方法,是爲QuerySet
中每個對象都生成一個獨立的彙總值。 好比,若是你在檢索一列圖書,你可能想知道每一本書有多少做者參與。 每本書與做者有多對多的關係;咱們想在QuerySet
中總結每本書的這種關係。
逐個對象的彙總結果能夠由annotate()
子句生成。 當annotate()
子句被指定以後,QuerySet
中的每一個對象都會被註上特定的值。
這些註解的語法都和aggregate()
子句所使用的相同。 annotate()
的每一個參數都描述了將要被計算的聚合。 好比,給圖書添加做者數量的註解:
# Build an annotated queryset >>> from django.db.models import Count >>> q = Book.objects.annotate(Count('authors')) # Interrogate the first object in the queryset >>> q[0] <Book: The Definitive Guide to Django> >>> q[0].authors__count 2 # Interrogate the second object in the queryset >>> q[1] <Book: Practical Django Projects> >>> q[1].authors__count 1
和使用 aggregate()
同樣,註解的名稱也根據聚合函數的名稱和聚合字段的名稱獲得的。 你能夠在指定註解時,爲默認名稱提供一個別名:
>>> q = Book.objects.annotate(num_authors=Count('authors')) >>> q[0].num_authors 2 >>> q[1].num_authors 1
與 aggregate()
不一樣的是, annotate()
不是一個終止子句。 annotate()
子句的輸出是一個QuerySet
;能夠使用任何其餘QuerySet
操做修改QuerySet
,包括filter()
,order_by()
或甚至附加調用annotate()
。
要查找每一個商店提供的圖書的價格範圍,您能夠使用註釋:
>>> from django.db.models import Max, Min >>> Store.objects.annotate(min_price=Min('books__price'), max_price=Max('books__price'))
這段代碼告訴 Django 獲取Store
模型,並鏈接(經過多對多關係)Book
模型,而後對每本書的價格進行聚合,得出最小值和最大值。
一樣的規則也用於 aggregate()
子句。 若是您想知道任何商店中可出售的任何圖書的最低價格和最高價格,您能夠使用匯總:
>>> Store.objects.aggregate(min_price=Min('books__price'), max_price=Max('books__price'))
關係鏈能夠按你的要求一直延伸。 例如,想獲得全部做者當中最小的年齡是多少,就能夠這樣寫:
>>> Store.objects.aggregate(youngest_age=Min('books__authors__age'))
(3)反向
在你所查詢的模型的關聯模型或者字段上的聚合和註解能夠遍歷"反轉"關係。 關聯模型的小寫名稱和雙下劃線也用在這裏。
例如,咱們能夠查詢全部出版商,並註上它們一共出了多少本書(注意咱們如何用 Publisher
指定Book
-> 'book'
的外鍵反轉關係):
查詢全部圖書中最舊的那本:
>> Publisher.objects.aggregate(oldest_pubdate=Min('book__pubdate'))
這不只僅能夠應用掛在外鍵上面。 還能夠用到多對多關係上。 例如,咱們能夠查詢每一個做者,註上它寫的全部書(以及合著的書)一共有多少頁(注意咱們如何使用 Author
來指定Book
-> 'book'
的多對多的反轉關係):
>>> Author.objects.annotate(total_pages=Sum('book__pages'))
查詢全部圖書的平均評分,這些圖書由咱們存檔過的做者所寫:
>>> Author.objects.aggregate(average_rating=Avg('book__rating'))
使用annotate()
子句時,過濾器有限制註解對象的做用。 例如,你想獲得每本以 "Django" 爲書名開頭的圖書做者的總數:
>>> from django.db.models import Count, Avg >>> Book.objects.filter(name__startswith="Django").annotate(num_authors=Count('authors'))
使用aggregate()
子句時,過濾器有限制聚合對象的做用。 例如,你能夠算出全部以 "Django" 爲書名開頭的圖書平均價格:
>>> Book.objects.filter(name__startswith="Django").aggregate(Avg('price'))
(4)過濾註釋
註解值也能夠被過濾。 像使用其餘模型字段同樣,註解也能夠在filter()
和exclude()
子句中使用別名。
例如,要獲得不止一個做者的圖書,能夠用:
>>> Book.objects.annotate(num_authors=Count('authors')).filter(num_authors__gt=1)
這個查詢首先生成一個註解結果,而後再生成一個做用於註解上的過濾器。
(5)order_by()
註解能夠用來作爲排序項。 在你定義 order_by()
子句時,你提供的聚合能夠引用定義的任何別名作爲查詢中 annotate()
子句的一部分。
例如,根據一本圖書做者數量的多少對查詢集 QuerySet
進行排序:
>>> Book.objects.annotate(num_authors=Count('authors')).order_by('num_authors')
(6)values()
一般,註解會添加到每一個對象上 —— 一個被註解的QuerySet
會爲初始QuerySet
的每一個對象返回一個結果集。 可是,若是使用了values()
子句,它就會限制結果中列的範圍,對註解賦值的方法就會徹底不一樣。 不是在原始的 QuerySet
返回結果中對每一個對象中添加註解,而是根據定義在values()
子句中的字段組合先對結果進行惟一的分組, 而後爲每一個惟一組提供註釋;在組的全部成員上計算註釋。
>>> Author.objects.annotate(average_rating=Avg('book__rating')).values('name', 'average_rating')
Django 提供了一系列 的QuerySet
篩選方法,用於改變 QuerySet
返回的結果類型或者SQL查詢執行的方式。
filter()
filter
(**kwargs)
返回一個新的QuerySet
,它包含知足查詢參數的對象
exclude()
exclude
(**kwargs)
返回一個新的QuerySet
,它包含不知足給定的查找參數的對象。
下面的示例排除全部pub_date
晚於2005-1-3 且headline
爲「Hello」 的記錄:
Entry.objects.exclude(pub_date__gt=datetime.date(2005, 1, 3), headline='Hello')
annotate()
annotate
(*args, **kwargs)
例如,若是你正在操做一個Blog列表,你可能想知道每一個Blog有多少Entry:
>>> from django.db.models import Count >>> q = Blog.objects.annotate(Count('entry')) # The name of the first blog >>> q[0].name 'Blogasaurus' # The number of entries on the first blog >>> q[0].entry__count 42
order_by()
order_by
(*fields)
默認狀況下,Meta
根據模型ordering
類的QuerySet
選項排序。 你能夠使用QuerySet
方法給每一個order_by
指定特定的排序。
例如:
Entry.objects.filter(pub_date__year=2005).order_by('-pub_date', 'headline')
上面的結果將按照pub_date
降序排序,而後再按照headline
升序排序。 "-pub_date"
前面的負號表示降序順序。 升序是隱含的。
reverse()
reverse()
方法反向排序QuerySet 中返回的元素。 第二次調用reverse()
將恢復到原有的排序。
如要獲取QuerySet 中最後五個元素,你能夠這樣作:
my_queryset.reverse()[:5]
注意,這與Python 中從一個序列的末尾進行切片有點不同。 上面的例子將首先返回最後一個元素,而後是倒數第二個元素,以此類推。 若是咱們有一個Python 序列,當咱們查看seq[-5:]
時,咱們將一會兒獲得倒數五個元素。 Django 不支持這種訪問模型(從末尾進行切片),由於它不可能利用SQL 高效地實現。
values()
values
(*fields, **expressions)
返回一個返回字典的QuerySet
,而不是使用模型實例做爲一個迭代。
每一個字典表示一個對象,鍵對應於模型對象的屬性名稱。
下面的例子將values()
與普通的模型對象進行比較:
# This list contains a Blog object. >>> Blog.objects.filter(name__startswith='Beatles') <QuerySet [<Blog: Beatles Blog>]> # This list contains a dictionary. >>> Blog.objects.filter(name__startswith='Beatles').values() <QuerySet [{'id': 1, 'name': 'Beatles Blog', 'tagline': 'All the latest Beatles news.'}]>
SELECT
接收可選的位置參數*fields
,它指定values()
應該限制哪些字段。 若是指定字段,每一個字典將只包含指定的字段的鍵/值。 若是沒有指定字段,每一個字典將包含數據庫表中全部字段的鍵和值。
例如:
>>> Blog.objects.values() <QuerySet [{'id': 1, 'name': 'Beatles Blog', 'tagline': 'All the latest Beatles news.'}]> >>> Blog.objects.values('id', 'name') <QuerySet [{'id': 1, 'name': 'Beatles Blog'}]>
values_list(*fields, flat=False)
與values()
相似,只是在迭代時返回的是元組而不是字典。 每一個元組包含傳遞給values_list()
調用的相應字段或表達式的值,所以第一個項目是第一個字段等。 像這樣:
>>> Entry.objects.values_list('id', 'headline') <QuerySet [(1, 'First entry'), ...]> >>> from django.db.models.functions import Lower >>> Entry.objects.values_list('id', Lower('headline')) <QuerySet [(1, 'first entry'), ...]>
select_related()
返回一個QuerySet
,當執行它的查詢時它沿着外鍵關係查詢關聯的對象的數據。 它會生成一個複雜的查詢並引發性能的損耗,可是在之後使用外鍵關係時將不須要數據庫查詢。
defer()
defer
(*fields)
在一些複雜的數據建模狀況下,你的模型可能包含大量字段,其中一些可能包含大量數據(例如文本字段),或者須要昂貴的處理來將它們轉換爲Python對象。 當你最初獲取數據時不知道是否須要這些特定字段的狀況下,若是你正在使用查詢集的結果,你能夠告訴Django不要從數據庫中檢索它們。
它經過傳遞字段名稱到defer()
實現不加載:
# 延遲body和headline兩個字段。 Entry.objects.defer("body").filter(rating=5).defer("headline")
若是要清除延遲字段集,請將None
做爲參數傳遞到defer()
:
# 當即加載全部的字段。 my_queryset.defer(None)
only()
only
(*fields)
only()
方法或多或少與defer()
相反。 你以不應該在檢索模型時延遲的字段調用它。 若是你有一個模型幾乎全部的字段須要延遲,使用only()
指定補充的字段集能夠致使更簡單的代碼。
get()
get
(**kwargs)
返回按照查詢參數匹配到的對象
count()
count
()
返回在數據庫中對應的 QuerySet
.對象的個數。 count()
永遠不會引起異常。
in_bulk()
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>}
latest()
latest
(field_name=None)
使用做爲日期字段提供的field_name
,按日期返回表中的最新對象。
此示例根據Entry
字段返回表中的最新pub_date
:
Entry.objects.latest('pub_date')
first()
first
()
返回結果集的第一個對象, 當沒有找到時返回None
. 若是 QuerySet
沒有設置排序,則將會自動按主鍵進行排序
p = Article.objects.order_by('title', 'pub_date').first()
last()
last
()
工做方式相似first()
,只是返回的是查詢集中最後一個對象。
aggregate()
aggregate
(*args, **kwargs)
返回彙總值的字典(平均值,總和等) 經過QuerySet
進行計算。 aggregate()
的每一個參數指定返回的字典中將要包含的值。
exists()
exists
()
若是QuerySet
包含任何結果,則返回True
,不然返回False
。
exact
精確匹配。
iexact
不區分大小寫的精確匹配
contains
大小寫敏感的包含關係測試。
例如:
Entry.objects.get(headline__contains='Lennon')
icontains
測試是否包含,不區分大小寫。
in
在給定的列表。
例如:
Entry.objects.filter(id__in=[1, 3, 4])
gt
大於
例如:
Entry.objects.filter(id__gt=4)
gte
大於或等於
lt
小於
lte
小於或等於
startswith
區分大小寫,開始位置匹配
例如:
Entry.objects.filter(headline__startswith='Lennon')
istartswith
不區分大小寫,開始位置匹配
endswith
區分大小寫。
iendswith
不區分大小寫。
date
對於datetime字段,將值做爲日期轉換。 容許連接附加字段查找。 獲取日期值。
例如:
Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1))
Entry.objects.filter(pub_date__date__gt=datetime.date(2005, 1, 1))
year
對於日期和日期時間字段,確切的年匹配。 容許連接附加字段查找。 整數年。
Entry.objects.filter(pub_date__year=2005)
Entry.objects.filter(pub_date__year__gte=2005)
month
對於日期和日期時間字段,確切的月份匹配。
day
對於日期和日期時間字段,具體到某一天的匹配。
你能夠在模型中使用自定義的Manager
,方法是繼承Manager
基類並實例化你的自定義Manager
。
你有兩個緣由可能會本身定義Manager
:向Manager
類中添加額外的方法,或者修改Manager
返回的原始QuerySet
。
(1)修改管理器的初始QuerySet
管理器
自帶的QuerySet
返回系統中全部的對象。 例如,使用下面這個模型:
from django.db import models class Book(models.Model): title = models.CharField(max_length=100) author = models.CharField(max_length=50)
...語句Book.objects.all()
將返回數據庫中的全部書籍。
你能夠經過重寫Manager.get_queryset()
方法來覆蓋Manager
自帶的QuerySet
。 get_queryset()
應該返回一個帶有你須要的屬性的QuerySet
。
例如,下面的模型有兩個Manager
,一個返回全部的對象,另外一個則只返回做者是Roald Dahl 的對象:
...語句Book.objects.all()
將返回數據庫中的全部書籍。
你能夠經過重寫Manager.get_queryset()
方法來覆蓋Manager
自帶的QuerySet
。 get_queryset()
應該返回一個帶有你須要的屬性的QuerySet
。
例如,下面的模型有兩個Manager
,一個返回全部的對象,另外一個則只返回做者是Roald Dahl 的對象:
# 首先,定義管理器的子類。 class DahlBookManager(models.Manager): def get_queryset(self): return super(DahlBookManager, self).get_queryset().filter(author='Roald Dahl') # 而後將它顯式地放入Book模型。 class Book(models.Model): title = models.CharField(max_length=100) author = models.CharField(max_length=50) objects = models.Manager() # 默認的管理器。 dahl_objects = DahlBookManager() # 用於Dahl的管理器。
在這個簡單的例子中,Book.objects.all()
將返回數據庫中全部的圖書,而Book.dahl_objects.all()
只返回Roald Dahl寫做的圖書。
固然,由於get_queryset()
返回QuerySet
對象,你能夠使用filter()
、exclude()
和全部其餘QuerySet
方法。 因此下面這些例子都是可用的:
Book.dahl_objects.all() Book.dahl_objects.filter(title='Matilda') Book.dahl_objects.count()
該例還展現了另一個頗有意思的技巧:同一模型使用多個管理器。 你能夠依據你本身的偏好在一個模型裏面添加多個 Manager()
實例。 這是給模型添加通用過濾器(選擇器)的一個簡單方法:
像這樣:
class AuthorManager(models.Manager): def get_queryset(self): return super(AuthorManager, self).get_queryset().filter(role='A') class EditorManager(models.Manager): def get_queryset(self): return super(EditorManager, self).get_queryset().filter(role='E') class Person(models.Model): first_name = models.CharField(max_length=50) last_name = models.CharField(max_length=50) role = models.CharField(max_length=1, choices=(('A', _('Author')), ('E', _('Editor')))) people = models.Manager() authors = AuthorManager() editors = EditorManager()
這個例子讓你能夠使用Person.authors.all()
、 Person.editors.all()
以及Person.people.all()
,都會獲得預期的結果
(2)從管理器調用自定義QuerySet方法
雖然大多數標準QuerySet
的方法能夠從Manager
中直接訪問到,可是若是你須要將一些被定義到一個自定義QuerySet
中的額外方法也在Manager
上實現,下面所展現的這個例子是惟一可行辦法:
class PersonQuerySet(models.QuerySet): def authors(self): return self.filter(role='A') def editors(self): return self.filter(role='E') class PersonManager(models.Manager): def get_queryset(self): return PersonQuerySet(self.model, using=self._db) def authors(self): return self.get_queryset().authors() def editors(self): return self.get_queryset().editors() class Person(models.Model): first_name = models.CharField(max_length=50) last_name = models.CharField(max_length=50) role = models.CharField(max_length=1, choices=(('A', _('Author')), ('E', _('Editor')))) people = PersonManager()
這個例子容許你直接從管理器Person.people
中調用authors()
和editors()
。
可用的Meta選項
abstract
Options.
abstract
若是 abstract = True
, 就表示模型是抽象基類。
app_label
Options.
app_label
若是該項目下有多個app,有一個model不是定義在本app下默認的model.py,而是在其餘app,也即它在本app settings的INSTALLED_APPS
沒有聲明,則必須使用app_lable聲明其屬於哪一個app:
app_label = 'myapp'
若是要表示具備格式app_label.object_name
或app_label.model_name
的模型,能夠使用model._meta.label
或model._meta.label_lower
。
base_manager_name
Options.
base_manager_name
模型中_base_manager
所使用的manager的名稱(模型管理器的名稱)。
db_table
Options.
db_table
該模型所用的數據表的名稱:
db_table = 'music_album'
Table names
爲了節省時間,Django 會自動的使用你的 model class 的名稱和包含這個 model 的 app 名稱來構建 數據庫的表名稱。 一個 model 的數據庫表名稱是經過將 「app label」 – 你在 manage.py startapp
中使用的名稱 – 和 model 的類名稱,加上一個下劃線在他們之間來構成。
舉個例子,bookstore
應用(使用manage.py startapp bookstore
建立),以class Book
定義的模型的數據表的名稱將是bookstore_book
。
使用 Meta
類中的 db_table
參數來重寫數據表的名稱。
若是你的數據庫表名稱是SQL保留字,或包含Python變量名稱中不容許的字符,特別是連字符 — 沒有問題。 Django在後臺引用列和表名。
db_tablespace
Options.
db_tablespace
當前模型所使用的database tablespace 的名字。 默認值是項目設置中的DEFAULT_TABLESPACE
,若是它存在的話。 若是後端並不支持表空間,這個選項能夠忽略。
default_manager_name
Options.
default_manager_name
模型的_default_manager
用到的管理器的名稱。
from django.db import models class Foo(models.Model): pass class Bar(models.Model): foo = models.ForeignKey(Foo) class Meta: default_related_name = 'bars'
>>> bar = Bar.objects.get(pk=1) >>> # 使用名稱"bar"做爲查詢名稱已棄用。 >>> Foo.objects.get(bar=bar) >>> # 你應該使用default_related_name "bars"。 >>> Foo.objects.get(bars=bar)
get_latest_by
Options.
get_latest_by
模型中某個可排序的字段的名稱,好比DateField
、DateTimeField
或者IntegerField
。 它指定了Manager
的latest()
和earliest()
中使用的默認字段。
例如:
get_latest_by = "order_date"
managed
Options.
managed
默認爲True
,表示Django會經過migrate
建立合適的數據表,而且可經過flush
管理命令移除這些數據庫表。 換句話說,Django會管理這些數據表的生命週期。
若是是False
,Django 就不會爲當前模型建立和刪除數據表。 若是當前模型表示一個已經存在的且是經過其它方法建立的者數據表或數據庫視圖,這會至關有用。 這是設置爲managed=False
時惟一的不一樣之處。 模型處理的其它任何方面都和日常同樣。 這包括:
若是你不聲明它的話,會向你的模型中添加一個自增主鍵。 爲了不給後面的代碼讀者帶來混亂,當你在使用未被管理的模型時,強烈推薦你指定(specify)數據表中全部的列。
若是一個模型設置了managed=False
且含有ManyToManyField
,且這個多對多字段指向其餘一樣也是未被管理模型的,那麼這兩個未被管理的模型的多對多中介表也不會被建立。 可是,一個被管理模型和一個未被管理模型之間的中介表就會被建立。
若是你須要修改這一默認行爲,建立中介表做爲顯式的模型(也要設置managed
),而且使用ManyToManyField.through
爲你的自定義模型建立關聯。
若是你進行測試,測試中涉及非託管 model (managed=False
),那麼在測試以前,你應該要確保在 測試啓動時 已經建立了正確的數據表。
若是你對在Python層面修改模型類的行爲感興趣,你能夠設置 managed=False
,而且爲一個已經存在的模型建立一個副本。 不過在面對這種狀況時還有個更好的辦法就是使 用Proxy models.
order_with_respect_to
Options.
order_with_respect_to
使此對象相對於給定字段能夠排序,一般爲ForeignKey
。 這能夠用於使關聯的對象相對於父對象可排序。 好比,若是Answer
和Question
相關聯,一個問題有至少一個答案,而且答案的順序很是重要,你能夠這樣作:
from django.db import models class Question(models.Model): text = models.TextField() # ... class Answer(models.Model): question = models.ForeignKey(Question, on_delete=models.CASCADE) # ... class Meta: order_with_respect_to = 'question'
當order_with_respect_to
設置以後,模型會提供兩個額外的用於設置和獲取關聯對象順序的方法:get_RELATED_order()
和set_RELATED_order()
,其中RELATED
是小寫的模型名稱。 例如,假設一個Question
對象有不少相關聯的Answer
對象,返回的列表中含有與之相關聯Answer
對象的主鍵:
>>> question = Question.objects.get(id=1) >>> question.get_answer_order() [1, 2, 3]
與Question
對象相關聯的Answer
對象的順序,能夠經過傳入一個包含Answer
主鍵的列表來設置:
>>> question.set_answer_order([3, 1, 2])
相關聯的對象也有兩個方法, get_next_in_order()
和get_previous_in_order()
,用於按照合適的順序訪問它們。 假設Answer
對象按照 id
來排序:
>>> answer = Answer.objects.get(id=2) >>> answer.get_next_in_order() <Answer: 3> >>> answer.get_previous_in_order() <Answer: 1>
ordering
Options.
ordering
對象默認的順序,在獲取對象的列表時使用:
ordering = ['-order_date']
它是一個字符串的列表或元組。 每一個字符串是一個字段名,前面帶有可選的「-」前綴表示倒序。 前面沒有「-」的字段表示正序。 使用字符串「?」來隨機排序。
例如,要按照pub_date
字段的正序排序,這樣寫:
ordering = ['pub_date']
按照pub_date
字段的倒序排序,這樣寫:
ordering = ['-pub_date']
先按照pub_date
的倒序排序,再按照 author
的正序排序,這樣寫:
ordering = ['-pub_date', 'author']
permissions
Options.
permissions
設置建立對象時權限表中額外的權限。 增長、刪除和修改權限會自動爲每一個模型建立。 這個例子指定了一種額外的權限,can_deliver_pizzas
:
permissions = (("can_deliver_pizzas", "Can deliver pizzas"),)
它是一個包含二元組的元組或者列表,格式爲 (permission_code, human_readable_permission_name)
。
default_permissions
Options.
default_permissions
默認爲('add', 'change', 'delete')
。 你能夠自定義這個列表,好比,若是你的應用不須要默認權限中的任何一項,能夠把它設置成空列表。 在模型被migrate
命令建立以前,這個屬性必須被指定,以防一些遺漏的屬性被建立。
proxy
Options.
proxy
若是proxy = True
, 它做爲另外一個模型的子類,將會做爲一個proxy model。
required_db_features
Options.
required_db_features
當前鏈接應具備的數據庫功能列表,以便在遷移階段考慮該模型。 例如,若是將此列表設置爲['gis_enabled']
,則模型將僅在啓用GIS的數據庫上同步。 在使用多個數據庫後端進行測試時,跳過某些模型也頗有用。 避免與ORM無關的模型之間的關係。
required_db_vendor
Options.
required_db_vendor
此型號特定於受支持的數據庫供應商的名稱。 當前內置的供應商名稱是:sqlite
,postgresql
,mysql
,oracle
。 若是此屬性不爲空,而且當前鏈接供應商不匹配,則該模型將不會同步。
select_on_save
Options.
select_on_save
該選項決定Django是否採用1.6以前的django.db.models.Model.save()
算法。 舊的算法使用SELECT
來判斷是否存在須要更新的行。 而新的算法直接嘗試使用UPDATE
。 在某些少見的狀況下,一個已存在行的UPDATE
操做對Django不可見。 一個例子是PostgreSQL的返回NULL
的ON UPDATE
觸發器。 這種狀況下,新式的算法最終會執行INSERT
操做,即便這一行已經在數據庫中存在。
一般這個屬性不須要設置。 默認爲False
。
關於舊式和新式兩種算法,請參見django.db.models.Model.save()
。
indexes
Options.
indexes
要在模型上定義的索引的列表:
from django.db import models class Customer(models.Model): first_name = models.CharField(max_length=100) last_name = models.CharField(max_length=100) class Meta: indexes = [ models.Index(fields=['last_name', 'first_name']), models.Index(fields=['first_name'], name='first_name_idx'), ]
unique_together
Options.
unique_together
用來設置的不重複的字段組合:
unique_together = (("driver", "restaurant"),)
它是一個元組的元組,組合起來的時候必須是惟一的。 它在Django admin層面使用,在數據庫層上進行數據約束(好比,在 CREATE TABLE
語句中包含 UNIQUE
語句)。
爲了方便起見,處理單一字段的集合時,unique_together 能夠是一維的元組:
unique_together = ("driver", "restaurant")
ManyToManyField
不能包含在unique_together中。 (不清楚它的含義是什麼!) 若是你須要驗證ManyToManyField
關聯的惟一性,試着使用信號或者顯式的through
模型。
當unique_together
的約束被違反時,模型校驗期間會拋出ValidationError
異常。
index_together
Options.
index_together
index_together = [ ["pub_date", "deadline"], ]
列表中的字段將會創建索引(例如,會在CREATE INDEX
語句中被使用)。
爲了方便起見,當須要處理的字段的集合只有一個的時候(集合只有一個!),index_together
能夠只用一箇中括號。也就是隻用一個一維列表。
index_together = ["pub_date", "deadline"]
verbose_name
Options.
verbose_name
對象的一個易於理解的名稱,爲單數:
verbose_name = "pizza"
若是此項沒有設置,Django會把類名拆分開來做爲自述名,好比CamelCase
會變成camel case
,
verbose_name_plural
Options.
verbose_name_plural
該對象複數形式的名稱:
verbose_name_plural = "stories"
若是此項沒有設置,Django 會使用 verbose_name
+ "s"
。
只讀的Meta
屬性
label
Options.
label
對象的表示,返回app_label.object_name
,例如'polls.Question'
。
label_lower
Options.
label_lower
模型的表示,返回app_label.model_name
,例如'polls.question'
。
當一個用戶請求Django 站點的一個頁面,下面是Django 系統決定執行哪一個Python 代碼遵循的算法:
ROOT_URLCONF
設置的值,可是若是傳入的HttpRequest
對象具備urlconf
屬性(由中間件設置),則其值將被用於代替ROOT_URLCONF
設置。urlpatterns
。 它是django.conf.urls.url()
實例的一個Python 列表。HttpRequest
實例。django.conf.urls.url()
的可選參數kwargs
覆蓋。(1)示例
下面是一個簡單的 URLconf:
from django.conf.urls import url from . import views urlpatterns = [ url(r'^articles/2003/$', views.special_case_2003), url(r'^articles/([0-9]{4})/$', views.year_archive), url(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive), # url(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.article_detail), # ]/articles/2005/03//articles/2003/03/03/
注:
^articles
而不是 ^/articles
。'r'
是可選的可是建議加上。 它告訴Python 這個字符串是「原始的」 —— 字符串中任何字符都不該該轉義。 一些請求的例子:
/articles/2005/03/
請求將匹配列表中的第三個模式。 Django 將調用函數views.month_archive(request, '2005', '03')
。/articles/2005/3/
不匹配任何URL 模式,由於列表中的第三個模式要求月份應該是兩個數字。/articles/2003/
將匹配列表中的第一個模式不是第二個,由於模式按順序匹配,第一個會首先測試是否匹配。 請像這樣自由插入一些特殊的狀況來探測匹配的次序。 這裏,Django會調用函數views.special_case_2003(request)
/articles/2003
不匹配任何一個模式,由於每一個模式要求URL 以一個斜線結尾。/articles/2003/03/03/
將匹配最後一個模式。 Django 將調用函數views.article_detail(request, '2003', '03', '03')
。(2)命名組
上面的示例使用簡單的、沒有命名的正則表達式組(經過圓括號)來捕獲URL 中的值並以位置 參數傳遞給視圖。 在更高級的用法中,能夠使用命名的正則表達式組來捕獲URL 中的值並以關鍵字 參數傳遞給視圖。
在Python 正則表達式中,命名正則表達式組的語法是(?P<name>pattern)
,其中name
是組的名稱,pattern
是要匹配的模式。
下面是以上URLconf 使用命名組的重寫:
from django.conf.urls import url from . import views urlpatterns = [ url(r'^articles/2003/$', views.special_case_2003), url(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive), url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive), url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]{2})/$', views.article_detail), ]
這個實現與前面的示例徹底相同,只有一個細微的差異:捕獲的值做爲關鍵字參數而不是位置參數傳遞給視圖函數。 像這樣:
/articles/2005/03/
請求將調用views.month_archive(request, year='2005', month='03')
函數,而不是views.month_archive(request, '2005', '03')
。/articles/2003/03/03/
請求將調用函數views.article_detail(request, year='2003', month='03', day='03')
。在實際應用中,這意味你的URLconf 會更加明晰且不容易產生參數順序問題的錯誤 —— 你能夠在你的視圖函數定義中從新安排參數的順序。 固然,這些好處是以簡潔爲代價的;一些開發人員發現命名組語法醜陋並且太冗長。
捕獲的參數老是字符串
每一個捕獲的參數都做爲一個普通的Python 字符串傳遞給視圖,不管正則表達式使用的是什麼匹配方式。 例如,下面這行URLconf 中:
url(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive)
views.year_archive()
的
year
參數將是一個字符串,
[0-9]{4}
只匹配整數字符串。
(3)傳遞額外的參數來查看函數
URLconfs 具備一個鉤子,讓你傳遞一個Python 字典做爲額外的參數傳遞給視圖函數。
django.conf.urls.url()
函數能夠接收一個可選的第三個參數,它是一個字典,表示想要傳遞給視圖函數的額外關鍵字參數。
像這樣:
from django.conf.urls import url from . import views urlpatterns = [ url(r'^blog/(?P<year>[0-9]{4})/$', views.year_archive, {'foo': 'bar'}), ]
在這個例子中,對於/blog/2005/
請求,Django 將調用views.year_archive(request, year='2005', foo='bar')
。
(4)URL的反向解析
在 Django 項目中常常須要獲取最終形式的 URL,這麼作是爲了在生成的內容中嵌入 URL(視圖和素材資源網址,呈現給用戶的網址,等等), 或者用於在服務器端處理導航流程(重定向等)
此時,必定不能硬編碼 URL(費時、不可伸縮,並且容易出錯), 或者參照 URL 配置創造一種生成 URL 的機制,由於這樣很是容易致使線上 URL 失效。
換句話講,咱們須要的是一個 DRY 機制。 這種機制的一個優勢是,當改進 URL 設計以後無需在項目源碼中大範圍搜索、替換失效的 URL。
咱們能夠得到URL的主要信息是負責處理URL的視圖的標識(例如名稱)。 必須參與正確URL查找的其餘信息片斷是視圖參數的類型(位置,關鍵字)和值。
Django 提供了一種方案,只需在 URL 映射中設計 URL。 咱們爲其提供 URL 配置,而後就能夠雙向使用:
第一種方式是咱們在前面的章節中一直討論的用法。 第二種方式叫作反向解析URL、反向URL匹配、反向URL查詢或者簡單的URL反查。
在須要URL 的地方,對於不一樣層級,Django 提供不一樣的工具用於URL 反查:
url
模板標籤。reverse()
函數。get_absolute_url()
方法。一個視圖函數,簡稱視圖,是一個簡單的Python 函數,它接受Web請求而且返回Web響應。 此響應能夠是網頁的HTML內容,重定向,404錯誤,XML文檔或圖像。 . . 或任何東西,真的。 不管視圖自己包含什麼邏輯,都要返回響應。 代碼寫在哪裏也無所謂,只要它在你的Python目錄下面。 除此以外沒有更多的要求了——能夠說「沒有什麼神奇的地方」。 爲了將代碼放在某處,約定是將視圖放置在項目或應用程序目錄中的名爲views.py
的文件中。
(1)Django的快捷函數
render()
render
(request, template_name, context=None, content_type=None, status=None, using=None)[source]
結合一個給定的模板和一個給定的上下文字典,並返回一個渲染後的 HttpResponse
對象。
Django 不提供返回TemplateResponse
的快捷函數,由於TemplateResponse
的構造與render()
提供的便利是一個層次的。
必需參數
request
template_name
可選參數
context
content_type
DEFAULT_CONTENT_TYPE
設置的值。
status
200
。
using
NAME
。
render_to_response()
render_to_response
(template_name, context=None, content_type=None, status=None, using=None)[source]
此功能在引入render()
以前進行,除了不能使request
可用於響應以外,它的工做方式相似。 不推薦,之後可能會被棄用。
redirect()
redirect
(to, permanent=False, *args, **kwargs)[source]
爲傳遞進來的參數返回HttpResponseRedirect
給正確的URL 。
參數能夠是:
get_absolute_url()
函數reverse()
將用於反向解析名稱。默認狀況下會發出臨時重定向;經過permanent=True
發出永久重定向。
get_object_or_404()
get_object_or_404
(klass, *args, **kwargs)[source]
在一個給定的模型管理器上調用get()
,可是引起Http404
而不是模型的DoesNotExist
異常。
實例
下面的示例從MyModel
中使用主鍵1 來獲取對象:
from django.shortcuts import get_object_or_404 def my_view(request): my_object = get_object_or_404(MyModel, pk=1)
基於類的視圖使用Python 對象實現視圖,它提供除函數視圖以外的另一種方式。 它們不替換基於函數的視圖,但與基於函數的視圖相比具備必定的區別和優點:
GET
,POST
等) 能夠經過單獨的方法而不是條件分支來解決。基於類的視圖的核心是容許你用不一樣的實例方法來響應不一樣的HTTP 請求方法,而不是在一個視圖函數中使用條件分支代碼來實現。
因此,視圖函數中處理HTTP GET
的代碼看上去將像:
from django.http import HttpResponse def my_view(request): if request.method == 'GET': # <view logic> return HttpResponse('result')
在基於類的視圖中,它將變成:
from django.http import HttpResponse from django.views import View class MyView(View): def get(self, request): # <view logic> return HttpResponse('result')
由於Django的URL解析器但願將請求和關聯的參數發送到可調用函數,而不是類,基於類的視圖具備一個as_view()
類方法,它返回一個能夠在請求時調用的函數到達與相關模式匹配的URL。 該函數建立一個類的實例並調用其dispatch()
方法。 GET
查看請求是POST
仍是dispatch
等等,並將請求轉發給相應的方法,若是該方法沒有定義則引起HttpResponseNotAllowed
:
# urls.py from django.conf.urls import url from myapp.views import MyView urlpatterns = [ url(r'^about/$', MyView.as_view()), ]
值得注意的是,方法的返回值與基於函數的視圖的返回值徹底相同,即HttpResponse
的某種形式。 這表示在基於類的視圖中能夠使用http shortcuts和TemplateResponse
對象。
雖然基於類的視圖的最小實現不須要任何類屬性來完成它的功能,可是在許多基於類的設計中類屬性很是重要,有兩種方式來設置類屬性。
第一種方式是Python 標準的方式,子類化並在子類中覆蓋屬性和方法。 因此,若是父類有一個greeting
屬性:
from django.http import HttpResponse from django.views import View class GreetingView(View): greeting = "Good Day" def get(self, request): return HttpResponse(self.greeting)
你能夠在子類中覆蓋它:
class MorningGreetingView(GreetingView): greeting = "Morning to ya"
另一種方式是在URLconf 中用as_view()
調用的關鍵字參數配置類的屬性:
urlpatterns = [ url(r'^about/$', GreetingView.as_view(greeting="good day")), ]
Mixin 是多繼承的一種形式,其來自多個父類的行爲和屬性能夠組合在一塊兒。
例如,在通用的基於類的視圖中,有一個Mixin 叫作 TemplateResponseMixin
,它的主要目的是定義render_to_response()
方法。 它與View
基類的組合是TemplateView
類,這個類能夠調度請求給正確的方法(TemplateResponseMixin
基類中定義的行爲),同時還具備一個render_to_response()
方法,該方法使用template_name
屬性來返回一個TemplateResponse
對象( View
中定義的行爲)。
Mixin 是重用多個類的代碼的一種極好的方法,可是它們須要一些代價。 代碼在Mixin 中越分散,子類將越難閱讀並知道它的行爲;若是你的繼承很深,將難以知道應該覆蓋哪個Mixin 的方法。
還要注意,只能繼承一個通用視圖 —— 也就是說,只能有一個父類繼承View
,其它的父類必須是Mixin。 繼承多個繼承自View
的類 將不能像預期的那樣工做
一個最基本的用於處理表單的視圖函數多是這樣的:
from django.http import HttpResponseRedirect from django.shortcuts import render from .forms import MyForm def myview(request): if request.method == "POST": form = MyForm(request.POST) if form.is_valid(): # <process form cleaned data> return HttpResponseRedirect('/success/') else: form = MyForm(initial={'key': 'value'}) return render(request, 'form_template.html', {'form': form})
相似的一個基於類的視圖看上去是這樣:
from django.http import HttpResponseRedirect from django.shortcuts import render from django.views import View from .forms import MyForm class MyFormView(View): form_class = MyForm initial = {'key': 'value'} template_name = 'form_template.html' def get(self, request, *args, **kwargs): form = self.form_class(initial=self.initial) return render(request, self.template_name, {'form': form}) def post(self, request, *args, **kwargs): form = self.form_class(request.POST) if form.is_valid(): # <process form cleaned data> return HttpResponseRedirect('/success/') return render(request, self.template_name, {'form': form})
這是一個很是簡單的例子,但您能夠看到,您能夠選擇經過覆蓋任何類屬性來定製此視圖,例如。 form_class
,經過URLconf配置,或子類化和覆蓋一個或多個方法(或二者都)!)。
更多-->>http://usyiyi.cn/translate/Django_111/topics/class-based-views/mixins.html
基於類的視圖的擴展不只僅侷限於使用Mixin。 你還能夠使用裝飾器。 因爲基於類的視圖不是函數,對它們的裝飾取決於你使用as_view()
仍是建立一個子類。
(1)在URLconf中進行裝飾
裝飾基於類的視圖的最簡單的方法是裝飾as_view()
方法的結果。 最方便的地方是URLconf 中部署視圖的位置:
from django.contrib.auth.decorators import login_required, permission_required from django.views.generic import TemplateView from .views import VoteView urlpatterns = [ url(r'^about/$', login_required(TemplateView.as_view(template_name="secret.html"))), url(r'^vote/$', permission_required('polls.can_vote')(VoteView.as_view())), ]
這個方法在每一個實例的基礎上運用裝飾器。 若是想讓視圖的每一個實例都被裝飾,你須要一種不一樣的方法。
(2)裝飾類
若要裝飾基於類的視圖的每一個實例,你須要裝飾類自己。 能夠將裝飾器運用到類的dispatch()
方法上來實現這點。
類的方法和獨立的函數不徹底相同,因此你不能夠直接將函數裝飾器運用到方法上 —— 你首先須要將它轉換成一個方法裝飾器。 method_decorator
裝飾器將函數裝飾器轉換成方法裝飾器,這樣它就能夠用於實例方法上。 像這樣:
from django.contrib.auth.decorators import login_required from django.utils.decorators import method_decorator from django.views.generic import TemplateView class ProtectedView(TemplateView): template_name = 'secret.html' @method_decorator(login_required) def dispatch(self, *args, **kwargs): return super(ProtectedView, self).dispatch(*args, **kwargs)
或者,更簡潔的是,您能夠裝飾類,並將要裝飾的方法的名稱做爲關鍵字參數name
傳遞:
@method_decorator(login_required, name='dispatch') class ProtectedView(TemplateView): template_name = 'secret.html'
若是您在幾個地方使用了一組經常使用的裝飾器,您能夠定義一個列表或元組的裝飾器,並使用它,而不是屢次調用method_decorator()
。 這兩個類是至關的:
decorators = [never_cache, login_required] @method_decorator(decorators, name='dispatch') class ProtectedView(TemplateView): template_name = 'secret.html' @method_decorator(never_cache, name='dispatch') @method_decorator(login_required, name='dispatch') class ProtectedView(TemplateView): template_name = 'secret.html'
裝飾器將按照傳遞給裝飾器的順序處理請求。 在這個例子中,never_cache()
將在login_required()
以前處理請求。
中間件是一個鉤子框架,它們能夠介入Django 的請求和響應處理過程。 它是一個輕量級、底層的「插件」系統,用於在全局修改Django 的輸入或輸出。
每一箇中間件組件負責完成某個特定的功能。 例如,Django 包含的一箇中間件組件AuthenticationMiddleware
,它使用會話將用戶和請求關聯起來。
(1)編寫本身的中間件
一箇中間件工廠是一個可調用的,它採用一個get_response
可調用並返回一箇中間件。 中間件是一個可調用的函數,它接受請求並返回響應,就像視圖同樣。
中間件能夠寫成一個以下所示的功能:
def simple_middleware(get_response): # 一次性配置和初始化。 def middleware(request): # 在調用視圖(以及稍後的中間件)以前 # 要爲每一個請求執行代碼。 response = get_response(request) # 爲每一個請求/響應執行的代碼 # 在調用視圖以後 return response return middleware
或者它能夠寫成一個類,其實例是可調用的,以下所示:
class SimpleMiddleware(object): def __init__(self, get_response): self.get_response = get_response # 一次性配置和初始化。 def __call__(self, request): # Code to be executed for each request before # the view (and later middleware) are called. response = self.get_response(request) # Code to be executed for each request/response after # the view is called. return response
__init__(get_response)
中間件工廠必須接受get_response
參數。 您也能夠初始化中間件的全局狀態。 記住幾個注意事項:
get_response
參數初始化您的中間件,所以您不能將__init__()
定義爲須要任何其餘參數。__call__()
方法不一樣,當Web服務器啓動時,__init__()
僅被調用一次。(2)激活中間件
要激活中間件組件,請將其添加到Django設置中的MIDDLEWARE
列表中。
在MIDDLEWARE
中,每一箇中間件組件由一個字符串表示:完整的Python路徑到中間件工廠的類或函數名稱。 例如,使用 django-admin startproject
建立工程的時候生成的默認值:
MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ]
(3)中間件順序和分層
在請求階段,在調用視圖以前,Django以MIDDLEWARE
(自上而下)定義的順序應用中間件。
你能夠像洋蔥同樣想起來:每一箇中間件類都是一個「層」,它覆蓋了洋蔥核心的視圖。 若是請求經過洋蔥的全部層(每一個調用get_response
將請求傳遞到下一層),一直到核心的視圖,響應將經過在每一層(以相反的順序)的路上退出。
若是其中一個層決定短路並返回響應而不調用其get_response
,那麼該層(包括視圖)內的洋蔥層都不會看到請求或響應。 響應將只返回經過請求傳遞的相同的層。
(4)其它中間件鉤子
除了前面描述的基本請求/響應中間件模式,您還能夠向基於類的中間件添加三種其餘特殊方法:
process_view()
process_view
(request,view_func,view_args,view_kwargs)
request
是一個HttpRequest
對象。 view_func
是 Django會調用的一個Python的函數。 (它是一個真實的函數對象,不是函數的字符名稱。) view_args
是一個會被傳遞到視圖的位置參數列表,而view_kwargs
是一個會被傳遞到視圖的關鍵字參數字典。 view_args
和 view_kwargs
都不包括第一個視圖參數(request
)。
process_view()
會在Django 調用視圖以前被調用。
它應該返回一個None
或一個HttpResponse
對象。 若是返回None
,Django 將會繼續處理這個請求,執行其它的process_view()
中間件,而後調用對應的視圖。 若是它返回一個HttpResponse
對象,Django不會打擾調用相應的視圖;它將應用響應中間件到HttpResponse
並返回結果。
process_exception()
process_exception
(request,exception)
request
是一個HttpRequest
對象。 Exception
是一個被視圖中的方法拋出來的 exception
對象。
當一個視圖拋出異常時,Django會調用process_exception()
來處理。 None
應該返回一個process_exception()
或者一個HttpResponse
對象。 若是它返回一個HttpResponse
對象,則將應用模板響應和響應中間件,並將生成的響應返回給瀏覽器。 不然,default exception handling開始。
再次提醒,在處理響應期間,中間件的執行順序是倒序執行的,這包括process_exception
。 若是異常中間件返回響應,那麼中間件上面的中間件類的process_exception
方法根本就不會被調用。
process_template_response()
process_template_response
(請求,響應)
request
是一個HttpRequest
對象。 response
是一個TemplateResponse
對象(或等價的對象),由Django視圖或者中間件返回。
若是響應的實例有render()
方法,process_template_response()
在視圖恰好執行完畢以後被調用,這代表了它是一個TemplateResponse
對象(或等價的對象)。
這個方法必須返回一個實現了render
方法的響應對象。 它能夠修改給定的response.template_name
對象,經過修改 response
和response.context_data
或者它能夠建立一個全新的 TemplateResponse
或等價的對象。
你不須要顯式渲染響應 —— 一旦全部的模板響應中間件被調用,響應會自動被渲染。
在一個響應的處理期間,中間件以相反的順序運行,這包括process_template_response()
。
TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates')] , 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ]
BACKEND
是一個指向實現了Django模板後端API的模板引擎類的帶點的Python路徑。 內置的後端有 django.template.backends.django.DjangoTemplates
和 django.template.backends.jinja2.Jinja2
。
因爲絕大多數引擎都是從文件加載模板的,因此每種模板引擎都包含兩項通用設置:
DIRS
定義了一個目錄列表,模板引擎按列表順序搜索這些目錄以查找模板源文件。APP_DIRS
告訴模板引擎是否應該進入每一個已安裝的應用中查找模板。 每種模板引擎後端都定義了一個慣用的名稱做爲應用內部存放模板的子目錄名稱。(譯者注:例如django爲它本身的模板引擎指定的是 ‘templates’ ,爲jinja2指定的名字是‘jinja2’)特別的是,django容許你有多個模板引擎後臺實例,且每一個實例有不一樣的配置選項。 在這種狀況下你必須爲每一個配置指定一個惟一的NAME
.
OPTIONS
中包含了具體的backend設置
模板
模版是純文本文件。 它能夠生成任何基於文本的格式(HTML,XML,CSV等))。
模版包括在使用時會被值替換掉的 變量,和控制模版邏輯的 標籤。
下面是一個小模版,它說明了一些基本的元素。 後面的文檔中會解釋每一個元素。
{% extends "base_generic.html" %} {% block title %}{{ section.title }}{% endblock %} {% block content %} <h1>{{ section.title }}</h1> {% for story in story_list %} <h2> <a href="{{ story.get_absolute_url }}"> {{ story.headline|upper }} </a> </h2> <p>{{ story.tease|truncatewords:"100" }}</p> {% endfor %} {% endblock %}
變量
變量看起來就像是這樣: {{ variable }}
。 當模版引擎遇到一個變量,它將計算這個變量,而後用結果替換掉它自己。 變量的命名包括任何字母數字以及下劃線 ("_"
)的組合。 點("."
) 也在會變量部分中出現,不過它有特殊的含義,咱們將在後面說明。 重要的, 變量名稱中不能有空格或標點符號。在前文的例子中, {{ section.title }}
將被替換爲 title
對象的 section
屬性。
過濾器
您能夠經過使用 過濾器來改變變量的顯示。
過濾器看起來是這樣的:{{ name|lower }}
。 這將在變量 {{ name }}
被過濾器 lower
過濾後再顯示它的值,該過濾器將文本轉換成小寫。 使用管道符號 (|
)來應用過濾器。
過濾器能夠「連接」。一個過濾器的輸出應用於下一個過濾器。 {{ text|escape|linebreaks }}
就是一個經常使用的過濾器鏈,它編碼文本內容,而後把行打破轉成<p>
標籤。
一些過濾器帶有參數。 過濾器的參數看起來像是這樣: {{ bio|truncatewords:30 }}
。 這將顯示 bio
變量的前30個詞。
過濾器參數包含空格的話,必須被引號包起來;例如,使用逗號和空格去鏈接一個列表中的元素,你須要使用 {{ list|join:", " }}
。
經常使用模板過濾器
默認
若是一個變量是false或者爲空,使用給定的默認值。 不然,使用變量的值。 像這樣:
{{ value|default:"nothing" }}
若是 value
沒有被提供,或者爲空, 上面的例子將顯示「nothing
」。
長度
返回值的長度。 它對字符串和列表都起做用。 像這樣:
{{ value|length }}
若是 value
是 ['a', 'b', 'c', 'd']
,那麼輸出是 4
。
filesizeformat
格式化爲「人類可讀」文件大小(即'13 KB'
,t4> MB'
,'102 bytes'
等)。 像這樣:
{{ value|filesizeformat }}
若是 value
是 123456789,輸出將會是 117.7 MB
。
更多-->>http://usyiyi.cn/translate/Django_111/ref/templates/builtins.html#ref-templates-builtins-filters
標籤
標籤看起來像是這樣的: {% tag %}
。 標籤比變量複雜得多:有些用於在輸出中建立文本,有些用於控制循環或邏輯,有些用於加載外部信息到模板中供之後的變量使用。
一些標籤須要開始和結束標籤(即 {% 標籤 %} ... 標籤 內容 ... {% ENDTAG %}
)。
經常使用標籤,更多-->>http://usyiyi.cn/translate/Django_111/ref/templates/builtins.html#ref-templates-builtins-tags
對於循環數組中的每一個元素。 例如,顯示 athlete_list
中提供的運動員列表:
for
<ul> {% for athlete in athlete_list %} <li>{{ athlete.name }}</li> {% endfor %} </ul>
變量 描述
forloop.counter 循環的當前迭代(1索引)
forloop.counter0 循環的當前迭代(0索引)
forloop.revcounter 循環結束的迭代次數(1索引)
forloop.revcounter0 循環結束的迭代次數(0索引)
forloop.first 若是這是第一次經過循環,則爲真
forloop.last 若是這是最後一次循環,則爲真
forloop.parentloop 對於嵌套循環,這是圍繞當前循環的循環
if,elif和else
計算一個變量,而且當變量是「true」時,顯示塊中的內容:
{% if athlete_list %} Number of athletes: {{ athlete_list|length }} {% elif athlete_in_locker_room_list %} Athletes should be out of the locker room soon! {% else %} No athletes. {% endif %}
在上面的例子中,若是 athlete_list
不是空的,運動員的數量將顯示爲 {{ athlete_list|length }}
的輸出。 不然,若是athlete_in_locker_room_list
不爲空,將顯示「運動員應該出...」。 若是兩個列表都是空的,將顯示 「No athletes.」 。
block
block標籤能夠被子模板覆蓋.
comment
在 {% comment %}
和 {% endcomment %}
,之間的內容會被忽略,做爲註釋。 在第一個標籤能夠插入一個可選的記錄。 好比,當要註釋掉一些代碼時,能夠用此來記錄代碼被註釋掉的緣由。
例如:
<p>Rendered text with {{ pub_date|date:"c" }}</p> {% comment "Optional note" %} <p>Commented out text with {{ create_date|date:"c" }}</p> {% endcomment %}
comment
標籤不能嵌套使用。
csrf_token
這個標籤用於跨站請求僞造保護
extends
表示當前模板繼承自一個父模板
註釋
要註釋模版中一行的部份內容,使用註釋語法 {# #}
.
例如,這個模版將被渲染爲 'hello'
:
{# greeting #}hello
Django模版引擎中最強大也是最複雜的部分就是模版繼承了。 模版繼承可讓您建立一個基本的「骨架」模版,它包含您站點中的所有元素,而且能夠定義可以被子模版覆蓋的 blocks 。
經過從下面這個例子開始,能夠容易的理解模版繼承:
<!DOCTYPE html> <html lang="en"> <head> <link rel="stylesheet" href="style.css" /> <title>{% block title %}My amazing site{% endblock %}</title> </head> <body> <div id="sidebar"> {% block sidebar %} <ul> <li><a href="/">Home</a></li> <li><a href="/blog/">Blog</a></li> </ul> {% endblock %} </div> <div id="content"> {% block content %}{% endblock %} </div> </body> </html>
這個模版,咱們把它叫做 base.html
, 它定義了一個能夠用於兩列排版頁面的簡單HTML骨架。 「子模版」的工做是用它們的內容填充空的blocks。
在這個例子中, block
標籤訂義了三個能夠被子模版內容填充的block。 block
告訴模版引擎: 子模版可能會覆蓋掉模版中的這些位置。
子模版可能看起來是這樣的:
{% extends "base.html" %} {% block title %}My amazing blog{% endblock %} {% block content %} {% for entry in blog_entries %} <h2>{{ entry.title }}</h2> <p>{{ entry.body }}</p> {% endfor %} {% endblock %}
extends
標籤是這裏的關鍵。 它告訴模版引擎,這個模版「繼承」了另外一個模版。 當模版系統處理這個模版時,首先,它將定位父模版——在此例中,就是「base.html」。
那時,模版引擎將注意到 base.html
中的三個 block
標籤,並用子模版中的內容來替換這些block。
請注意,子模版並無定義 sidebar
block,因此係統使用了父模版中的值。 父模版的 {% block %}
標籤中的內容老是被用做備選內容(fallback)。
您能夠根據須要使用多級繼承。 使用繼承的一個經常使用方式是相似下面的三級結構:
base.html
模版來控制您整個站點的主要視覺和體驗。base_SECTIONNAME.html
模版。 例如, base_news.html
, base_sports.html
。 這些模版都繼承自 base.html
,而且包含了每部分特有的樣式和設計。這種方式使代碼獲得最大程度的複用,而且使得添加內容到共享的內容區域更加簡單,例如分支範圍內的導航。
這裏是使用繼承的一些提示:
若是你在模版中使用 {% extends %}
標籤,它必須是模版中的第一個標籤。 其餘的任何狀況下,模版繼承都將沒法工做。
在base模版中設置越多的 {% block %}
標籤越好。 請記住,子模版沒必要定義所有父模版中的blocks,因此,你能夠在大多數blocks中填充合理的默認內容,而後,只定義你須要的那一個。 多一點鉤子總比少一點好。
若是你發現你本身在大量的模版中複製內容,那可能意味着你應該把內容移動到父模版中的一個 {% block %}
中。
若是須要獲取父模板中的block 的內容,能夠使用{{ block.super }}
變量。 若是你想要在父block 中新增內容而不是徹底覆蓋它,它將很是有用。 使用{{ block.super }}
插入的數據不會被自動轉義,由於父模板中的內容已經被轉義。
在{% block %}
以外建立的變量使用模板標籤as
語法不能在塊內使用。 例如,此模板不會顯示任何內容:
最後,請注意不能在一個模版中定義多個相同名字的block
標籤。 這個限制的存在是由於block標籤的做用是「雙向」的。 這個意思是,block 標籤不只提供了一個坑去填,它定義向父模版的坑中所填的內容。 若是在一個模版中有兩個名字同樣的 block
標籤,模版的父模版將不知道使用哪一個block的內容。
include
加載模板並以標籤內的參數渲染。 這是一種能夠引入別的模板的方法。
模板名能夠是變量或者是硬編碼的字符串,能夠用單引號也能夠是雙引號.
下面這個示例包括模板"foo/bar.html"
的內容:
{% include "foo/bar.html" %}
load
加載自定義模板標籤集。
舉個例子, 下面這模板將會從package
包中載入全部otherlibrary
和somelibrary
中已經註冊的標籤和過濾器:
{% load somelibrary package.otherlibrary %}
當從模版中生成HTML時,總會有這樣一個風險:值可能會包含影響HTML最終呈現的字符。 例如,思考這個模版片斷:
Hello, {{ name }}
首先,它看起來像是一個無害的方式來顯示用戶的名字,可是設想一下,若是用戶像下面這樣輸入他的名字,會發生什麼:
<script>alert('hello')</script>
...這意味着瀏覽器會彈出一個JavaScript警報框!
顯然,用戶提交的數據都被不該該被盲目的信任,而且被直接插入到你的網頁中,由於一個懷有惡意的用戶可能會使用這樣的漏洞來作一些可能的壞事。 這種類型的安全問題被叫作 跨站腳本(Cross Site Scripting) (XSS) 攻擊。
爲避免這個問題,你有兩個選擇:
escape
過濾器(下面的文檔中將提到),它將把潛在的有害HTML 字符轉換成無害的。 在Django 最初的幾年裏,這是默認的解決方案,但問題是它將責任放在大家這些開發人員/模板做者身上,以確保轉義了全部內容。 並且很容易忘記轉義數據。默認狀況下,Django 中的每一個模板會自動轉義每一個變量的輸出。 明確地說,下面五個字符被轉義:
<
會轉換爲<
>
會轉換爲>
'
(單引號)轉換爲'
"
(雙引號)會轉換爲 "
&
會轉換爲 &
咱們要再次強調這個行爲是默認打開的。 若是你使用Django的模板系統,會處於保護之下。
如何關閉
若是你不但願數據自動轉義,不管是在站點、模板仍是變量級別,你能夠使用幾種方法來關閉它。
然而你爲何想要關閉它呢? 因爲有時,模板變量含有一些你打算渲染成原始HTML的數據,你並不想轉義這些內容。 例如,你可能會在數據庫中儲存一些HTML代碼,而且直接在模板中嵌入它們。 或者,你可能使用Django的模板系統來生成不是HTML的文本 -- 好比郵件信息。
對於單個變量
使用safe
過濾器來關閉獨立變量上的自動轉義:
This will be escaped: {{ data }}
This will not be escaped: {{ data|safe }}
對於模板庫
{% autoescape off %}
Hello {{ name }}
{% endautoescape %}
autoescape
標籤接受on
或者 off
做爲它的參數。 有時你可能想在自動轉義關閉的狀況下強制使用它。 下面是一個模板的示例
Auto-escaping is on by default. Hello {{ name }}
{% autoescape off %}
This will not be auto-escaped: {{ data }}.
Nor this: {{ other_data }}
{% autoescape on %}
Auto-escaping applies again: {{ name }}
{% endautoescape %}
{% endautoescape %}
自動轉義標籤做用於擴展了當前模板的模板,以及經過 include
標籤包含的模板,就像全部block標籤那樣。 像這樣:
{% autoescape off %} <h1>{% block title %}{% endblock %}</h1> {% block content %} {% endblock %} {% endautoescape %}
指定自定義模板標籤和過濾器的最多見的地方在Django應用程序中。 若是它們與現有的應用程序相關聯,則將它們捆綁在一塊兒是有意義的;不然,它們能夠添加到新的應用程序。 當將Django應用程序添加到INSTALLED_APPS
中時,在下面描述的常規位置中定義的任何標籤將自動在模板中加載。
這個應用應該包含一個templatetags
目錄,和views.py
、models.py
等文件處於同一級別目錄下。 若是目錄不存在則建立它——不要忘記建立__init__.py
文件以使得該目錄能夠做爲Python 的包。
你的自定義的標籤和過濾器將放在templatetags
目錄下的一個模塊裏。 這個模塊的名字是你稍後將要載入標籤時使用的,因此要謹慎的選擇名字以防與其餘應用下的自定義標籤和過濾器名字衝突。
例如,你的自定義標籤/過濾器在一個名爲poll_extras.py
的文件中,那麼你的app目錄結構看起來應該是這樣的:
polls/
__init__.py
models.py
templatetags/
__init__.py
poll_extras.py
views.py
而後你能夠在模板中像以下這樣使用:
{% load poll_extras %}
(1)編寫自定義過濾器
自定義過濾器就是一個帶有一個或兩個參數的Python 函數:
例如,在{{ var|foo:"bar" }}
中,foo
過濾器應當傳入變量var
和參數 "bar"
。
因爲模板語言沒有提供異常處理,任何從過濾器中拋出的異常都將會顯示爲服務器錯誤。 所以,若是有合理的值能夠返回,過濾器應該避免拋出異常。 在模板中有一個明顯錯誤的狀況下,引起一個異常可能仍然要好於用靜默的失敗來掩蓋錯誤。
這是一個定義過濾器的例子:
def cut(value, arg):
"""Removes all values of arg from the given string"""
return value.replace(arg, '')
下面是這個過濾器應該如何使用:
{{ somevariable|cut:"0" }}
大多數過濾器沒有參數。 在這種狀況下,你的函數不帶這個參數便可。 例如:
def lower(value): # Only one argument.
"""Converts a string into all lowercase"""
return value.lower()
(2)註冊自定義過濾器
django.template.Library.
filter()
一旦你寫好了你的自定義過濾器函數,你就開始須要把它註冊爲你的 Library
實例,來讓它在Django模板語言中可用:
register.filter('cut', cut)
register.filter('lower', lower)
Library.filter()
方法須要兩個參數:
你還能夠把register.filter()
用做裝飾器:
@register.filter(name='cut') def cut(value, arg): return value.replace(arg, '') @register.filter def lower(value): return value.lower()
更多-->>http://usyiyi.cn/translate/Django_111/howto/custom-template-tags.html
(3)編寫自定義模板標籤
標籤比過濾器更復雜,由於標籤能夠作任何事情。
簡單標籤
django.template.Library.
simple_tag()
許多模板標籤須要許多參數 - 字符串或模板變量,而且僅在基於輸入參數和一些外部信息進行一些處理後返回結果。 例如,current_time
標籤可能接受一個格式字符串,並返回與之對應的格式化後的時間。
爲了簡單化這些類型標籤的建立,Django 提供一個輔助函數simple_tag
。 這個函數是django.template.Library
的一個方法,接受一個任意數目的參數的函數,將其包裝在一個render
函數和上面提到的其餘必要部分中,並在模板系統中註冊它。
咱們的current_time
函數從而能夠這樣寫
import datetime from django import template register = template.Library() @register.simple_tag def current_time(format_string): return datetime.datetime.now().strftime(format_string)
關於simple_tag
輔助函數幾件值得注意的事項︰
和其餘標籤程序不一樣, 若是模板上下文中開啓了自動轉義模式 simple_tag
的輸出將經過conditional_escape()
轉義, 來保證正確的HTML和防護XSS漏洞.
若是不須要額外的轉義,您將須要使用mark_safe()
,若是您絕對確保您的代碼不包含XSS漏洞。 要創建小型HTML片斷,強烈建議您使用format_html()
而不是mark_safe()
。
若是你的模板標籤須要訪問當前上下文,你能夠在註冊標籤時使用takes_context
參數︰
@register.simple_tag(takes_context=True) def current_time(context, format_string): timezone = context['timezone'] return your_get_current_time_method(timezone, format_string)
GET和POST
處理表單時候只會用到POST
和 GET
方法。
Django 的登陸表單使用POST
方法,在這個方法中瀏覽器組合表單數據、對它們進行編碼以用於傳輸、將它們發送到服務器而後接收它的響應。
相反,GET
組合提交的數據爲一個字符串,而後使用它來生成一個URL。 這個URL 將包含數據發送的地址以及數據的鍵和值。 若是你在Django 文檔中作一次搜索,你會當即看到這點,此時將生成一個https://docs.djangoproject.com/search/?q=forms&release=1
形式的URL。
POST
和GET
用於不一樣的目的。
用於改變系統狀態的請求 —— 例如,給數據庫帶來變化的請求 —— 應該使用POST
。 GET
只應該用於不會影響系統狀態的請求。
GET
還不適合密碼錶單,由於密碼將出如今URL 中,以及瀏覽器的歷史和服務器的日誌中,並且都是以普通的文本格式。 它還不適合數據量大的表單和二進制數據,例如一張圖片。 使用GET
請求做爲管理站點的表單具備安全隱患:攻擊者很容易模擬表單請求來取得系統的敏感數據。 POST
,若是與其它的保護措施結合將對訪問提供更多的控制,例如Django 的CSRF protection。
另外一個方面,GET
適合網頁搜索這樣的表單,由於這種表示一個GET
請求的URL 能夠很容易地做爲書籤、分享和從新提交。
Django在表單中的角色
處理表單是一件很複雜的事情。 考慮一下Django 的Admin 站點,不一樣類型的大量數據項須要在一個表單中準備好、渲染成HTML、使用一個方便的界面編輯、返回給服務器、驗證並清除,而後保存或者向後繼續處理。
Django 的表單功能能夠簡化並自動化大部分這些工做,並且還能夠比大部分程序員本身所編寫的代碼更安全。
Django 會處理表單工做中的三個顯著不一樣的部分:
能夠手工編寫代碼來實現,可是Django 能夠幫你完成全部這些工做。
Django的Form類
表單系統的核心部分是Django 的Form
類。 Django 的模型描述一個對象的邏輯結構、行爲以及展示給咱們的方式,與此相似,Form
類描述一個表單並決定它如何工做和展示。
就像模型類的屬性映射到數據庫的字段同樣,表單類的字段會映射到HTML 的<input>
表單的元素。 (ModelForm
經過一個Form
映射模型類的字段到HTML 表單的<input>
元素;Django 的Admin 站點就是基於這個)。
一個表單的字段自己就是類;他們管理表單數據,並在提交表單時執行驗證。 DateField
和FileField
處理的數據類型差異很大,必須完成不一樣的事情。
表單字段在瀏覽器中呈現給用戶的是一個HTML 的「widget」 —— 用戶界面的一個片斷。 每一個字段類型都有一個合適的默認Widget class,須要時能夠覆蓋。
實例化、處理和渲染表單
在Django 中渲染一個對象時,咱們一般:
除了幾個關鍵點不一樣以外,在模板中渲染表單和渲染其它類型的對象幾乎同樣。
在模型實例不包含數據的狀況下,在模板中對它作處理不多有什麼用處。 可是渲染一個未填充的表單卻很是有意義 —— 咱們但願用戶去填充它。
因此當咱們在視圖中處理模型實例時,咱們通常從數據庫中獲取它。 當咱們處理表單時,咱們通常在視圖中實例化它。
當咱們實例化表單時,咱們能夠選擇讓它爲空仍是預先填充它,例如使用:
獲取HTML表單數據是最有趣的,由於這樣作可讓用戶不只能夠閱讀網站,還能夠將信息發送回來。
假設您想在您的網站上建立一個簡單的表單,以獲取用戶的名字。 你須要相似這樣的模板:
<form action="/your-name/" method="post"> <label for="your_name">Your name: </label> <input id="your_name" type="text" name="your_name" value="{{ current_name }}"> <input type="submit" value="OK"> </form>
這告訴瀏覽器使用POST
方法將表單數據返回到URL / your-name /
。 它將顯示一個標籤爲"Your name:"的文本字段,和一個"OK"按鈕。 若是模板上下文包含current_name
變量,則將用於預填your_name
字段。
您將須要一個視圖來渲染包含HTML表單的模板,而且能夠根據須要提供current_name
字段。
當表單提交時,發往服務器的POST
請求將包含表單數據。
如今你還須要一個對應/your-name/
URL 的視圖,它在請求中找到正確的鍵/值對,而後處理它們。
這是一個很是簡單的表單。 實際應用中,一個表單可能包含幾十上百個字段,其中大部分須要預填充,並且咱們預料到用戶未來回編輯-提交幾回才能完成操做。
即便在提交表單以前,咱們也可能須要在瀏覽器中進行一些驗證。咱們可能想要使用更復雜的字段,這樣可讓用戶作一些事情,例如從日曆中選擇日期等等。
這個時候,讓Django 來爲咱們完成大部分工做是很容易的。
(1)form.py
咱們已經計劃好了咱們的 HTML 表單應該呈現的樣子。 在Django 中,咱們的起始點是這裏:
from django import forms class NameForm(forms.Form): your_name = forms.CharField(label='Your name', max_length=100)
它定義一個Form
類,只帶有一個字段(your_name
)。 咱們已經對這個字段使用一我的性化的標籤,當渲染時它將出如今<label>
中(在這個例子中,即便咱們省略它,咱們指定的label
仍是會自動生成)。
字段容許的最大長度經過max_length
定義。 它完成兩件事情。 首先,它在HTML 的<input>
上放置一個maxlength="100"
(這樣瀏覽器將在第一時間阻止用戶輸入多於這個數目的字符)。 它還意味着當Django 收到瀏覽器發送過來的表單時,它將驗證數據的長度。
Form
的實例具備一個is_valid()
方法,它爲全部的字段運行驗證的程序。 當調用這個方法時,若是全部的字段都包含合法的數據,它將:
True
cleaned_data
屬性中。完整的表單,第一次渲染時,看上去將像:
<label for="your_name">Your name: </label> <input id="your_name" type="text" name="your_name" maxlength="100" required />
注意它不包含 <form>
標籤和提交按鈕。 咱們必須本身在模板中提供它們。
(2)視圖
發送回Django網站的表單數據由視圖處理,一般是發佈表單的相同視圖。 這容許咱們重用一些相同的邏輯。
要操做一個經過URL發佈的表單,咱們要在視圖中實例化它。
from django.shortcuts import render from django.http import HttpResponseRedirect from .forms import NameForm def get_name(request): # 若是這是一個POST請求,咱們就須要處理表單數據 if request.method == 'POST': # 建立一個表單實例,而且使用表單數據填充request請求: form = NameForm(request.POST) # 檢查數據有效性: if form.is_valid(): # 在須要時,能夠在form.cleaned_date中處理數據 # ... # 重定向到一個新的URL: return HttpResponseRedirect('/thanks/') # 若是是GET或者其它請求方法,咱們將建立一個空的表單。 else: form = NameForm() return render(request, 'name.html', {'form': form})
若是訪問視圖的是一個GET
請求,它將建立一個空的表單實例並將它放置到要渲染的模板的上下文中。 這是咱們在第一次訪問該URL 時預期發生的狀況。
若是使用POST
請求提交表單,該視圖將再次建立一個表單實例,並使用請求中的數據填充表單:形式 = NameForm(request.POST)
這被稱爲「將數據綁定到表單」(如今是綁定的形式)。
咱們調用窗體的is_valid()
方法;若是不是True
,咱們返回到表單的模板。 這時表單再也不爲空(未綁定),因此HTML 表單將用以前提交的數據填充,而後能夠根據要求編輯並改正它。
若是True
爲is_valid()
,咱們將可以在cleaned_data
屬性中找到全部合法的表單數據。 在發送HTTP 重定向給瀏覽器告訴它下一步的去向以前,咱們能夠用這個數據來更新數據庫或者作其它處理。
(3)模板
咱們不須要在name.html
模板中作不少工做。 最簡單的例子是:
<form action="/your-name/" method="post"> {% csrf_token %} {{ form }} <input type="submit" value="Submit" /> </form>
Django會根據模型類的字段和屬性,在HTML中自動生成對應表單標籤和標籤屬性。生成的標籤會被放置到{{ form }}
所在的位置。
如今咱們有了一個能夠工做的網頁表單,它經過Django Form
描述、經過視圖處理並渲染成一個HTML <form>
。
這是你入門所須要知道的全部內容,可是表單框架爲了便利提供了更多的內容。 一旦你理解了上面描述的基本處理過程,你應該能夠理解表單系統的其它功能並準備好學習更多的底層機制。
更多字段
考慮一個比咱們上面的最小例子更有用的形式,咱們能夠用它來在我的網站上實現「聯繫我」功能:
from django import forms class ContactForm(forms.Form): subject = forms.CharField(max_length=100) message = forms.CharField(widget=forms.Textarea) sender = forms.EmailField() cc_myself = forms.BooleanField(required=False)
咱們前面的表單只使用一個字段your_name
,它是一個CharField
。 在這個例子中,咱們的表單具備四個字段:message
、subject
、sender
和cc_myself
。 CharField
,EmailField
和BooleanField
只是三種可用的字段類型
窗口小部件
每一個表單字段都有一個對應的Widget class,它對應一個HTML 表單Widget,例如<input type="text">
。
在大部分狀況下,字段都具備一個合理的默認Widget。 例如,默認狀況下,CharField
具備一個TextInput
Widget,它在HTML 中生成一個<input type="text">
。 若是你須要message
,在定義表單字段時你應該指定一個合適的Widget,例如咱們定義的<textarea>
字段。
字段數據
無論表單提交的是什麼數據,一旦經過調用is_valid()
成功驗證(is_valid()
返回True
),驗證後的表單數據將位於form.cleaned_data
字典中。 這些數據已經爲你轉換好爲Python 的類型。
在上面的聯繫表單示例中,cc_myself
將是一個布爾值。 相似地,IntegerField
和FloatField
字段分別將值轉換爲Python 的int
和float
。
下面是在視圖中如何處理表單數據:
from django.core.mail import send_mail if form.is_valid(): subject = form.cleaned_data['subject'] message = form.cleaned_data['message'] sender = form.cleaned_data['sender'] cc_myself = form.cleaned_data['cc_myself'] recipients = ['info@example.com'] if cc_myself: recipients.append(sender) send_mail(subject, message, sender, recipients) return HttpResponseRedirect('/thanks/')
(1)綁定和綁定形式
Form
要麼是綁定的,要麼是未綁定的。
若要建立一個未綁定的Form
實例,只需簡單地實例化該類:
>>> f = ContactForm()
若要綁定數據到表單,能夠將數據以字典的形式傳遞給Form
類的構造函數的第一個參數:
>>> data = {'subject': 'hello', ... 'message': 'Hi there', ... 'sender': 'foo@example.com', ... 'cc_myself': True} >>> f = ContactForm(data)
>>> f.is_bound
True
若是你有一個綁定的Form
實例可是想改下數據,或者你想綁定一個未綁定的Form
表單到某些數據,你須要建立另一個Form
實例。 Form
實例的數據沒有辦法修改。 Form
實例一旦建立,你應該將它的數據視爲不可變的,不管它有沒有數據。
(2)使用表單驗證數據
讓咱們試下非法的數據。 下面的情形中,subject
爲空(默認全部字段都是必需的)且sender
是一個不合法的郵件地址:
>>> data = {'subject': '', ... 'message': 'Hi there', ... 'sender': 'invalid email address', ... 'cc_myself': True} >>> f = ContactForm(data) >>> f.is_valid() False
Form.
errors
訪問errors
屬性能夠得到錯誤信息的一個字典:
>>> f.errors {'sender': ['Enter a valid email address.'], 'subject': ['This field is required.']}
Form.errors.
as_data
()
返回一個dict
,它映射字段到原始的ValidationError
實例。
>>> f.errors.as_data() {'sender': [ValidationError(['Enter a valid email address.'])], 'subject': [ValidationError(['This field is required.'])]}
Form.errors.
as_json
(escape_html=False)
返回JSON 序列化後的錯誤。
>>> f.errors.as_json() {"sender": [{"message": "Enter a valid email address.", "code": "invalid"}], "subject": [{"message": "This field is required.", "code": "required"}]}
(3)訪問乾淨數據
Form.
cleaned_data
Form
類中的每一個字段不只負責驗證數據,還負責「清潔」它們 —— 將它們轉換爲正確的格式。 這是個很是好用的功能,由於它容許字段以多種方式輸入數據,並總能獲得一致的輸出。
例如,DateField
將輸入轉換爲Python 的 datetime.date
對象。 不管你傳遞的是DateField
格式的字符串、datetime.date
對象、仍是其它格式的數字,'1994-07-15'
將始終將它們轉換成datetime.date
對象,只要它們是合法的。
一旦你建立一個Form
實例並經過驗證後,你就能夠經過它的cleaned_data
屬性訪問清潔的數據:
>>> data = {'subject': 'hello', ... 'message': 'Hi there', ... 'sender': 'foo@example.com', ... 'cc_myself': True} >>> f = ContactForm(data) >>> f.is_valid() True >>> f.cleaned_data {'cc_myself': True, 'message': 'Hi there', 'sender': 'foo@example.com', 'subject': 'hello'}
(4)輸出表單爲HTML
as_p()
Form.
as_p
()
<p>
渲染表單爲一系列的<p>
標籤,每一個as_p()
標籤包含一個字段:
>>> f = ContactForm() >>> f.as_p() '<p><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" required /></p>\n<p><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" required /></p>\n<p><label for="id_sender">Sender:</label> <input type="text" name="sender" id="id_sender" required /></p>\n<p><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself" /></p>' >>> print(f.as_p()) <p><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" required /></p> <p><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" required /></p> <p><label for="id_sender">Sender:</label> <input type="email" name="sender" id="id_sender" required /></p> <p><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself" /></p>
as_ul()
Form.
as_ul
()
<li>
渲染表單爲一系列的<li>
標籤,每一個as_ul()
標籤包含一個字段。 它不包含</ul>
和<ul>
,因此你能夠本身指定<ul>
的任何HTML 屬性:
as_table()
Form.
as_table T0>()
最後,as_table()
輸出表單爲一個HTML <table>
。 它與print
徹底相同。 事實上,當你print
一個表單對象時,在後臺調用的就是as_table()
方法:
Field
(**kwargs)[source]
建立一個Form
類時,最重要的部分是定義表單的字段。 每一個字段均可以有自定義的驗證邏輯,以及一些其它的鉤子。
Field.
clean
(value)[source]
雖然Field
類主要使用在Form
類中,但你也能夠直接實例化它們來使用,以便更好地瞭解它們是如何工做的。 每一個django.forms.ValidationError
實例都有一個clean()
方法, 它接受一個參數,而後返回「清潔的」數據或者拋出一個Field
異常:
>>> from django import forms >>> f = forms.EmailField() >>> f.clean('foo@example.com') 'foo@example.com' >>> f.clean('invalid email address') Traceback (most recent call last): ... ValidationError: ['Enter a valid email address.']
核心字段參數
每一個Field
類的構造函數至少接受這些參數。 有些Field
類接受額外的、字段特有的參數,但如下參數應該老是能接受:
required
Field.
required
默認狀況下,每一個""
類都假設必需有值,因此若是你傳遞一個空的值 —— 無論是None
仍是空字符串(Field
) —— clean()
將引起一個ValidationError
異常:
>>> from django import forms >>> f = forms.CharField() >>> f.clean('foo') 'foo' >>> f.clean('') Traceback (most recent call last): ... ValidationError: ['This field is required.'] >>> f.clean(None) Traceback (most recent call last): ... ValidationError: ['This field is required.'] >>> f.clean(' ') ' ' >>> f.clean(0) '0' >>> f.clean(True) 'True' >>> f.clean(False) 'False'
若要表示一個字段不是必需的,請傳遞Field
給 required=False
的構造函數:
>>> f = forms.CharField(required=False) >>> f.clean('foo') 'foo' >>> f.clean('') '' >>> f.clean(None) '' >>> f.clean(0) '0' >>> f.clean(True) 'True' >>> f.clean(False) 'False'
若是ValidationError
具備clean()
,而你傳遞給required=False
一個空值,Field
將返回一個轉換後的空值而不是引起clean()
。 例如CharField
,它將是一個空的Unicode 字符串。 對於其它Field
類,它多是None
。 (每一個字段各不相同)。
label
Field.
label
label
參數讓你指定字段「對人類友好」的label。 當Field
在Form
中顯示時將用到它。
正如在前面「輸出表單爲HTML」中解釋的,Field
默認label 是經過將字段名中全部的下劃線轉換成空格並大寫第一個字母生成的。 若是默認的標籤不合適,能夠指定label
。
下面是一個完整示例,Form
爲它的兩個字段實現了label
。 咱們指定auto_id=False
來讓輸出簡單一些:
>>> from django import forms >>> class CommentForm(forms.Form): ... name = forms.CharField(label='Your name') ... url = forms.URLField(label='Your website', required=False) ... comment = forms.CharField() >>> f = CommentForm(auto_id=False) >>> print(f) <tr><th>Your name:</th><td><input type="text" name="name" required /></td></tr> <tr><th>Your website:</th><td><input type="url" name="url" /></td></tr> <tr><th>Comment:</th><td><input type="text" name="comment" required /></td></tr>
label_suffix
Field.
label_suffix
label_suffix
參數讓你基於每一個字段覆蓋表單的label_suffix
:
>>> class ContactForm(forms.Form): ... age = forms.IntegerField() ... nationality = forms.CharField() ... captcha_answer = forms.IntegerField(label='2 + 2', label_suffix=' =') >>> f = ContactForm(label_suffix='?') >>> print(f.as_p()) <p><label for="id_age">Age?</label> <input id="id_age" name="age" type="number" required /></p> <p><label for="id_nationality">Nationality?</label> <input id="id_nationality" name="nationality" type="text" required /></p> <p><label for="id_captcha_answer">2 + 2 =</label> <input id="id_captcha_answer" name="captcha_answer" type="number" required /></p>
initial
Field.
initial
Form
參數讓你指定渲染未綁定的Field
中的initial
時使用的初始值。
若要指定動態的初始數據,參見Form.initial
參數。
這個參數的使用場景是當你想要顯示一個「空」的表單,其某個字段初始化爲一個特定的值。 像這樣:
>>> from django import forms >>> class CommentForm(forms.Form): ... name = forms.CharField(initial='Your name') ... url = forms.URLField(initial='http://') ... comment = forms.CharField() >>> f = CommentForm(auto_id=False) >>> print(f) <tr><th>Name:</th><td><input type="text" name="name" value="Your name" required /></td></tr> <tr><th>Url:</th><td><input type="url" name="url" value="http://" required /></td></tr> <tr><th>Comment:</th><td><input type="text" name="comment" required /></td></tr>
widget
Field.
widget
Field
參數讓你指定渲染Widget
時使用的widget
類
help_text
Field.
help_text
help_text
參數讓你指定Field
的描述文本。 若是提供Field
,在經過Field
的便捷方法(例如,help_text
)渲染Form
時,它將緊接着as_ul()
顯示。
像模型字段的help_text
同樣,此值不會以自動生成的形式進行HTML轉義。
下面是一個完整的示例,Form
爲它的兩個字段實現了help_text
。 咱們指定auto_id=False
來讓輸出簡單一些:
>>> from django import forms >>> class HelpTextContactForm(forms.Form): ... subject = forms.CharField(max_length=100, help_text='100 characters max.') ... message = forms.CharField() ... sender = forms.EmailField(help_text='A valid email address, please.') ... cc_myself = forms.BooleanField(required=False) >>> f = HelpTextContactForm(auto_id=False) >>> print(f.as_table()) <tr><th>Subject:</th><td><input type="text" name="subject" maxlength="100" required /><br /><span class="helptext">100 characters max.</span></td></tr> <tr><th>Message:</th><td><input type="text" name="message" required /></td></tr> <tr><th>Sender:</th><td><input type="email" name="sender" required /><br />A valid email address, please.</td></tr> <tr><th>Cc myself:</th><td><input type="checkbox" name="cc_myself" /></td></tr> >>> print(f.as_ul())) <li>Subject: <input type="text" name="subject" maxlength="100" required /> <span class="helptext">100 characters max.</span></li> <li>Message: <input type="text" name="message" required /></li> <li>Sender: <input type="email" name="sender" required /> A valid email address, please.</li> <li>Cc myself: <input type="checkbox" name="cc_myself" /></li> >>> print(f.as_p()) <p>Subject: <input type="text" name="subject" maxlength="100" required /> <span class="helptext">100 characters max.</span></p> <p>Message: <input type="text" name="message" required /></p> <p>Sender: <input type="email" name="sender" required /> A valid email address, please.</p> <p>Cc myself: <input type="checkbox" name="cc_myself" /></p>
error_messages
Field.
error_messages
error_messages
參數讓你覆蓋字段引起的異常中的默認信息。 傳遞的是一個字典,其鍵爲你想覆蓋的錯誤信息。 例如,下面是默認的錯誤信息:
>>> from django import forms >>> generic = forms.CharField() >>> generic.clean('') Traceback (most recent call last): ... ValidationError: ['This field is required.']
而下面是自定義的錯誤信息:
>>> name = forms.CharField(error_messages={'required': 'Please enter your name'}) >>> name.clean('') Traceback (most recent call last): ... ValidationError: ['Please enter your name']
validators
Field.
validators
validators
參數讓你能夠爲字段提供一個驗證函數的列表。
localize
Field.
localize
localize
參數能夠實現表單數據輸入的定位,以及渲染輸出。
disabled
Field.
disabled
disabled
布爾參數,當設置爲True
時,使用disabled
HTML屬性禁用表單域,以使用戶沒法編輯。 即便用戶篡改了提交給服務器的字段的值,它也將被忽略,有利於表單初始數據中的值。
has_changed()
Field.
has_changed
()[source]
has_changed()
方法用於決定字段的值是否從初始值發生了改變。 返回True
或False
。
BooleanField
BooleanField
(**kwargs)[source]
CheckboxInput
False
True
或 False
。True
,驗證值是否爲required=True
(例如複選框被勾上)。required
CharField
(**kwargs)[source]
TextInput
empty_value
給出的任何值。max_length
或min_length
。 不然,全部的輸入都是合法的。min_length
, max_length
, required
有三個可選參數進行驗證:
max_length
min_length
若是提供,這兩個參數將確保字符串的最大和最小長度。
strip
若是True
(默認),該值將被剝離前導和尾隨空格。
empty_value
用來表示「空」的值。 默認爲空字符串。
ChoiceField
ChoiceField
(**kwargs)[source]
Select
''
(一個空字符串)required
, invalid_choice
invalid_choice
錯誤消息可能包含%(value)s
,它將被選擇的選項替換掉。
還有一個參數:
choices
用來做爲該字段選項的一個二元組組成的可迭代對象(例如,列表或元組)或者一個可調用對象。 參數的格式與模型字段的choices
參數相同。
TypedChoiceField
TypedChoiceField
(**kwargs)[source]
就像ChoiceField
同樣,除了TypedChoiceField
還有兩個額外的參數:coerce
和empty_value
。
Select
empty_value
給出的任何值。coerce
參數類型的值。required
, invalid_choice
接收的額外參數:
coerce
接收一個參數並返回強制轉換後的值的一個函數。 例如內建的bool
、float
、int
和其它類型。 默認爲id 函數。 注意強制轉換在輸入驗證結束後發生,因此它可能強制轉換不在 choices
中的值。
empty_value
用於表示「空」的值。默認爲空字符串; None
是這裏的另外一個常見選擇。 注意這個值不會被coerce
參數中指定的函數強制轉換,因此請根據狀況進行選擇。
DateField
DateField
(**kwargs)[source]
DateInput
None
datetime.date
對象。datetime.date
、datetime.datetime
或指定日期格式的字符串。required
, invalid
接收一個可選的參數:
input_formats
一個格式的列表,用於轉換一個字符串爲datetime.date
對象。
若是沒有提供input_formats
,默認的輸入格式爲:
['%Y-%m-%d', # '2006-10-25' '%m/%d/%Y', # '10/25/2006' '%m/%d/%y'] # '10/25/06'
DateTimeField
DateTimeField
(**kwargs)[source]
DateTimeInput
None
datetime.datetime
對象。datetime.datetime
、datetime.date
或指定日期格式的字符串。required
, invalid
接收一個可選的參數:
input_formats
一個格式的列表,用於轉換一個字符串爲datetime.datetime
對象。
若是沒有提供input_formats
,默認的輸入格式爲:
['%Y-%m-%d %H:%M:%S', # '2006-10-25 14:30:59' '%Y-%m-%d %H:%M', # '2006-10-25 14:30' '%Y-%m-%d', # '2006-10-25' '%m/%d/%Y %H:%M:%S', # '10/25/2006 14:30:59' '%m/%d/%Y %H:%M', # '10/25/2006 14:30' '%m/%d/%Y', # '10/25/2006' '%m/%d/%y %H:%M:%S', # '10/25/06 14:30:59' '%m/%d/%y %H:%M', # '10/25/06 14:30' '%m/%d/%y'] # '10/25/06'
DecimalField
(**kwargs)[source]
Field.localize
是False
時爲NumberInput
,不然爲TextInput
。None
decimal
。max_whole_digits
, max_digits
, max_decimal_places
, max_value
, invalid
, required
, min_value
%(limit_value)s
和min_value
錯誤信息可能包含max_value
,它們將被真正的限制值替換。 相似地,max_whole_digits
、max_decimal_places
和 max_digits
錯誤消息可能包含%(max)s
。
接收四個可選的參數:
max_value
min_value
它們控制字段中容許的值的範圍,應該以decimal.Decimal
值給出。
max_digits
值容許的最大位數(小數點以前和以後的數字總共的位數,前導的零將被刪除)。
decimal_places
容許的最大小數位。
DurationField
DurationField
(**kwargs)[source]
TextInput
None
timedelta
。timedelta
。required
, invalid
.接收任何能夠被parse_duration()
理解的格式。
EmailField
EmailField
(**kwargs)[source]
EmailInput
''
(一個空字符串)required
, invalid
具備兩個可選的參數用於驗證,max_length
和min_length
。 若是提供,這兩個參數將確保字符串的最大和最小長度。
FileField
FileField
(**kwargs)[source]
ClearableFileInput
None
UploadedFile
對象,它封裝文件內容和文件名爲一個單獨的對象。missing
, invalid
, required
, empty
, max_length
具備兩個可選的參數用於驗證,max_length
和 allow_empty_file
。 若是提供,這兩個參數確保文件名的最大長度,並且即便文件內容爲空時驗證也會成功。
FloatField
FloatField
(**kwargs)[source]
Field.localize
是False
時爲NumberInput
,不然爲TextInput
。None
float()
函數同樣,容許前導和尾隨的空白符。max_value
, invalid
, required
, min_value
接收兩個可選的參數用於驗證,max_value
和min_value
。 它們控制字段中容許的值的範圍。
IntergerField
IntegerField
(**kwargs)[source]
Field.localize
是False
時爲NumberInput
,不然爲TextInput
。None
int()
函數。max_value
, invalid
, required
, min_value
%(limit_value)s
和min_value
錯誤信息可能包含max_value
,它們將被真正的限制值替換。
採用兩個可選參數進行驗證:
max_value
min_value
它們控制字段中容許的值的範圍。
GenericIPAddressField
GenericIPAddressField
(**kwargs)[source]
包含IPv4或IPv6地址的字段。
TextInput
''
(一個空字符串)required
, invalid
IPv6地址規範化遵循 RFC 4291#section-2.2第2.2節,包括使用該段第3段中建議的IPv4格式,如::ffff:192.0.2.0
例如,::ffff:0a0a:0a0a
將被標準化爲2001::1
和2001:0::0:01
::ffff:10.10.10.10
。 全部字符都轉換爲小寫。
有兩個可選參數:
protocol
限制指定協議的有效輸入。 接受的值爲IPv6
(默認值),IPv4
或both
。 匹配不區分大小寫。
unpack_ipv4
解開IPv4映射地址,例如::ffff:192.0.2.1
。 若是啓用此選項,則該地址將解包到192.0.2.1
。 默認爲禁用。 只能在protocol
設置爲'both'
時使用。
MultipleChoiceField
MultipleChoiceField
(**kwargs)[source]
SelectMultiple
[]
(一個空列表)invalid_list
, invalid_choice
, required
invalid_choice
錯誤消息可能包含%(value)s
,它將被選擇的選項替換掉。
對於choices
,須要一個額外的必需參數ChoiceField
。
TypedMultipleChoiceField
TypedMultipleChoiceField
(**kwargs)[source]
就像MultipleChoiceField
,除了TypedMultipleChoiceField
須要兩個額外的參數,coerce
和empty_value
。
SelectMultiple
empty_value
coerce
參數提供的類型值列表。required
, invalid_choice
invalid_choice
錯誤消息可能包含%(value)s
,它將被選擇的選項替換掉。
對於TypedChoiceField
,須要兩個額外的參數empty_value
和coerce
。
RegexField
RegexField
(**kwargs)[source]
TextInput
''
(一個空字符串)required
, invalid
須要一個必需的參數:
regex
指定爲字符串或編譯的正則表達式對象的正則表達式。
還須要max_length
,min_length
和strip
,它們與CharField
同樣工做。
strip
默認爲False
。 若是啓用,則將在正則表達式驗證以前應用剝離
SlugField
SlugField
(**kwargs)[source]
TextInput
''
(一個空字符串)required
, invalid
此字段用於在表單中表示模型SlugField
。
使用可選參數:
allow_unicode
布爾型指令除了ASCII字母外,還能夠接受Unicode字母。 默認爲False
。
TimeField
TimeField
(**kwargs)[source]
TextInput
None
datetime.time
對象。datetime.time
或以特定時間格式格式化的字符串。required
, invalid
接收一個可選的參數:
input_formats
用於嘗試將字符串轉換爲有效的datetime.time
對象的格式列表。
若是沒有提供input_formats
,默認的輸入格式爲:
URLField
URLField
(**kwargs)[source]
URLInput
''
(一個空字符串)required
, invalid
採用如下可選參數:
max_length
min_length
這些與CharField.max_length
和CharField.min_length
相同。
更多-->>http://usyiyi.cn/translate/Django_111/ref/forms/fields.html
不要將Widget 與form fields搞混淆。 表單字段負責驗證輸入並直接在模板中使用。 Widget 負責渲染網頁上HTML 表單的輸入元素和提取提交的原始數據
每當你指定表單的一個字段的時候,Django 將使用適合其數據類型的默認Widget。然而,若是你想要使用一個不一樣的Widget,你能夠在定義字段時使用widget
參數。 像這樣:
from django import forms class CommentForm(forms.Form): name = forms.CharField() url = forms.URLField() comment = forms.CharField(widget=forms.Textarea)
這將使用一個Textarea
Widget來設置表單的評論 ,而不是默認的TextInput
Widget
許多小部件具備可選的額外參數;在字段上定義窗口小部件時能夠設置它們。 在下面的示例中,設置了SelectDateWidget
的years
屬性:
from django import forms BIRTH_YEAR_CHOICES = ('1980', '1981', '1982') FAVORITE_COLORS_CHOICES = ( ('blue', 'Blue'), ('green', 'Green'), ('black', 'Black'), ) class SimpleForm(forms.Form): birth_year = forms.DateField(widget=forms.SelectDateWidget(years=BIRTH_YEAR_CHOICES)) favorite_colors = forms.MultipleChoiceField( required=False, widget=forms.CheckboxSelectMultiple, choices=FAVORITE_COLORS_CHOICES, )
(1)小部件繼承自select小部件
繼承自Select
的Widget 負責處理HTML 選項。 它們呈現給用戶一個能夠選擇的選項列表。 不一樣的小部件呈現出不一樣的選擇;Select
小部件自己使用<select>
HTML列表表示,而RadioSelect
使用單選按鈕。
ChoiceField
字段默認使用Select
。 Widget 上顯示的選項來自ChoiceField
,對ChoiceField.choices
的改變將更新Select.choices
。 像這樣:
>>> from django import forms >>> CHOICES = (('1', 'First',), ('2', 'Second',)) >>> choice_field = forms.ChoiceField(widget=forms.RadioSelect, choices=CHOICES) >>> choice_field.choices [('1', 'First'), ('2', 'Second')] >>> choice_field.widget.choices [('1', 'First'), ('2', 'Second')] >>> choice_field.widget.choices = () >>> choice_field.choices = (('1', 'First and only',),) >>> choice_field.widget.choices [('1', 'First and only')]
提供choices
屬性的Widget 也能夠用於不是基於選項的字段 , 例如CharField
—— 當選項與模型有關而不僅是Widget 時,建議使用基於ChoiceField
的字段。
(2)樣式化小部件
若是你想讓某個Widget 實例與其它Widget 看上去不同,你須要在Widget 對象實例化並賦值給一個表單字段時指定額外的屬性(以及可能須要在你的CSS 文件中添加一些規則)。
例以下面這個簡單的表單:
from django import forms class CommentForm(forms.Form): name = forms.CharField() url = forms.URLField() comment = forms.CharField()
這個表單包含三個默認的TextInput
Widget,以默認的方式渲染 —— 沒有CSS 類、沒有額外的屬性。 這表示每一個Widget 的輸入框將渲染得如出一轍:
>>> f = CommentForm(auto_id=False) >>> f.as_table() <tr><th>Name:</th><td><input type="text" name="name" required /></td></tr> <tr><th>Url:</th><td><input type="url" name="url" required /></td></tr> <tr><th>Comment:</th><td><input type="text" name="comment" required /></td></tr>
在真正得網頁中,你可能不想讓每一個Widget 看上去都同樣。 你可能想要給comment 一個更大的輸入元素,你可能想讓‘name’ Widget 具備一些特殊的CSS 類。 能夠指定‘type’ 屬性使用的是新式的HTML5 輸入類型。 在建立Widget 時使用Widget.attrs
參數能夠實現:
class CommentForm(forms.Form): name = forms.CharField(widget=forms.TextInput(attrs={'class': 'special'})) url = forms.URLField() comment = forms.CharField(widget=forms.TextInput(attrs={'size': '40'}))
Django 將在渲染的輸出中包含額外的屬性:
>>> f = CommentForm(auto_id=False) >>> f.as_table() <tr><th>Name:</th><td><input type="text" name="name" class="special" required /></td></tr> <tr><th>Url:</th><td><input type="url" name="url" required /></td></tr> <tr><th>Comment:</th><td><input type="text" name="comment" size="40" required /></td></tr>
(3)基於小部件類
Widget
和MultiWidget
是全部built-in widgets 的基類,並可用於自定義Widget 的基類。
Widget
Widget
(attrs=None)[source]
這是個抽象類,它不能夠渲染,可是提供基本的屬性attrs
。 你能夠在自定義的Widget 中實現或覆蓋render()
方法。
ATTRS T0>
包含渲染後的Widget 將要設置的HTML 屬性。
>>> from django import forms >>> name = forms.TextInput(attrs={'size': 10, 'title': 'Your name',}) >>> name.render('name', 'A name') '<input title="Your name" type="text" name="name" value="A name" size="10" required />'
若是你給一個屬性賦值True
或False
,它將渲染成一個HTML5 風格的布爾屬性:
>>> name = forms.TextInput(attrs={'required': True}) >>> name.render('name', 'A name') '<input name="name" type="text" value="A name" required />' >>> >>> name = forms.TextInput(attrs={'required': False}) >>> name.render('name', 'A name') '<input name="name" type="text" value="A name" />'
format_value
(value)[source]
清除並返回一個用於小部件模板的值。 value
不能保證是有效的輸入,所以子類的實現應該防護性地編程。
在舊版本中,此方法是名爲_format_value()
的私有API。 舊的名稱將工做,直到Django 2.0。
get_context
(name,value,attrs)[source]
返回在渲染窗口小部件模板時要使用的值的字典。 默認狀況下,該字典包含一個單一的鍵'widget'
,它是包含如下鍵的小部件的字典表示形式:
'name'
:name
參數中的字段的名稱。'is_hidden'
:一個布爾值,表示該小部件是否被隱藏。'required'
:一個布爾值,表示是否須要此窗口小部件的字段。'value'
:由format_value()
返回的值。'attrs'
:要在已渲染的小部件上設置HTML屬性。 attrs
屬性和attrs
參數的組合。'template_name'
:self.template_name
的值。Widget
子類能夠經過覆蓋此方法來提供自定義上下文值。
id_for_label
(id_)[source]
給定該字段的ID,返回此小部件的HTML ID屬性,以供<label>
使用。 若是ID不可用,則返回None
。
這個鉤子是必要的,由於一些小部件具備多個HTML元素,所以具備多個ID。 在這種狀況下,該方法應該返回與widget的標籤中的第一個ID相對應的ID值。
render
(name, value, attrs=None, renderer=None)[source]
使用給定的渲染器將小部件渲染爲HTML。 若是renderer
是None
,則使用FORM_RENDERER
設置中的渲染器。
添加了renderer
參數。 支持不接受的子類將在Django 2.1中被刪除。
value_from_datadict
(data,files,name)[source]
根據一個字典和該Widget 的名稱,返回該Widget 的值。 files
可能包含來自request.FILES
的數據。 若是沒有提供value,則返回None
。 在處理表單數據的過程當中,value_from_datadict
可能調用屢次,因此若是你自定義並添加額外的耗時處理時,你應該本身實現一些緩存機制。
value_omitted_from_data
(數據,文件,名稱)[source]
給定data
和files
字典和此小部件的名稱,返回是否有數據或文件的小部件。
該方法的結果會影響模型窗體falls back to its default。
特殊狀況是CheckboxInput
,CheckboxSelectMultiple
和SelectMultiple
,它始終返回False
,由於未選中的複選框並未選擇&lt; select multiple&gt;
不會出如今HTML表單提交的數據中,所以用戶是否提交了值是未知的。
use_required_attribute
(initial)[source]
給定一個表單域的initial
值,返回是否能夠使用required
表單使用此方法與Field.required
和Form.use_required_attribute
一塊兒肯定是否顯示每一個字段的required
屬性。
默認狀況下,爲隱藏的小部件返回False
,不然返回True
。 特殊狀況是ClearableFileInput
,當initial
未設置時返回False
,CheckboxSelectMultiple
,它始終返回False
,由於瀏覽器驗證將須要檢查全部複選框,而不是至少一個。
在與瀏覽器驗證不兼容的自定義小部件中覆蓋此方法。 例如,由隱藏的textarea
元素支持的WSYSIWG文本編輯器小部件可能但願始終返回False
,以免在隱藏字段上進行瀏覽器驗證。
MultiWidget
class MultiWidget
(widgets,attrs = None)[source]
由多個Widget 組合而成的Widget。 MultiWidget
始終與MultiValueField
聯合使用。
MultiWidget
具備一個必選參數:
widgets
一個包含須要的Widget 的可迭代對象。
以及一個必需的方法:
decompress
(value)[source]
這個方法接受來自字段的一個「壓縮」的值,並返回「解壓」的值的一個列表。 能夠假設輸入的值是合法的,但不必定是非空的。
子類必須實現 這個方法,並且由於值可能爲空,實現必需要防衛這點。
「解壓」的基本原理是須要「分離」組合的表單字段的值爲每一個Widget 的值。
有個例子是,SplitDateTimeWidget
將datetime
值分離成兩個獨立的值分別表示日期和時間:
from django.forms import MultiWidget class SplitDateTimeWidget(MultiWidget): # ... def decompress(self, value): if value: return [value.date(), value.time().replace(microsecond=0)] return [None, None]
它提供一些自定義上下文:
get_context
(name,value,attrs)[source]
除了Widget.get_context()
中描述的'widget'
以外,MultiValueWidget
添加了一個widget['subwidgets']
這些能夠在窗口小部件模板中循環:
{% for subwidget in widget.subwidgets %} {% include widget.template_name with widget=subwidget %} {% endfor %}
下面示例中的Widget 繼承MultiWidget
以在不一樣的選擇框中顯示年、月、日。 這個Widget 主要想用於DateField
而不是MultiValueField
,因此咱們實現了value_from_datadict()
:
from datetime import date from django.forms import widgets class DateSelectorWidget(widgets.MultiWidget): def __init__(self, attrs=None): # create choices for days, months, years # example below, the rest snipped for brevity. years = [(year, year) for year in (2011, 2012, 2013)] _widgets = ( widgets.Select(attrs=attrs, choices=days), widgets.Select(attrs=attrs, choices=months), widgets.Select(attrs=attrs, choices=years), ) super(DateSelectorWidget, self).__init__(_widgets, attrs) def decompress(self, value): if value: return [value.day, value.month, value.year] return [None, None, None] def value_from_datadict(self, data, files, name): datelist = [ widget.value_from_datadict(data, files, name + '_%s' % i) for i, widget in enumerate(self.widgets)] try: D = date( day=int(datelist[0]), month=int(datelist[1]), year=int(datelist[2]), ) except ValueError: return '' else: return str(D)
構造器在一個元組中建立了多個Select
widget。 super
類使用這個元組來啓動widget。
必需的decompress()
方法將datetime.date
值拆成年、月和日的值,對應每一個widget。 注意這個方法如何處理value
爲None
的狀況。
value_from_datadict()
的默認實現會返回一個列表,對應每個Widget
。 當和MultiValueField
一塊兒使用MultiWidget
的時候,這樣會很是合理,可是因爲咱們想要和擁有單一值得DateField
一塊兒使用這個widget,咱們必須覆寫這一方法,將全部子widget的數據組裝成datetime.date
。 這個方法從POST
字典中獲取數據,而且構造和驗證日期。 若是日期有效,會返回它的字符串,不然會返回一個空字符串,它會使form.is_valid
返回False
。
Django 提供全部基本的HTML Widget,並在django.forms.widgets
模塊中提供一些常見的Widget 組,包括the input of text、various checkboxes and selectors、uploading files和handling of multi-valued input。
TextInput
TextInput
[source]
input_type
:'text'
template_name
:'django/forms/widgets/text.html'
<input type =「text」 ...>
NumberInput
EmailInput
EmailInput
[source]
input_type
:'email'
template_name
:'django/forms/widgets/email.html'
<input type =「email」 ...>
URLInput
URLInput
[source]
input_type
:'url'
template_name
:'django/forms/widgets/url.html'
<input type =「url」 ...>
PasswordInput
PasswordInput
[source]
input_type
:'password'
template_name
:'django/forms/widgets/password.html'
<input type =「password」 ...>
接收一個可選的參數:
render_value T0>
決定在驗證錯誤後從新顯示錶單時,Widget 是否填充(默認爲False
)。
DateInput
DateInput
[source]
input_type
:'text'
template_name
:'django/forms/widgets/date.html'
<input type =「text」 ...>
接收的參數與TextInput
相同,可是帶有一些可選的參數:
格式
字段的初始值應該顯示的格式。
若是沒有提供format
參數,默認的格式爲參考Format localization在DATE_INPUT_FORMATS
中找到的第一個格式。
DateTimeInput
DateTimeInput
[source]
input_type
:'text'
template_name
:'django/forms/widgets/datetime.html'
<input type =「text」 ...>
接收的參數與TextInput
相同,可是帶有一些可選的參數:
格式
字段的初始值應該顯示的格式。
若是沒有提供format
參數,默認的格式爲參考Format localization在DATETIME_INPUT_FORMATS
中找到的第一個格式。
默認狀況下,時間值的微秒部分始終設置爲0
。 若是須要微秒,請使用supports_microseconds
屬性設置爲True
的子類。
TimeInput
TimeInput
[source]
input_type
:'text'
template_name
:'django/forms/widgets/time.html'
<input type =「text」 ...>
接收的參數與TextInput
相同,可是帶有一些可選的參數:
格式
字段的初始值應該顯示的格式。
若是沒有提供format
參數,默認的格式爲參考Format localization在TIME_INPUT_FORMATS
中找到的第一個格式。
有關微秒的處理,請參閱DateTimeInput
。
Textarea
Textarea
[source]
template_name
:'django/forms/widgets/textarea.html'
<textarea>...</textarea>
選擇器和複選框小部件
這些小部件使用HTML元素<select>
, <input type="checkbox">
, 和 <input type="radio">
.
呈現多個選項的窗口小部件具備指定用於呈現每一個選項的模板的option_template_name
屬性。 For example, for the Select
widget, select_option.html
renders the <option>
for a <select>
.
CheckboxInput
CheckboxInput
[source]
input_type
:'checkbox'
template_name
:'django/forms/widgets/checkbox.html'
<input type="checkbox" ...>
接收一個可選的參數:
check_test T0>
一個可調用的對象,接收CheckboxInput
的值並若是複選框應該勾上返回True
。
Select
NullBooleanSelect
NullBooleanSelect
[source]
template_name
:'django/forms/widgets/select.html'
option_template_name
:'django/forms/widgets/select_option.html'
Select Widget,選項爲‘Unknown’、‘Yes’ 和‘No’。
SelectMultiple
RadioSelect
RadioSelect
[source]
template_name
:'django/forms/widgets/radio.html'
option_template_name
:'django/forms/widgets/radio_option.html'
相似Select
,可是渲染成<li>
標籤中的一個單選按鈕列表:
<ul> <li><input type="radio" name="..."></li> ... </ul>
你能夠迭代模板中的單選按鈕來更細緻地控制生成的HTML。 假設表單RadioSelect
具備一個字段beatles
,它使用myform
做爲Widget:
{% for radio in myform.beatles %} <div class="myradio"> {{ radio }} </div> {% endfor %}
它將生成如下HTML:
<div class="myradio"> <label for="id_beatles_0"><input id="id_beatles_0" name="beatles" type="radio" value="john" required /> John</label> </div> <div class="myradio"> <label for="id_beatles_1"><input id="id_beatles_1" name="beatles" type="radio" value="paul" required /> Paul</label> </div> <div class="myradio"> <label for="id_beatles_2"><input id="id_beatles_2" name="beatles" type="radio" value="george" required /> George</label> </div> <div class="myradio"> <label for="id_beatles_3"><input id="id_beatles_3" name="beatles" type="radio" value="ringo" required /> Ringo</label> </div>
這包括<label>
標籤。 你能夠使用單選按鈕的id_for_label
、choice_label
和 tag
屬性進行更細的控制。 例如,這個模板...
{% for radio in myform.beatles %} <label for="{{ radio.id_for_label }}"> {{ radio.choice_label }} <span class="radio">{{ radio.tag }}</span> </label> {% endfor %}
...將致使如下HTML:
<label for="id_beatles_0"> John <span class="radio"><input id="id_beatles_0" name="beatles" type="radio" value="john" required /></span> </label> <label for="id_beatles_1"> Paul <span class="radio"><input id="id_beatles_1" name="beatles" type="radio" value="paul" required /></span> </label> <label for="id_beatles_2"> George <span class="radio"><input id="id_beatles_2" name="beatles" type="radio" value="george" required /></span> </label> <label for="id_beatles_3"> Ringo <span class="radio"><input id="id_beatles_3" name="beatles" type="radio" value="ringo" required /></span> </label>
若是你不迭代單選按鈕 —— 例如,你的模板只是簡單地包含{{ myform.beatles }}
—— 它們將以<ul>
中的<li>
標籤輸出,就像上面同樣。
外部<ul>
容器接收小部件的id
屬性,若是已定義,不然將接收BoundField.auto_id
。
當迭代單選按鈕時,for
和input
標籤分別包含label
和id
屬性。 每一個單項按鈕具備一個id_for_label
屬性來輸出元素的ID。
CheckboxSelectMultiple
CheckboxSelectMultiple
[source]
template_name
:'django/forms/widgets/checkbox_select.html'
option_template_name
:'django/forms/widgets/checkbox_option.html'
相似SelectMultiple
,可是渲染成一個複選框列表:
<ul> <li><input type="checkbox" name="..." ></li> ... </ul>
外部<ul>
容器接收小部件的id
屬性,若是已定義,不然將接收BoundField.auto_id
。
像RadioSelect
同樣,您能夠循環查看小部件選擇的各個複選框。 與RadioSelect
不一樣,複選框將不包含required
HTML屬性,若是該字段是必需的,由於瀏覽器驗證將須要檢查全部複選框,而不是至少檢查一個。
當迭代單選按鈕時,for
和input
標籤分別包含label
和id
屬性。 每一個單項按鈕具備一個id_for_label
屬性來輸出元素的ID。
文件上傳小部件
FileInput
FileInput
[source]
template_name
:'django/forms/widgets/file.html'
<input type="file" ...>
ClearableFileInput
ClearableFileInput
[source]
template_name
:'django/forms/widgets/clearable_file_input.html'
<input type =「file」 ...>
清除字段的值,若是該字段不是必需的,並具備初始數據。複合小部件
SplitDateTimeWidget
SplitDateTimeWidget
[source]
template_name
:'django/forms/widgets/splitdatetime.html'
封裝(使用MultiWidget
)兩個Widget:DateInput
用於日期,TimeInput
用於時間。 必須與SplitDateTimeField
而不是DateTimeField
一塊兒使用。
SplitDateTimeWidget
有兩個可選的屬性:
date_format
time_format
SelectDateWidget
MONTHS = {
1:_('jan'), 2:_('feb'), 3:_('mar'), 4:_('apr'),
5:_('may'), 6:_('jun'), 7:_('jul'), 8:_('aug'),
9:_('sep'), 10:_('oct'), 11:_('nov'), 12:_('dec')
}
empty_label
若是DateField
不是必選的,SelectDateWidget
將有一個空的選項位於選項的頂部(默認爲---
)。 你能夠經過empty_label
屬性修改這個文本。 list
能夠是一個string
、empty_label
或tuple
。 當使用字符串時,全部的選擇框都帶有這個空選項。 若是tuple
爲具備3個字符串元素的list
或empty_label
,每一個選擇框將具備它們自定義的空選項。 空選項應該按這個順序('year_label', 'month_label', 'day_label')
。
# A custom empty label with string field1 = forms.DateField(widget=SelectDateWidget(empty_label="Nothing")) # A custom empty label with tuple field1 = forms.DateField( widget=SelectDateWidget( empty_label=("Choose Year", "Choose Month", "Choose Day"), ), )
若是你正在構建一個數據庫驅動的應用,那麼你應該會有與Django 的模型緊密映射的表單。 舉個例子,你也許會有個BlogComment
模型,而且你還想建立一個表單讓你們提交評論到這個模型中。 在這種狀況下,在表單中定義字段將是冗餘的,由於你已經在模型中定義了字段。
基於這個緣由,Django 提供一個輔助類來讓你能夠從Django 的模型建立Form
。
像這樣:
>>> from django.forms import ModelForm >>> from myapp.models import Article # Create the form class. >>> class ArticleForm(ModelForm): ... class Meta: ... model = Article ... fields = ['pub_date', 'headline', 'content', 'reporter'] # Creating a form to add an article. >>> form = ArticleForm() # Creating a form to change an existing article. >>> article = Article.objects.get(pk=1) >>> form = ArticleForm(instance=article)
(1)字段類型
生成的Form
類中將具備和指定的模型字段對應的表單字段,順序爲fields
屬性中指定的順序。
每一個模型字段有一個對應的默認表單字段。 好比,模型中的CharField
表現成表單中的CharField
。 模型中的MultipleChoiceField
字段會表現成ManyToManyField
字段。 下面是一個完整的列表:
模型字段 | 表單域 |
---|---|
AutoField |
沒有以形式表示 |
BigAutoField |
沒有以形式表示 |
BigIntegerField |
IntegerField with min_value set to -9223372036854775808 and max_value set to 9223372036854775807. |
BooleanField |
BooleanField |
CharField |
CharField with max_length set to the model field’s max_length andempty_value set to None if null=True . |
CommaSeparatedIntegerField |
CharField |
DateField |
的DateField |
DateTimeField |
DateTimeField字段 |
DecimalField |
DecimalField |
EmailField |
EmailField |
FileField |
的FileField |
FilePathField |
FilePathField |
FloatField |
FloatField |
ForeignKey |
ModelChoiceField (見下文) |
ImageField |
ImageField |
IntegerField |
IntegerField |
IPAddressField |
IPAddressField |
GenericIPAddressField |
GenericIPAddressField |
ManyToManyField |
ModelMultipleChoiceField (見下文) |
NullBooleanField |
NullBooleanField |
PositiveIntegerField |
IntegerField |
PositiveSmallIntegerField |
IntegerField |
SlugField |
SlugField |
SmallIntegerField |
IntegerField |
TextField |
CharField with widget=forms.Textarea |
TimeField |
TimeField |
URLField |
URLField |
可能如你所料,ManyToManyField
和 ForeignKey
字段類型屬於特殊狀況:
QuerySet
表示成ChoiceField
,它是一個django.forms.ModelChoiceField
,其選項是模型的ForeignKey
。QuerySet
表示成MultipleChoiceField
,它是一個django.forms.ModelMultipleChoiceField
,其選項是模型的ManyToManyField
。此外,生成的每一個表單字段都有如下屬性集:
不然,required=True
。verbose_name
設置爲模型字段的label
,並將第一個字母大寫。help_text
設置爲模型字段的help_text
。
選項一般會包含空選項,而且會默認選擇。 若是字段是必選的,它會強制用戶選擇一個選項。 若是模型字段的default
且具備一個顯示的default
值,將不會包含空選項(初始將選擇blank=False
值)。最後,請注意你能夠爲給定的模型字段從新指定表單字段。
一個完整的例子
from django.db import models from django.forms import ModelForm TITLE_CHOICES = ( ('MR', 'Mr.'), ('MRS', 'Mrs.'), ('MS', 'Ms.'), ) class Author(models.Model): name = models.CharField(max_length=100) title = models.CharField(max_length=3, choices=TITLE_CHOICES) birth_date = models.DateField(blank=True, null=True) def __str__(self): # __unicode__ on Python 2 return self.name class Book(models.Model): name = models.CharField(max_length=100) authors = models.ManyToManyField(Author) class AuthorForm(ModelForm): class Meta: model = Author fields = ['name', 'title', 'birth_date'] class BookForm(ModelForm): class Meta: model = Book fields = ['name', 'authors']
使用這些模型,上面的ModelForm
子類將大體至關於這個(惟一的區別是save()
方法,咱們稍後將討論。):
from django import forms class AuthorForm(forms.Form): name = forms.CharField(max_length=100) title = forms.CharField( max_length=3, widget=forms.Select(choices=TITLE_CHOICES), ) birth_date = forms.DateField(required=False) class BookForm(forms.Form): name = forms.CharField(max_length=100) authors = forms.ModelMultipleChoiceField(queryset=Author.objects.all())
在ModelForm上進行驗證
驗證ModelForm
主要有兩步:
與普通的表單驗證類型相似,模型表單的驗證在調用is_valid()
或訪問errors
屬性時隱式調用,或者經過full_clean()
顯式調用,儘管在實際應用中你將不多使用後一種方法。
clean()
的驗證(Model.full_clean()
)在表單驗證這一步的內部觸發,緊跟在表單的Model
方法調用以後。
覆蓋clean()方法
能夠重寫模型表單的clean()
來提供額外的驗證,方法和普通的表單同樣。
模型表單實例包含一個instance
屬性,表示與它綁定的模型實例。
與模型驗證的交互
做爲驗證過程的一部分,clean()
將調用與表單字段對應的每一個模型字段的ModelForm
方法。 若是你已經排除某些模型字段,這些字段不會運行驗證
模型error_message的注意事項
form field
級別或form Meta級別的錯誤信息永遠比model field
級別的錯誤信息優先。
model fields
的錯誤信息只用於model validation步驟引起ValidationError
的時候,且不會有對應的表單級別的錯誤信息。
你能夠根據模型驗證引起的Meta
覆蓋錯誤信息,方法是添加 NON_FIELD_ERRORS
鍵到ModelForm
內聯error_messages
類的NON_FIELD_ERRORS
字典:
from django.forms import ModelForm from django.core.exceptions import NON_FIELD_ERRORS class ArticleForm(ModelForm): class Meta: error_messages = { NON_FIELD_ERRORS: { 'unique_together': "%(model_name)s's %(field_labels)s are not unique.", } }
save()方法
每一個ModelForm還具備一個save()方法。 這個方法根據表單綁定的數據建立並保存數據庫對象。
ModelForm
的子類能夠接受現有的模型實例做爲關鍵字參數instance
;若是提供此功能,則save()
將更新該實例。 若是沒有提供,save()
將建立模型的一個新實例:
>>> from myapp.models import Article >>> from myapp.forms import ArticleForm # Create a form instance from POST data. >>> f = ArticleForm(request.POST) # Save a new Article object from the form's data. >>> new_article = f.save() # Create a form to edit an existing Article, but use # POST data to populate the form. >>> a = Article.objects.get(pk=1) >>> f = ArticleForm(request.POST, instance=a) >>> f.save()
注意,若是表單hasn’t been validated,form.errors
調用將經過檢查save()
來進行驗證。 若是表單中的數據不合法,將引起True
—— 例如,若是form.errors
爲ValueError
。
若是表單數據中沒有可選字段,則生成的模型實例使用模型字段default
(若是有)。 This behavior doesn’t apply to fields that use CheckboxInput
, CheckboxSelectMultiple
, or SelectMultiple
(or any custom widget whosevalue_omitted_from_data()
method always returns False
) since an unchecked checkbox and unselected<select multiple>
don’t appear in the data of an HTML form submission. 若是您正在設計一個API而且但願使用這些小部件之一的字段的缺省回退行爲,請使用自定義表單字段或小部件。
較舊的版本沒有CheckboxInput
的例外,這意味着若是這是模型字段默認值,則未選中的複選框將接收到True
的值。
此save()方法接受一個可選的關鍵字爲commit的參數,commit的取值爲True或者False。 若是
commit=False
時save()
,那麼它將返回一個尚未保存到數據庫的對象。 這種狀況下,你須要調用返回的模型實例的save()
。 若是你想在保存以前自定義一些處理,或者你想使用特定的model saving options,能夠這樣使用。 True
默認爲commit
。
使用commit=False
的另一個反作用是在模型具備多對多關係的時候。 若是模型具備多對多關係並且當你保存表單時指定commit=False
,Django 不會當即爲多對多關係保存表單數據。 這是由於只有實例在數據庫中存在時才能夠保存實例的多對多數據。
爲了解決這個問題,每當你使用ModelForm
保存表單時,Django 將添加一個save_m2m()
方法到你的commit=False
子類。 在你手工保存由表單生成的實例以後,你能夠調用save_m2m()
來保存多對多的表單數據。 像這樣:
# Create a form instance with POST data. >>> f = AuthorForm(request.POST) # Create, but don't save the new author instance. >>> new_author = f.save(commit=False) # Modify the author in some way. >>> new_author.some_field = 'some_value' # Save the new instance. >>> new_author.save() # Now, save the many-to-many data for the form. >>> f.save_m2m()
save_m2m()僅在你使用save(commit=False)時才須要。 當你直接使用
save()
,全部的數據 —— 包括多對多數據 —— 都將保存而不須要任何額外的方法調用。 像這樣:
# Create a form instance with POST data. >>> a = Author() >>> f = AuthorForm(request.POST, instance=a) # Create and save the new author instance. There's no need to do anything else. >>> new_author = f.save()
除了forms
和ModelForm
方法以外,save_m2m()
與其它save()
的工做方式徹底同樣。 例如,request.FILES
用於檢查合法性,is_multipart()
方法用於決定表單是否須要multipart 的文件上傳(以及這以後is_valid()
是否必須必須傳遞給表單)等等。
(2)選擇要使用的字段
強烈建議你使用fields
屬性顯式設置全部將要在表單中編輯的字段。 若是不這樣作,當表單不當心容許用戶設置某些特定的字段,特別是有的字段添加到模型中的時候,將很容易致使安全問題。 這些問題可能在網頁上根本看不出來,它與表單的渲染方式有關。
另一種方式是自動包含全部的字段,或者排除某些字段。 這種基本方式的安全性要差不少,並且已經致使大型的網站受到嚴重的利用(例如 GitHub)。
然而,有兩種簡單的方法保證你不會出現這些安全問題:
設置'__all__'
屬性爲特殊的值fields
以表示須要使用模型的全部字段。 像這樣:
from django.forms import ModelForm class AuthorForm(ModelForm): class Meta: model = Author fields = '__all__'
設置Meta
內聯的ModelForm
類的exclude
屬性爲一個要從表單中排除的字段的列表。
像這樣:
class PartialAuthorForm(ModelForm): class Meta: model = Author exclude = ['title']
由於Author
模型有3個字段name
、birth_date
和 birth_date
,上面的例子會讓title
和 name
出如今表單中。
若是使用上面兩種方法,表單中字段出現的順序將和字段在模型中定義的順序一致,其中ManyToManyField
出如今最後。
(3)覆蓋默認字段
上文字段類型表中默認的字段類型只是合理的默認值。 若是你的模型中有一個DateField
,你可能想在表單中也將它表示成DateField
。 可是,ModelForm
可讓您靈活地更改給定模型的表單域。
使用內部類Meta
的widgets
屬性能夠指定一個字段的自定義Widget。 它是映射字段名到Widget 類或實例的一個字典。
例如,<textarea>
的Author
屬性爲name
,若是你但願它表示成一個CharField
而不是默認的<input type="text">
,你能夠覆蓋字段默認的Widget:
from django.forms import ModelForm, Textarea from myapp.models import Author class AuthorForm(ModelForm): class Meta: model = Author fields = ('name', 'title', 'birth_date') widgets = { 'name': Textarea(attrs={'cols': 80, 'rows': 20}), }
無論是Widget 實例(Textarea
)仍是Widget 類(Textarea(...)
),widgets
字典均可以接收。
相似地,若是你但願進一步自定義字段,你能夠指定內部類Meta
的error_messages
、help_texts
和labels
。
例如,若是你但願自定義name
字段全部面向用戶的字符串:
from django.utils.translation import ugettext_lazy as _ class AuthorForm(ModelForm): class Meta: model = Author fields = ('name', 'title', 'birth_date') labels = { 'name': _('Writer'), } help_texts = { 'name': _('Some useful help text.'), } error_messages = { 'name': { 'max_length': _("This writer's name is too long."), }, }
您還能夠指定field_classes
來自定義表單實例化的字段類型。
例如,若是你想爲slug
字段使用MySlugFormField
,能夠像下面這樣:
from django.forms import ModelForm from myapp.models import Article class ArticleForm(ModelForm): class Meta: model = Article fields = ['pub_date', 'headline', 'content', 'reporter', 'slug'] field_classes = { 'slug': MySlugFormField, }
最後,若是你想徹底控制一個字段 - 包括它的類型,驗證器,必需的等等。 - 您能夠經過聲明性地指定像常規Form
中的字段來執行此操做。
若是想要指定字段的驗證器,能夠顯式定義字段並設置它的validators
參數:
from django.forms import ModelForm, CharField from myapp.models import Article class ArticleForm(ModelForm): slug = CharField(validators=[validate_slug]) class Meta: model = Article fields = ['pub_date', 'headline', 'content', 'reporter', 'slug']
(4)啓動字段定位
默認狀況下,ModelForm
中的字段不會本地化它們的數據。 你能夠使用Meta
類的localized_fields
屬性來啓用字段的本地化功能。
>>> from django.forms import ModelForm >>> from myapp.models import Author >>> class AuthorForm(ModelForm): ... class Meta: ... model = Author ... localized_fields = ('birth_date',)
若是localized_fields
設置爲'__all__'
這個特殊的值,全部的字段都將本地化。
(5)表單繼承
在基本的表單裏,你能夠經過繼承ModelForms
來擴展和重用他們。 當你的form是經過models生成的,並且須要在父類的基礎上聲明額外的field和method,這種繼承是方便的。 例如,使用之前的ArticleForm
類:
>>> class EnhancedArticleForm(ArticleForm): ... def clean_pub_date(self): ... ...
以上建立了一個與 pub_date
很是相似的form,除了一些額外的驗證和ArticleForm
的cleaning
若是要更改Meta.fields
或Meta.exclude
列表,您還能夠將父類的Meta
子類子類化:
>>> class RestrictedArticleForm(EnhancedArticleForm): ... class Meta(ArticleForm.Meta): ... exclude = ('body',)
上例從父類ArticleForm.Meta
繼承後增長了額外的方法,並修改了 EnhancedArticleForm
排除了一個字段
固然,有一些注意事項
應用正常的Python名稱解析規則。 若是你有多個基類聲明一個Meta
內部類,只會使用第一個。 這意味着孩子的Meta
(若是存在),不然第一個父母的Meta
等。
它能夠同時繼承Form
和ModelForm
,可是,必須確保Form
首先出如今MRO中。 這是由於這些類依賴於不一樣的元類,而一個類只能有一個元類。
能夠經過在子類上將名稱設置爲None
,聲明性地刪除從父類繼承的Field
。
您只能使用此技術從由父類聲明性定義的字段中選擇退出;它不會阻止ModelForm
元類生成默認字段。
(6)提供初始值
做爲一個有參數的表單, 在實例化一個表單時能夠經過指定initial
字段來指定表單中數據的初始值. 這種方式指定的初始值將會同時替換掉表單中的字段和值. 像這樣:
>>> article = Article.objects.get(pk=1) >>> article.headline 'My headline' >>> form = ArticleForm(initial={'headline': 'Initial headline'}, instance=article) >>> form['headline'].value() 'Initial headline'
ModelForm工廠函數
你能夠用單獨的函數 modelform_factory()
來代替使用類定義來從模型直接建立表單。 這在不須要不少自定義的狀況下應該是更方便的。
>>> from django.forms import modelform_factory >>> from myapp.models import Book >>> BookForm = modelform_factory(Book, fields=("author", "title"))
這個函數還能對已有的表單類作簡單的修改,好比,對給出的字段指定 widgets :
>>> from django.forms import Textarea >>> Form = modelform_factory(Book, form=BookForm, ... widgets={"title": Textarea()})
表單包含的字段能夠用 Meta
或ModelForm
關鍵字參數說明,或者用exclude
內部fields
類的相應屬性說明。
(7)模型表單集
models.
BaseModelFormSet
與regular formsets同樣, 它是Django提供的幾個有力的表單集類來簡化模型操做。 讓咱們繼續使用上面的Author
模型:
>>> from django.forms import modelformset_factory >>> from myapp.models import Author >>> AuthorFormSet = modelformset_factory(Author, fields=('name', 'title'))
使用 fields
限定表單集僅能夠使用給出的字段, 或者使用排除法,指定哪些字段被不被使用。
>>> AuthorFormSet = modelformset_factory(Author, exclude=('birth_date',))
下面將建立一個與Author
模型數據相關聯的功能強大的表單集, 與普通表單集運行同樣:
>>> formset = AuthorFormSet() >>> print(formset) <input type="hidden" name="form-TOTAL_FORMS" value="1" id="id_form-TOTAL_FORMS" /><input type="hidden" name="form-INITIAL_FORMS" value="0" id="id_form-INITIAL_FORMS" /><input type="hidden" name="form-MAX_NUM_FORMS" id="id_form-MAX_NUM_FORMS" /> <tr><th><label for="id_form-0-name">Name:</label></th><td><input id="id_form-0-name" type="text" name="form-0-name" maxlength="100" /></td></tr> <tr><th><label for="id_form-0-title">Title:</label></th><td><select name="form-0-title" id="id_form-0-title"> <option value="" selected>---------</option> <option value="MR">Mr.</option> <option value="MRS">Mrs.</option> <option value="MS">Ms.</option> </select><input type="hidden" name="form-0-id" id="id_form-0-id" /></td></tr>
更改查詢集
默認的, 若是你使用model生成formset,formset會使用一個包含模型所有對象的queryset(例如:Author.objects.all()
). 你能夠使用queryset
參數重寫這一行爲:
>>> formset = AuthorFormSet(queryset=Author.objects.filter(name__startswith='O'))
或者,你能夠建立子類設置 __init__
in self.queryset
:
from django.forms import BaseModelFormSet from myapp.models import Author class BaseAuthorFormSet(BaseModelFormSet): def __init__(self, *args, **kwargs): super(BaseAuthorFormSet, self).__init__(*args, **kwargs) self.queryset = Author.objects.filter(name__startswith='O')
而後,將BaseAuthorFormSet
類傳給modelformset_factory函數:
>>> AuthorFormSet = modelformset_factory( ... Author, fields=('name', 'title'), formset=BaseAuthorFormSet)
若是想返回不包含任何已存在模型實例的表單集,能夠指定一個空的查詢集(QuerySet)
>>> AuthorFormSet(queryset=Author.objects.none())
更改表單
默認狀況下,當你使用modelformset_factory
時, modelform_factory()
將會建立一個模型 一般這有助於指定一個自定義模型表單. 例如,你能夠建立一個自定義驗證的表單模型
class AuthorForm(forms.ModelForm): class Meta: model = Author fields = ('name', 'title') def clean_name(self): # custom validation for the name field ...
而後,把你的模型做爲參數傳遞過去
AuthorFormSet = modelformset_factory(Author, form=AuthorForm)
並不老是須要自定義一個模型表單, modelform_factory
函數有幾個參數,能夠傳給modelformset_factory
,他們的說明以下:
以widgets
的形式指定要使用的小部件
使用ModelForm
參數,能夠用字典值自定義widgets
列出字段的widget類。 這與 ModelForm
字典在 Meta
的內部widgets
類做用式同樣。
>>> AuthorFormSet = modelformset_factory( ... Author, fields=('name', 'title'), ... widgets={'name': Textarea(attrs={'cols': 80, 'rows': 20})})
啓用localized_fields
的字段的本地化
使用 localized_fields
參數,能夠使表單中字段啓用本地化。
>>> AuthorFormSet = modelformset_factory( ... Author, fields=('name', 'title', 'birth_date'), ... localized_fields=('birth_date',))
若是'__all__'
設置爲localized_fields
這個特殊的值,全部的字段都將本地化。
(8)在表單中保存對象
作爲 ModelForm
, 你能夠保存數據到模型對象 ,如下就完成了表單集的 save()
方法:
# Create a formset instance with POST data. >>> formset = AuthorFormSet(request.POST) # Assuming all is valid, save the data. >>> instances = formset.save()
save()
方法返回已保存到數據庫的實例。 若是給定實例的數據在綁定數據中沒有更改,那麼實例將不會保存到數據庫,而且不會包含在返回值中(在上面的示例中爲instances
)。
當窗體中缺乏字段(例如由於它們已被排除)時,這些字段不會由save()
方法設置。 您能夠在選擇要使用的字段中找到有關此限制的更多信息,這也適用於常規ModelForms
。
傳遞commit=False
返回未保存的模型實例:
# don't save to the database >>> instances = formset.save(commit=False) >>> for instance in instances: ... # do something with instance ... instance.save()
這使您可以在將數據保存到數據庫以前將數據附加到實例。 若是您的表單集包含formset.save_m2m()
,您還須要調用ManyToManyField
,以確保多對多關係正確保存。
調用save()
以後,您的模型formset將有三個包含formset更改的新屬性:
models.BaseModelFormSet。
changed_objects T0>
models.BaseModelFormSet。
deleted_objects T0>
models.BaseModelFormSet。
new_objects T0>
限制可編輯對象的數量
與普通表單集同樣,你能夠用在modelformset_factory()
中使用 extra
和 max_num
參數,來控制額外表單的顯示數量。
max_num
不會限制已經存在的表單對像的顯示:
>>> Author.objects.order_by('name') <QuerySet [<Author: Charles Baudelaire>, <Author: Paul Verlaine>, <Author: Walt Whitman>]> >>> AuthorFormSet = modelformset_factory(Author, fields=('name',), max_num=1) >>> formset = AuthorFormSet(queryset=Author.objects.order_by('name')) >>> [x.name for x in formset.get_queryset()] ['Charles Baudelaire', 'Paul Verlaine', 'Walt Whitman']
另外,extra=0
不會阻止建立新的模型實例,由於您能夠add additional forms with JavaScript或僅發送其餘POST數據。 對於禁止建立新實例的「僅編輯」視圖,Formsets 還沒有提供功能。
若是 max_num
大於存在的關聯對像的數量,表單集將添加 extra
個額外的空白表單,只要表單總數量不超過 max_num
:
>>> AuthorFormSet = modelformset_factory(Author, fields=('name',), max_num=4, extra=2) >>> formset = AuthorFormSet(queryset=Author.objects.order_by('name')) >>> for form in formset: ... print(form.as_table()) <tr><th><label for="id_form-0-name">Name:</label></th><td><input id="id_form-0-name" type="text" name="form-0-name" value="Charles Baudelaire" maxlength="100" /><input type="hidden" name="form-0-id" value="1" id="id_form-0-id" /></td></tr> <tr><th><label for="id_form-1-name">Name:</label></th><td><input id="id_form-1-name" type="text" name="form-1-name" value="Paul Verlaine" maxlength="100" /><input type="hidden" name="form-1-id" value="3" id="id_form-1-id" /></td></tr> <tr><th><label for="id_form-2-name">Name:</label></th><td><input id="id_form-2-name" type="text" name="form-2-name" value="Walt Whitman" maxlength="100" /><input type="hidden" name="form-2-id" value="2" id="id_form-2-id" /></td></tr> <tr><th><label for="id_form-3-name">Name:</label></th><td><input id="id_form-3-name" type="text" name="form-3-name" maxlength="100" /><input type="hidden" name="form-3-id" id="id_form-3-id" /></td></tr>
None
值爲f max_num
(缺省)設置一個較高的限制可顯示1000個表單。 實際上至關於沒有限制。
(9)在視圖中使用表單
模型表單集與表單集十分相似, 假設咱們想要提供一個表單集來編輯Author
模型實例:
from django.forms import modelformset_factory from django.shortcuts import render from myapp.models import Author def manage_authors(request): AuthorFormSet = modelformset_factory(Author, fields=('name', 'title')) if request.method == 'POST': formset = AuthorFormSet(request.POST, request.FILES) if formset.is_valid(): formset.save() # do something. else: formset = AuthorFormSet() return render(request, 'manage_authors.html', {'formset': formset})
能夠看到,模型表單集的視圖邏輯與「正常」表單集的視圖邏輯沒有顯着不一樣。 惟一的區別是咱們調用formset.save()
將數據保存到數據庫中。
在ModelFormSet
上覆蓋clean()
與ModelForms
同樣,默認狀況下,unique_together
的unique_for_date|month|year
方法將驗證formset中沒有項目違反惟一約束(unique
,ModelFormSet
或clean()
)。 若是要覆蓋clean
上的ModelFormSet
方法並維護此驗證,則必須調用父類的clean()
方法:
from django.forms import BaseModelFormSet class MyModelFormSet(BaseModelFormSet): def clean(self): super(MyModelFormSet, self).clean() # example custom validation across forms in the formset for form in self.forms: # your custom formset validation ...
另請注意,到達此步驟時,已爲每一個Form
建立了各個模型實例。 修改form.cleaned_data
中的值不足以影響保存的值。 若是您但願修改form.instance
中的值,則必須修改ModelFormSet.clean()
:
from django.forms import BaseModelFormSet class MyModelFormSet(BaseModelFormSet): def clean(self): super(MyModelFormSet, self).clean() for form in self.forms: name = form.cleaned_data['name'].upper() form.cleaned_data['name'] = name # update the instance value. form.instance.name = name
使用自定義查詢集
如前所述,您能夠覆蓋模型formset使用的默認查詢集:
from django.forms import modelformset_factory from django.shortcuts import render from myapp.models import Author def manage_authors(request): AuthorFormSet = modelformset_factory(Author, fields=('name', 'title')) if request.method == "POST": formset = AuthorFormSet( request.POST, request.FILES, queryset=Author.objects.filter(name__startswith='O'), ) if formset.is_valid(): formset.save() # Do something. else: formset = AuthorFormSet(queryset=Author.objects.filter(name__startswith='O')) return render(request, 'manage_authors.html', {'formset': formset})
請注意,咱們在此示例中的GET
和POST
中傳遞queryset
參數。
在模板中使用Formset
在Django模板中有三種方式來渲染表單集。
第一種方式,你可讓表單集完成大部分的工做
<form method="post" action=""> {{ formset }} </form>
其次,你能夠手動渲染formset,但讓表單處理本身:
<form method="post" action=""> {{ formset.management_form }} {% for form in formset %} {{ form }} {% endfor %} </form>
當您本身手動呈現表單時,請確保呈現如上所示的管理表單。
第三,您能夠手動呈現每一個字段:
<form method="post" action=""> {{ formset.management_form }} {% for form in formset %} {% for field in form %} {{ field.label_tag }} {{ field }} {% endfor %} {% endfor %} </form>
若是您選擇使用此第三種方法,而且不對{% for %}
loop,你須要渲染主鍵字段。 例如,若是您要渲染模型的age
和name
字段:
<form method="post" action=""> {{ formset.management_form }} {% for form in formset %} {{ form.id }} <ul> <li>{{ form.name }}</li> <li>{{ form.age }}</li> </ul> {% endfor %} </form>
注意咱們須要如何顯式渲染{{ form.id }}
。 這確保了在POST
狀況下的模型形式集將正常工做。 (此示例假設名爲id
的主鍵。 若是您明肯定義了本身的主鍵(不是id
),請確保其呈現)。
(10)表單集
表單集是同一個頁面上多個表單的抽象。 它很是相似於一個數據表格。 假設有下述表單:
>>> from django import forms >>> class ArticleForm(forms.Form): ... title = forms.CharField() ... pub_date = forms.DateField()
你可能但願容許用戶一次建立多個Article。 你能夠根據ArticleForm
建立一個表單集:
>>> from django.forms import formset_factory >>> ArticleFormSet = formset_factory(ArticleForm)
你已經建立一個命名爲ArticleFormSet
的表單集。 表單集讓你能迭表明單集中的表單並顯示它們,就和普通的表單同樣
>>> formset = ArticleFormSet() >>> for form in formset: ... print(form.as_table()) <tr><th><label for="id_form-0-title">Title:</label></th><td><input type="text" name="form-0-title" id="id_form-0-title" /></td></tr> <tr><th><label for="id_form-0-pub_date">Pub date:</label></th><td><input type="text" name="form-0-pub_date" id="id_form-0-pub_date" /></td></tr>
正如你所看到的,這裏僅顯示一個空表單。 顯示的表單的數目經過extra
參數控制。 默認狀況下,formset_factory()
定義了一個額外的形式;如下示例將顯示兩個空格:
>>> ArticleFormSet = formset_factory(ArticleForm, extra=2)
對formset
的迭代將以它們建立時的順序渲染表單。 經過提供一個__iter__()
方法,能夠改變這個順序。
表單集還能夠索引,它將返回對應的表單。 若是覆蓋__iter__
,你還須要覆蓋__getitem__
以得到一致的行爲。
使用formset 的初始數據
初始數據體現着表單集的主要功能。 如上所述,你能夠定義表單的數目。 它表示除了從初始數據生成的表單以外,還要生成多少個額外的表單。 讓咱們看個例子:
>>> import datetime >>> from django.forms import formset_factory >>> from myapp.forms import ArticleForm >>> ArticleFormSet = formset_factory(ArticleForm, extra=2) >>> formset = ArticleFormSet(initial=[ ... {'title': 'Django is now open source', ... 'pub_date': datetime.date.today(),} ... ]) >>> for form in formset: ... print(form.as_table()) <tr><th><label for="id_form-0-title">Title:</label></th><td><input type="text" name="form-0-title" value="Django is now open source" id="id_form-0-title" /></td></tr> <tr><th><label for="id_form-0-pub_date">Pub date:</label></th><td><input type="text" name="form-0-pub_date" value="2008-05-12" id="id_form-0-pub_date" /></td></tr> <tr><th><label for="id_form-1-title">Title:</label></th><td><input type="text" name="form-1-title" id="id_form-1-title" /></td></tr> <tr><th><label for="id_form-1-pub_date">Pub date:</label></th><td><input type="text" name="form-1-pub_date" id="id_form-1-pub_date" /></td></tr> <tr><th><label for="id_form-2-title">Title:</label></th><td><input type="text" name="form-2-title" id="id_form-2-title" /></td></tr> <tr><th><label for="id_form-2-pub_date">Pub date:</label></th><td><input type="text" name="form-2-pub_date" id="id_form-2-pub_date" /></td></tr>
上面如今一共有三個表單。 一個是初始數據生成的,還有兩個是額外的表單。 還要注意的是,咱們傳遞的初始數據是一個由字典組成的列表。
若是您使用initial
來顯示錶單集,則在處理該表單的提交時,應該傳遞相同的initial
,以便表單集能夠檢測用戶更改哪些表單。例如,您可能有如下相似的東西:ArticleFormSet(request.POST, initial = [...])
。
限制表單的最大數量
formset_factory()
的 max_num
參數 ,給予你限制表單集展現表單個數的能力
>>> from django.forms import formset_factory >>> from myapp.forms import ArticleForm >>> ArticleFormSet = formset_factory(ArticleForm, extra=2, max_num=1) >>> formset = ArticleFormSet() >>> for form in formset: ... print(form.as_table()) <tr><th><label for="id_form-0-title">Title:</label></th><td><input type="text" name="form-0-title" id="id_form-0-title" /></td></tr> <tr><th><label for="id_form-0-pub_date">Pub date:</label></th><td><input type="text" name="form-0-pub_date" id="id_form-0-pub_date" /></td></tr>
假如 max_num
的值 比已經在初始化數據中存在的條目數目多的話, max_num
對應個數的額外空表單將會被添加到表單集, 只要表單總數不超過 extra
. 例如,若是initial
和extra=2
,而且用一個max_num=2
項初始化表單集,將顯示空白表單。
假如初始化數據的條目超過 max_num
的值, 全部初始化數據表單都會被展示而且忽視 max_num
值的限定 ,並且不會有額外的表單被呈現。 好比, 若是extra=3
,max_num=1
而且表單集由兩個初始化條蜜,那麼兩個帶有初始化數據的表單將被呈現。
max_num
的值爲 None
(默認值) 等同於限制了一個比較高的展示表單數目(1000個). 實際上就是等同於沒限制.
默認的, max_num
隻影響了表單的數目展現,但不影響驗證. 假如 max_num
傳給了 formset_factory()
, 而後 validate_max=True
纔將會影響驗證. 見validate_max。
表單驗證
表單集的驗證幾乎和 通常的Form
同樣. 表單集裏面有一個 is_valid
的方法來提供快捷的驗證全部表單的功能。
>>> from django.forms import formset_factory
>>> from myapp.forms import ArticleForm
>>> ArticleFormSet = formset_factory(ArticleForm)
>>> data = {
... 'form-TOTAL_FORMS': '1',
... 'form-INITIAL_FORMS': '0',
... 'form-MAX_NUM_FORMS': '',
... }
>>> formset = ArticleFormSet(data)
>>> formset.is_valid()
True
咱們沒有傳遞任何數據到formset,致使一個有效的形式。 表單集足夠聰明,能夠忽略未更改的其餘表單。 若是咱們提供無效的文章:
>>> data = { ... 'form-TOTAL_FORMS': '2', ... 'form-INITIAL_FORMS': '0', ... 'form-MAX_NUM_FORMS': '', ... 'form-0-title': 'Test', ... 'form-0-pub_date': '1904-06-16', ... 'form-1-title': 'Test', ... 'form-1-pub_date': '', # <-- this date is missing but required ... } >>> formset = ArticleFormSet(data) >>> formset.is_valid() False >>> formset.errors [{}, {'pub_date': ['This field is required.']}]
正如咱們看見的, formset.errors
是一個列表, 他包含的錯誤信息正好與表單集內的表單一一對應 錯誤檢查會在兩個表單中分別執行,被預見的錯誤出現錯誤列表的第二項
就像使用正常的Form
同樣,表單集的表單中的每一個字段均可能包含HTML屬性,例如用於瀏覽器驗證的maxlength
。 可是,formets的表單域不會包含required
屬性,由於添加和刪除表單時驗證可能不正確。
BaseFormSet。
total_error_count
()[source]
想知道表單集內有多少個錯誤能夠使用total_error_count
方法
>>> # Using the previous example
>>> formset.errors
[{}, {'pub_date': ['This field is required.']}]
>>> len(formset.errors)
2
>>> formset.total_error_count()
1
咱們也能夠檢查表單數據是否從初始值發生了變化 (i.e. the form was sent without any data):
>>> data = { ... 'form-TOTAL_FORMS': '1', ... 'form-INITIAL_FORMS': '0', ... 'form-MAX_NUM_FORMS': '', ... 'form-0-title': '', ... 'form-0-pub_date': '', ... } >>> formset = ArticleFormSet(data) >>> formset.has_changed() False
瞭解ManagementForm
你也許已經注意到了那些附加的數據 (form-MAX_NUM_FORMS
, form-TOTAL_FORMS
and form-INITIAL_FORMS
) 他們是必要的,且必須位於表單集數據的最上方 這些必須傳遞給ManagementForm
. ManagementFormThis 用於管理表單集中的表單. 若是你不提供這些數據,將會觸發異常
>>> data = { ... 'form-0-title': 'Test', ... 'form-0-pub_date': '', ... } >>> formset = ArticleFormSet(data) >>> formset.is_valid() Traceback (most recent call last): ... django.forms.utils.ValidationError: ['ManagementForm data is missing or has been tampered with']
也一樣用於記錄多少的表單實例將被展現 若是您經過JavaScript添加新表單,則應該增長此表單中的計數字段。 On the other hand, if you are using JavaScript to allow deletion of existing objects, then you need to ensure the ones being removed are properly marked for deletion by including form-#-DELETE
in the POST
data. 指望全部形式存在於POST
數據中。
管理表單可用做表單集自己的屬性。 在模板中呈現表單集時,您能夠經過呈現{{ my_formset.management_form }} t0>(替換您的formset的名稱適當)。
total_form_count
和initial_form_count
initial_form_count
有一些與total_form_count
,BaseFormSet
和ManagementForm
密切相關的方法。
total_form_count
返回此表單集中的表單總數。 initial_form_count
返回Formset中預填充的表單數,也用於肯定須要多少表單。你可能永遠不須要重寫這些方法,因此請確保你明白他們作什麼以前這樣作。
empty_form
__prefix__
提供了一個附加屬性BaseFormSet
,它返回一個前綴爲empty_form
的表單實例,以便於使用JavaScript的動態表
自定義表單驗證
一個formset有一個相似於Form
類的clean
方法。 這是您定義本身的驗證,在formset級別工做:
>>> from django.forms import BaseFormSet >>> from django.forms import formset_factory >>> from myapp.forms import ArticleForm >>> class BaseArticleFormSet(BaseFormSet): ... def clean(self): ... """Checks that no two articles have the same title.""" ... if any(self.errors): ... # Don't bother validating the formset unless each form is valid on its own ... return ... titles = [] ... for form in self.forms: ... title = form.cleaned_data['title'] ... if title in titles: ... raise forms.ValidationError("Articles in a set must have distinct titles.") ... titles.append(title) >>> ArticleFormSet = formset_factory(ArticleForm, formset=BaseArticleFormSet) >>> data = { ... 'form-TOTAL_FORMS': '2', ... 'form-INITIAL_FORMS': '0', ... 'form-MAX_NUM_FORMS': '', ... 'form-0-title': 'Test', ... 'form-0-pub_date': '1904-06-16', ... 'form-1-title': 'Test', ... 'form-1-pub_date': '1912-06-23', ... } >>> formset = ArticleFormSet(data) >>> formset.is_valid() False >>> formset.errors [{}, {}] >>> formset.non_form_errors() ['Articles in a set must have distinct titles.']
在全部clean
方法被調用後,調用formset Form.clean
方法。 將使用表單集上的non_form_errors()
方法找到錯誤。
驗證表單集中的表單數
Django 提供了兩種方法去檢查表單可以提交的最大數和最小數, 應用若是須要更多的關於提交數量的自定義驗證邏輯,應該使用自定義表單擊驗證
validate_max
I若是max_num
被提交給 formset_factory()
, validation 將在數據集中檢查被提交表單的數量, 減去被標記刪除的, 必須小於等於validate_max=True
.
>>> from django.forms import formset_factory >>> from myapp.forms import ArticleForm >>> ArticleFormSet = formset_factory(ArticleForm, max_num=1, validate_max=True) >>> data = { ... 'form-TOTAL_FORMS': '2', ... 'form-INITIAL_FORMS': '0', ... 'form-MIN_NUM_FORMS': '', ... 'form-MAX_NUM_FORMS': '', ... 'form-0-title': 'Test', ... 'form-0-pub_date': '1904-06-16', ... 'form-1-title': 'Test 2', ... 'form-1-pub_date': '1912-06-23', ... } >>> formset = ArticleFormSet(data) >>> formset.is_valid() False >>> formset.errors [{}, {}] >>> formset.non_form_errors() ['Please submit 1 or fewer forms.']
max_num
validates 將會對validate_max=True
嚴格限制,即便提供的初始數據超過 max_num
而致使其無效
validate_min
若是min_num
被傳遞到formset_factory()
,驗證也將檢查數據集中的表格數量減去那些被標記爲刪除的表格數量大於或等於到validate_min=True
。
>>> from django.forms import formset_factory >>> from myapp.forms import ArticleForm >>> ArticleFormSet = formset_factory(ArticleForm, min_num=3, validate_min=True) >>> data = { ... 'form-TOTAL_FORMS': '2', ... 'form-INITIAL_FORMS': '0', ... 'form-MIN_NUM_FORMS': '', ... 'form-MAX_NUM_FORMS': '', ... 'form-0-title': 'Test', ... 'form-0-pub_date': '1904-06-16', ... 'form-1-title': 'Test 2', ... 'form-1-pub_date': '1912-06-23', ... } >>> formset = ArticleFormSet(data) >>> formset.is_valid() False >>> formset.errors [{}, {}] >>> formset.non_form_errors() ['Please submit 3 or more forms.']
處理表單的排序和刪除
formset_factory()
提供兩個可選參數can_order
和can_delete
來實現表單集中表單的排序和刪除。
can_order
BaseFormSet。
can_order T0>
默認值:False
使你建立能排序的表單集。
>>> from django.forms import formset_factory >>> from myapp.forms import ArticleForm >>> ArticleFormSet = formset_factory(ArticleForm, can_order=True) >>> formset = ArticleFormSet(initial=[ ... {'title': 'Article #1', 'pub_date': datetime.date(2008, 5, 10)}, ... {'title': 'Article #2', 'pub_date': datetime.date(2008, 5, 11)}, ... ]) >>> for form in formset: ... print(form.as_table()) <tr><th><label for="id_form-0-title">Title:</label></th><td><input type="text" name="form-0-title" value="Article #1" id="id_form-0-title" /></td></tr> <tr><th><label for="id_form-0-pub_date">Pub date:</label></th><td><input type="text" name="form-0-pub_date" value="2008-05-10" id="id_form-0-pub_date" /></td></tr> <tr><th><label for="id_form-0-ORDER">Order:</label></th><td><input type="number" name="form-0-ORDER" value="1" id="id_form-0-ORDER" /></td></tr> <tr><th><label for="id_form-1-title">Title:</label></th><td><input type="text" name="form-1-title" value="Article #2" id="id_form-1-title" /></td></tr> <tr><th><label for="id_form-1-pub_date">Pub date:</label></th><td><input type="text" name="form-1-pub_date" value="2008-05-11" id="id_form-1-pub_date" /></td></tr> <tr><th><label for="id_form-1-ORDER">Order:</label></th><td><input type="number" name="form-1-ORDER" value="2" id="id_form-1-ORDER" /></td></tr> <tr><th><label for="id_form-2-title">Title:</label></th><td><input type="text" name="form-2-title" id="id_form-2-title" /></td></tr> <tr><th><label for="id_form-2-pub_date">Pub date:</label></th><td><input type="text" name="form-2-pub_date" id="id_form-2-pub_date" /></td></tr> <tr><th><label for="id_form-2-ORDER">Order:</label></th><td><input type="number" name="form-2-ORDER" id="id_form-2-ORDER" /></td></tr>
它會給每一個表單添加一個字段, 這個新字段名爲ORDER
,是一個forms.IntegerField
。 它根據初始數據,爲這些表單自動生成數值。 下面讓咱們看一下,若是用戶改變這個值會發生什麼變化:
>>> data = { ... 'form-TOTAL_FORMS': '3', ... 'form-INITIAL_FORMS': '2', ... 'form-MAX_NUM_FORMS': '', ... 'form-0-title': 'Article #1', ... 'form-0-pub_date': '2008-05-10', ... 'form-0-ORDER': '2', ... 'form-1-title': 'Article #2', ... 'form-1-pub_date': '2008-05-11', ... 'form-1-ORDER': '1', ... 'form-2-title': 'Article #3', ... 'form-2-pub_date': '2008-05-01', ... 'form-2-ORDER': '0', ... } >>> formset = ArticleFormSet(data, initial=[ ... {'title': 'Article #1', 'pub_date': datetime.date(2008, 5, 10)}, ... {'title': 'Article #2', 'pub_date': datetime.date(2008, 5, 11)}, ... ]) >>> formset.is_valid() True >>> for form in formset.ordered_forms: ... print(form.cleaned_data) {'pub_date': datetime.date(2008, 5, 1), 'ORDER': 0, 'title': 'Article #3'} {'pub_date': datetime.date(2008, 5, 11), 'ORDER': 1, 'title': 'Article #2'} {'pub_date': datetime.date(2008, 5, 10), 'ORDER': 2, 'title': 'Article #1'}
can_delete
BaseFormSet。
can_delete T0>
默認值:False
使你建立一個表單集,能夠選擇刪除一些表單。
>>> from django.forms import formset_factory >>> from myapp.forms import ArticleForm >>> ArticleFormSet = formset_factory(ArticleForm, can_delete=True) >>> formset = ArticleFormSet(initial=[ ... {'title': 'Article #1', 'pub_date': datetime.date(2008, 5, 10)}, ... {'title': 'Article #2', 'pub_date': datetime.date(2008, 5, 11)}, ... ]) >>> for form in formset: ... print(form.as_table()) <tr><th><label for="id_form-0-title">Title:</label></th><td><input type="text" name="form-0-title" value="Article #1" id="id_form-0-title" /></td></tr> <tr><th><label for="id_form-0-pub_date">Pub date:</label></th><td><input type="text" name="form-0-pub_date" value="2008-05-10" id="id_form-0-pub_date" /></td></tr> <tr><th><label for="id_form-0-DELETE">Delete:</label></th><td><input type="checkbox" name="form-0-DELETE" id="id_form-0-DELETE" /></td></tr> <tr><th><label for="id_form-1-title">Title:</label></th><td><input type="text" name="form-1-title" value="Article #2" id="id_form-1-title" /></td></tr> <tr><th><label for="id_form-1-pub_date">Pub date:</label></th><td><input type="text" name="form-1-pub_date" value="2008-05-11" id="id_form-1-pub_date" /></td></tr> <tr><th><label for="id_form-1-DELETE">Delete:</label></th><td><input type="checkbox" name="form-1-DELETE" id="id_form-1-DELETE" /></td></tr> <tr><th><label for="id_form-2-title">Title:</label></th><td><input type="text" name="form-2-title" id="id_form-2-title" /></td></tr> <tr><th><label for="id_form-2-pub_date">Pub date:</label></th><td><input type="text" name="form-2-pub_date" id="id_form-2-pub_date" /></td></tr> <tr><th><label for="id_form-2-DELETE">Delete:</label></th><td><input type="checkbox" name="form-2-DELETE" id="id_form-2-DELETE" /></td></tr>
與can_order
相似,這爲每一個名爲DELETE
的表單添加一個新字段,是一個forms.BooleanField
。 以下,你能夠經過deleted_forms
來獲取標記刪除字段的數據:
>>> data = { ... 'form-TOTAL_FORMS': '3', ... 'form-INITIAL_FORMS': '2', ... 'form-MAX_NUM_FORMS': '', ... 'form-0-title': 'Article #1', ... 'form-0-pub_date': '2008-05-10', ... 'form-0-DELETE': 'on', ... 'form-1-title': 'Article #2', ... 'form-1-pub_date': '2008-05-11', ... 'form-1-DELETE': '', ... 'form-2-title': '', ... 'form-2-pub_date': '', ... 'form-2-DELETE': '', ... } >>> formset = ArticleFormSet(data, initial=[ ... {'title': 'Article #1', 'pub_date': datetime.date(2008, 5, 10)}, ... {'title': 'Article #2', 'pub_date': datetime.date(2008, 5, 11)}, ... ]) >>> [form.cleaned_data for form in formset.deleted_forms] [{'DELETE': True, 'pub_date': datetime.date(2008, 5, 10), 'title': 'Article #1'}]
若是你使用 ModelFormSet
,調用 formset.save()
將刪除那些有刪除標記的表單的模型實例。
若是你調用formset.save(commit=False)
, 對像將不會被自動刪除。 你須要調用formset.deleted_objects
每一個對像的 delete()
來真正刪除他們。
將自定義參數傳遞給表單集
有時,您的表單類會使用自定義參數,例如MyArticleForm
。 在實例化表單集時能夠傳遞此參數:
>>> from django.forms import BaseFormSet >>> from django.forms import formset_factory >>> from myapp.forms import ArticleForm >>> class MyArticleForm(ArticleForm): ... def __init__(self, *args, **kwargs): ... self.user = kwargs.pop('user') ... super(MyArticleForm, self).__init__(*args, **kwargs) >>> ArticleFormSet = formset_factory(MyArticleForm) >>> formset = ArticleFormSet(form_kwargs={'user': request.user})
form_kwargs
也可能取決於具體的窗體實例。 formset基類提供了一個get_form_kwargs
方法。 該方法採用單個參數 - 表單中的表單的索引。 empty_form的索引爲None
:
>>> from django.forms import BaseFormSet >>> from django.forms import formset_factory >>> class BaseArticleFormSet(BaseFormSet): ... def get_form_kwargs(self, index): ... kwargs = super(BaseArticleFormSet, self).get_form_kwargs(index) ... kwargs['custom_kwarg'] = index ... return kwargs
在視圖和模板中使用表單集
在視圖中使用表單集就像使用標準的Form
類同樣簡單, 惟一要作的就是確信你在模板中處理表單。 讓咱們看一個簡單視圖:
from django.forms import formset_factory from django.shortcuts import render from myapp.forms import ArticleForm def manage_articles(request): ArticleFormSet = formset_factory(ArticleForm) if request.method == 'POST': formset = ArticleFormSet(request.POST, request.FILES) if formset.is_valid(): # do something with the formset.cleaned_data pass else: formset = ArticleFormSet() return render(request, 'manage_articles.html', {'formset': formset})
manage_articles.html
模板也能夠像這樣:
<form method="post" action=""> {{ formset.management_form }} <table> {% for form in formset %} {{ form }} {% endfor %} </table> </form>
不過,上面能夠用一個快捷寫法,讓表單集來分發管理表單:
<form method="post" action=""> <table> {{ formset }} </table> </form>
上面表單集調用 as_table
方法。
手動呈現can_delete
和can_order
若是手動在模板中渲染字段,則能夠使用{{ form.DELETE }}呈現
can_delete
參數/ T5> T2>:
<form method="post" action=""> {{ formset.management_form }} {% for form in formset %} <ul> <li>{{ form.title }}</li> <li>{{ form.pub_date }}</li> {% if formset.can_delete %} <li>{{ form.DELETE }}</li> {% endif %} </ul> {% endfor %} </form>
相似地,若是表單集有能力(can_order=True
),則能夠使用{{ form.ORDER t4> }}
。
在視圖中使用多個表單集
能夠在視圖中使用多個表單集, 表單集從表單中借鑑了不少方法 你能夠使用 prefix
給每一個表單字段添加前綴,以容許多個字段傳遞給視圖,而不發生命名衝突 讓咱們看看能夠怎麼作
from django.forms import formset_factory from django.shortcuts import render from myapp.forms import ArticleForm, BookForm def manage_articles(request): ArticleFormSet = formset_factory(ArticleForm) BookFormSet = formset_factory(BookForm) if request.method == 'POST': article_formset = ArticleFormSet(request.POST, request.FILES, prefix='articles') book_formset = BookFormSet(request.POST, request.FILES, prefix='books') if article_formset.is_valid() and book_formset.is_valid(): # do something with the cleaned_data on the formsets. pass else: article_formset = ArticleFormSet(prefix='articles') book_formset = BookFormSet(prefix='books') return render(request, 'manage_articles.html', { 'article_formset': article_formset, 'book_formset': book_formset, })
你能夠以正常的方式渲染模板。 記住 prefix
在POST請求和非POST 請求中均需設置,以便他能渲染和執行正確
(11)表單和字段驗證
表單驗證發生在數據驗證以後。 若是你想自定義這個過程,有不一樣的地方能夠進行更改,每一個都有不一樣的用途。 表單處理過程當中要運行三種類別的驗證方法。 它們一般在你調用表單的is_valid()
方法時執行。 還有其餘一些事情也能夠觸發清理和驗證(訪問errors
屬性或直接調用full_clean()
),但一般不須要它們。
通常狀況下,若是處理的數據有問題,每一個類別的驗證方法都會引起ValidationError
,並將相關信息傳遞給ValidationError
。See below中引起ValidationError
的最佳實踐。 若是沒有引起ValidationError
,這些方法應該返回驗證後的(規整化的)數據的Python 對象。
大部分應該能夠使用validators 完成,它們能夠很容易地重用。 Validators 是簡單的函數(或可調用對象),它們接收一個參數並對非法的輸入拋出ValidationError
。 Validators 在字段的to_python
和validate
方法調用以後運行。
表單的驗證分爲幾個步驟,能夠自定義或覆蓋:
Field
上的to_python()
方法是每次驗證的第一步。 它強制該值爲正確的數據類型,並引起ValidationError
,若是這是不可能的。 這個方法從Widget 接收原始的值並返回轉換後的值。 例如,一個FloatField
將數據轉換成一個Python float
或者提起一個ValidationError
。
Field
上的validate()
方法處理不適合驗證器的字段特定驗證。 它須要一個被強制爲正確的數據類型的值,並在任何錯誤上引起ValidationError
。 這個方法不返回任何東西且不該該改變任何值。 當你遇到不能夠或不想放在validator 中的驗證邏輯時,應該覆蓋它來處理驗證。
Field
上的run_validators()
方法運行全部字段的驗證器,並將全部錯誤聚合到單個ValidationError
中。 你應該不須要覆蓋這個方法。
Field
子類的clean()
方法負責運行to_python()
,validate()
和run_validators()
以正確的順序傳播錯誤。 若是任什麼時候刻、任何方法引起ValidationError
,驗證將中止並引起這個錯誤。 這個方法返回驗證後的數據,這個數據在後面將插入到表單的 cleaned_data
字典中。
在表單子類中調用clean_<fieldname>()
方法,其中<fieldname>
替換爲表單域屬性的名稱。 這個方法完成於特定屬性相關的驗證,這個驗證與字段的類型無關。 這個方法沒有任何傳入的參數。 你須要查找clean()
中該字段的值,記住此時它已是一個Python 對象而不是表單中提交的原始字符串(它位於cleaned_data
中是由於字段的self.cleaned_data
方法已經驗證過一次數據)。
例如,若是你想驗證名爲clean_serialnumber()
的serialnumber
的內容是否惟一, CharField
將是實現這個功能的理想之處。你須要的不是一個特別的字段(它只是一個CharField
),而是一個特定於表單字段特定驗證,並規整化數據。
此方法的返回值將替換cleaned_data
中的現有值,所以它必須是來自cleaned_data
的字段值(即便此方法未更改)或新的清潔價值。
表單子類的clean()
方法能夠執行須要訪問多個表單字段的驗證。 這是您能夠在哪裏進行檢查,例如「若是提供了字段A
,字段B
必須包含有效的電子郵件地址」。 這個方法能夠返回一個徹底不一樣的字典,該字典將用做cleaned_data
。
由於字段的驗證方法在調用clean()
時會運行,你還能夠訪問表單的errors
屬性,它包含驗證每一個字段時的全部錯誤。
注意,你覆蓋的Form.clean()
引起的任何錯誤將不會與任何特定的字段關聯。 它們位於一個特定的「字段」(叫作__all__
)中,若是須要能夠經過 non_field_errors()
方法訪問。 若是你想添加一個特定字段的錯誤到表單中,須要調用 add_error()
。
還要注意,覆蓋clean()
子類的ModelForm
方法須要特殊的考慮。 (更多信息參見ModelForm documentation)。
這些方法按以上給出的順序執行,一次驗證一個字段。 也就是說,對於表單中的每一個字段(按它們在表單定義中出現的順序),先運行Field.clean()
,而後運行clean_<fieldname>()
。 每一個字段的這兩個方法都執行完以後,最後運行Form.clean()
方法,不管前面的方法是否拋出過異常。
下面有上面每一個方法的示例。
咱們已經提到過,全部這些方法均可以拋出ValidationError
。 對於任何字段,若是Field.clean()
方法引起了一個ValidationError
,則不會調用任何字段特定的清除方法。 可是,剩餘的字段的驗證方法仍然會執行。
ValidationError
爲了讓錯誤信息更加靈活或容易重寫,請考慮下面的準則:給構造函數提供一個富有描述性的錯誤碼code
:
# Good ValidationError(_('Invalid value'), code='invalid') # Bad ValidationError(_('Invalid value'))
不要將變量強加到消息中;使用佔位符和構造函數的params
參數:
# Good ValidationError( _('Invalid value: %(value)s'), params={'value': '42'}, ) # Bad ValidationError(_('Invalid value: %s') % value)
使用字典參數而不要用位置參數。 這使得重寫錯誤信息時不用考慮變量的順序或者徹底省略它們:
# Good ValidationError( _('Invalid value: %(value)s'), params={'value': '42'}, ) # Bad ValidationError( _('Invalid value: %s'), params=('42',), )
用gettext
封裝錯誤消息使得它能夠翻譯:
# Good ValidationError(_('Invalid value')) # Bad ValidationError('Invalid value')
全部的準則放在一塊兒就是:
raise ValidationError( _('Invalid value: %(value)s'), code='invalid', params={'value': '42'}, )
若是你想編寫可重用的表單、表單字段和模型字段,遵照這些準則是很是必要的。
若是你在驗證的最後(例如,表單的clean()
方法)且知道永遠 不須要從新錯誤信息,雖然不提倡但你仍然能夠選擇重寫不詳細的信息:
ValidationError(_('Invalid value: %s') % value)
Form.errors.as_data()
和Form.errors.as_json()
方法很大程度上受益於code
(利用params
名和ValidationError
字典)。
提升多個錯誤
若是在一個驗證方法中檢查到多個錯誤而且但願將它們都反饋給表單的提交者,能夠傳遞一個錯誤的列表給ValidationError
構造函數。
和上面同樣,建議傳遞的列表中的params
實例都帶有 code
和ValidationError
,可是傳遞一個字符串列表也能夠工做:
# Good raise ValidationError([ ValidationError(_('Error 1'), code='error1'), ValidationError(_('Error 2'), code='error2'), ]) # Bad raise ValidationError([ _('Error 1'), _('Error 2'), ])
在實踐中使用驗證
前面幾節解釋在通常狀況下表單的驗證是如何工做的。 由於有時直接看功能在實際中的應用會更容易掌握,下面是一些列小例子,它們用到前面的每一個功能。
使用驗證器
Django 的表單(以及模型)字段支持使用簡單的函數和類用於驗證,它們叫作Validator。 Validator 是可調用對象或函數,它接收一個值,若是該值合法則什麼也不返回,不然拋出ValidationError
。 它們能夠經過字段的validators
參數傳遞給字段的構造函數,或者定義在Field
類的default_validators
屬性中。
簡單的Validator 能夠用於在字段內部驗證值,讓咱們看下Django 的SlugField
:
from django.forms import CharField from django.core import validators class SlugField(CharField): default_validators = [validators.validate_slug]
正如你所看到的,SlugField
只是一個帶有自定義Validator 的CharField
,它們驗證提交的文本符合某些字符規則。 這也能夠在字段定義時實現,因此:
slug = forms.SlugField()
等同於:
slug = forms.CharField(validators=[validators.validate_slug])
常見的情形,例如驗證郵件地址和正則表達式,能夠使用Django 中已經存在的Validator 類處理。 例如,validators.validate_slug
是RegexValidator
的一個實例,它構造時的第一個參數爲:^[-a-zA-Z0-9_]+$
。 writing validators 一節能夠查到已經存在的Validator 以及如何編寫Validator 的一個示例。
表單域默認清除
讓咱們首先建立一個自定義的表單字段,它驗證其輸入是一個由逗號分隔的郵件地址組成的字符串。 完整的類像這樣:
from django import forms from django.core.validators import validate_email class MultiEmailField(forms.Field): def to_python(self, value): """Normalize data to a list of strings.""" # Return an empty list if no input was given. if not value: return [] return value.split(',') def validate(self, value): """Check if value consists only of valid emails.""" # Use the parent's handling of required fields, etc. super(MultiEmailField, self).validate(value) for email in value: validate_email(email)
使用這個字段的每一個表單都將在處理該字段數據以前運行這些方法。 這個驗證特定於該類型的字段,與後面如何使用它無關。
讓咱們來建立一個簡單的ContactForm
來向你演示如何使用這個字段:
class ContactForm(forms.Form): subject = forms.CharField(max_length=100) message = forms.CharField() sender = forms.EmailField() recipients = MultiEmailField() cc_myself = forms.BooleanField(required=False)
只須要簡單地使用MultiEmailField
,就和其它表單字段同樣。 當調用表單的to_python()
方法時,MultiEmailField.clean()
方法將做爲驗證過程的一部分運行,它將調用自定義的is_valid()
和validate()
方法。
清理特定字段屬性
繼續前面的例子,假設在ContactForm
中,咱們要確保recipients
字段始終包含地址"fred@example.com"
這是對咱們表單特定的驗證,因此咱們不想把它放在通常的MultiEmailField
類中。 相反,咱們寫一個在recipients
字段上運行的清理方法,像這樣:
from django import forms class ContactForm(forms.Form): # Everything as before. ... def clean_recipients(self): data = self.cleaned_data['recipients'] if "fred@example.com" not in data: raise forms.ValidationError("You have forgotten about Fred!") # Always return a value to use as the new cleaned data, even if # this method didn't change it. return data
清理和驗證相互依賴的字段
假設咱們向聯繫表單添加了另外一個要求:若是cc_myself
字段是True
,則subject
必須包含單詞"help"
咱們一次在多個字段上執行驗證,所以表單的clean()
方法是一個很好的選擇。 請注意,咱們正在談論這裏的表單上的clean()
方法,而較早的咱們在一個字段上寫了一個clean()
方法。 在肯定哪些地方進行驗證時,保持領域和形式差別很重要。 字段是單個數據點,表單是字段的集合。
在調用表單clean()
方法的時候,全部字段的驗證方法已經執行完(前兩節),因此self.cleaned_data
填充的是目前爲止已經合法的數據。 因此你須要記住這個事實,你須要驗證的字段可能沒有經過初試的字段檢查。
在這一步,有兩種方法報告錯誤。 最簡單的方法是在表單的頂端顯示錯誤。 你能夠在ValidationError
方法中拋出clean()
來建立錯誤。 像這樣:
from django import forms class ContactForm(forms.Form): # Everything as before. ... def clean(self): cleaned_data = super(ContactForm, self).clean() cc_myself = cleaned_data.get("cc_myself") subject = cleaned_data.get("subject") if cc_myself and subject: # Only do something if both fields are valid so far. if "help" not in subject: raise forms.ValidationError( "Did not send for 'help' in the subject despite " "CC'ing yourself." )
在這段代碼中,若是拋出驗證錯誤,表單將在表單的頂部顯示(一般是)描述該問題的一個錯誤信息。
在示例代碼中調用super(ContactForm, self).clean()
能夠確保父類中的任何驗證邏輯都被維護。 If your form inherits another that doesn’t return a cleaned_data
dictionary in its clean()
method (doing so is optional), then don’t assigncleaned_data
to the result of the super()
call and use self.cleaned_data
instead:
def clean(self): super(ContactForm, self).clean() cc_myself = self.cleaned_data.get("cc_myself") ...
報告驗證錯誤的第二種方法可能包括將錯誤消息分配給其中一個字段。 在這種狀況下,讓咱們在表單的顯示中分別關聯一個錯誤信息到「subject」 和「cc_myself」 行。 在實際應用中要當心,由於它可能致使表單的輸出變得使人困惑。 咱們只是向你展現這裏能夠怎麼作,在特定的狀況下,須要你和你的設計人員肯定什麼是好的方法。 咱們的新代碼(代替前面的示例)像這樣:
from django import forms class ContactForm(forms.Form): # Everything as before. ... def clean(self): cleaned_data = super(ContactForm, self).clean() cc_myself = cleaned_data.get("cc_myself") subject = cleaned_data.get("subject") if cc_myself and subject and "help" not in subject: msg = "Must put 'help' in subject when cc'ing yourself." self.add_error('cc_myself', msg) self.add_error('subject', msg)
add_error()
的第二個參數能夠是一個簡單的字符串,但更傾向是ValidationError
的一個實例。 更多細節參見Raising ValidationError。 注意,add_error()
將從cleaned_data
中刪除相應的字段。
Django認證系統同時處理認證和受權。 簡單地講,認證驗證一個用戶是否它們聲稱的那我的,受權決定一個經過了認證的用戶被容許作什麼。 這裏的詞語「認證」同時指代這兩項任務。
認證系統包含:
Django中的認證系統致力於變得很是通用,但它不提供在web認證系統中某些常見的功能。 某些常見問題的解決方法已經在第三方包中實現:
安裝
認證的支持做爲Django的一個contrib模塊,打包於django.contrib.auth
中。 默認狀況下,要求的配置已經包含在django-admin startproject
生成的settings.py
中,它們的組成包括INSTALLED_APPS
設置中的兩個選項:
'django.contrib.auth'
包含認證框架的核心和默認的模型。'django.contrib.contenttypes'
是Django內容類型系統,它容許權限與你建立的模型關聯。和MIDDLEWARE
設置中的這些條目:
SessionMiddleware
跨請求管理sessions。AuthenticationMiddleware
使用會話將用戶與請求關聯起來。有了這些設置,運行manage.py migrate
命令將爲認證相關的模型建立必要的數據庫表併爲你的應用中定義的任意模型建立權限。
這篇文檔解釋默認配置下Django認證系統的使用。 這些配置已經逐步能夠知足大部分常見項目的須要,能夠處理範圍很是普遍的任務,且具備一套細緻的密碼和權限實現。 對於須要與默認配置不一樣需求的項目,Django支持extension and customization認證。
Django的認證同時提供認證和受權,並一般統一稱爲認證系統,由於這些功能某些地方是耦合的。
User
對象
User
對象是認證系統的核心。 它們一般表示與你的站點進行交互的用戶,並用於啓用限制訪問、註冊用戶信息和給建立者關聯內容等。 在Django的認證框架中只存在一種類型的用戶,所以諸如'superusers'
或管理員'staff'
用戶只是具備特殊屬性集的user對象,而不是不一樣類型的user對象。
默認user的基本屬性有:
完整的參考請參閱full API documentation
,如下的內容更偏重特定的任務。
建立用戶
建立users最直接的方法是使用create_user()
輔助函數:
>>> from django.contrib.auth.models import User >>> user = User.objects.create_user('john', 'lennon@thebeatles.com', 'johnpassword') # 到這裏,user 這一個User對象已經保存於 # 數據庫中了。 # 你能夠繼續修改它的屬性。 # 若是你想要修改其餘字段。 >>> user.last_name = 'Lennon' >>> user.save()
若是你已經安裝了Django admin,你也能夠create users interactively.
建立超級用戶
使用createsuperuser
命令建立superusers:
$ python manage.py createsuperuser --username=joe --email=joe@example.com
將會提示你輸入一個密碼。 在你輸入一個密碼後,該user將會當即建立。 若是您離開--username
或--email
選項,它將提示您輸入這些值。
更改密碼
Django不會在user模型上存儲原始的(明文)密碼,而只是一個哈希(完整的細節參見documentation of how passwords are managed)。 由於這個緣由,不要嘗試直接操做user的password屬性。 這也是爲何建立一個user時要使用輔助函數。
若要修改一個用戶的密碼,你有幾種選擇:
manage.py changepassword *username*
提供了一種從命令行更改用戶密碼的方法。 它提示你修改一個給定user的密碼,你必須輸入兩次。 若是它們匹配,新的密碼將會當即修改。 若是你沒有提供user,命令行將嘗試修改與當前系統用戶匹配的用戶名的密碼。
你也能夠經過程序修改密碼,使用set_password()
:
>>> from django.contrib.auth.models import User >>> u = User.objects.get(username='john') >>> u.set_password('new password') >>> u.save()
若是你安裝了Django admin,你還能夠在authentication system’s admin pages修改user的密碼。
Django還提供views和forms用於容許user修改他們本身密碼。
更改用戶密碼將會註銷全部會話。 詳細信息請參閱Session invalidation on password change。
認證用戶
authenticate
(request=None, **credentials)[source]
使用authenticate()
來驗證一組憑據。 它以credentials爲關鍵字參數,默認爲username
和password
,根據每一個認證的後端進行檢查,若是credentials對某個後端有效則返回一個User
對象。 若是credentials對任何後端都無效,或者若是後端引起了PermissionDenied
,則返回None
。 像這樣:
from django.contrib.auth import authenticate user = authenticate(username='john', password='secret') if user is not None: # A backend authenticated the credentials else: # No backend authenticated the credentials
request
是可選的HttpRequest
,它在認證後端的authenticate()
方法上傳遞。
權限和受權
Django自己提供了一個簡單的權限系統。 它提供了一種爲特定用戶和用戶組分配權限的方法。
它被Django的admin站點使用,但歡迎你在你本身的代碼中使用。
Django admin 站點使用以下的權限:
權限不但能夠根據每一個對象的類型,並且能夠根據特定的對象實例設置。 經過使用ModelAdmin
類提供的has_add_permission()
、has_change_permission()
和has_delete_permission()
方法,能夠針對相同類型的不一樣對象實例自定義權限。
User
對象具備兩個多對多的字段:groups
和user_permissions
。 User
對象能夠用和其它Django模型同樣的方式訪問它們相關聯的對象:
myuser.groups.set([group_list])
myuser.groups.add(group, group, ...)
myuser.groups.remove(group, group, ...)
myuser.groups.clear()
myuser.user_permissions.set([permission_list])
myuser.user_permissions.add(permission, permission, ...)
myuser.user_permissions.remove(permission, permission, ...)
myuser.user_permissions.clear()
默認權限
當django.contrib.auth
在你的INSTALLED_APPS
設置中列出時,它將確保爲你安裝的應用中的每一個Django模型建立3個默認的權限 – add、change和delete。
當你運行manage.py migrate
時,將建立這些權限;在django.contrib.auth
添加到INSTALLED_APPS
以後,首次運行migrate
時,將爲全部先前安裝的模型建立默認權限,以及當時安裝的任何新模型。 以後,每次運行manage.py migrate
,它將爲新的模型建立默認的權限(建立權限的函數與post_migrate
信號鏈接)。
假設你有個app_label
叫作foo
的應用,這個應用有一個名爲Bar
的模型,要測試基本的權限,你應該使用:
user.has_perm('foo.add_bar')
user.has_perm('foo.change_bar')
user.has_perm('foo.delete_bar')
不多直接訪問Permission
模型。
Groups
django.contrib.auth.models.Group
模型是用戶分類的一種通用的方式,經過這種方式你能夠應用權限或其它標籤到這些用戶。 一個用戶能夠屬於任意多個組。
組中某個用戶自動具備賦給那個組的權限。 例如,若是組Site editors
具備權限 can_edit_home_page
,那麼該組中的任何用戶都具備該權限。
除權限以外,組仍是給用戶分類的一種方便的方法以給他們某些標籤或擴展的功能。 例如,你能夠建立一個組'Special users'
,而後你能夠這樣寫代碼,給他們訪問你的站點僅限會員的部分,或者給他們發僅限於會員的郵件。
以編程方式建立權限
雖然custom permissions能夠定義在模型的Meta
類中,但你也能夠直接建立權限。 例如,您能夠在myapp
中爲BlogPost
模型建立can_publish
權限:
from myapp.models import BlogPost from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType content_type = ContentType.objects.get_for_model(BlogPost) permission = Permission.objects.create( codename='can_publish', name='Can Publish Posts', content_type=content_type, )
權限的緩存
在第一次獲取權限用於檢查後,模型的後端
將在該用戶對象上緩存這些權限。 這對於常見的請求-響應週期一般沒問題,由於一般在添加權限後不會當即檢查權限(例如在管理後臺中)。 若是你要添加權限並當即檢查它們,例如在測試中或視圖中,最簡單的解決方案是從數據庫從新獲取用戶。 像這樣:
from django.contrib.auth.models import Permission, User from django.contrib.contenttypes.models import ContentType from django.shortcuts import get_object_or_404 from myapp.models import BlogPost def user_gains_perms(request, user_id): user = get_object_or_404(User, pk=user_id) # any permission check will cache the current set of permissions user.has_perm('myapp.change_blogpost') content_type = ContentType.objects.get_for_model(BlogPost) permission = Permission.objects.get( codename='change_blogpost', content_type=content_type, ) user.user_permissions.add(permission) # Checking the cached permission set user.has_perm('myapp.change_blogpost') # False # Request new instance of User # Be aware that user.refresh_from_db() won't clear the cache. user = get_object_or_404(User, pk=user_id) # Permission cache is repopulated from the database user.has_perm('myapp.change_blogpost') # True ...
Web請求中的認證
它們在每一個請求上提供一個request.user
屬性,表示當前的用戶。 若是當前的用戶沒有登入,該屬性將設置成AnonymousUser
的一個實例,不然它將是User
的實例。
你能夠使用is_authenticated
將它們區分開,以下所示:
if request.user.is_authenticated: # Do something for authenticated users. ... else: # Do something for anonymous users. ...
如何登陸用戶
若是你有一個認證了的用戶,你想把它附帶到當前的會話中 - 這能夠經過login()
函數完成。
login
(request, user, backend=None)[source]
從視圖中登入一個用戶,請使用login()
。 它接受一個HttpRequest
對象和一個User
對象。 login()
使用Django的session框架來將用戶的ID保存在session中。
請注意,匿名會話期間的任何數據集在用戶登陸後都會保留在會話中。
下面的示例向你演示如何使用authenticate()
和login()
:
from django.contrib.auth import authenticate, login def my_view(request): username = request.POST['username'] password = request.POST['password'] user = authenticate(request, username=username, password=password) if user is not None: login(request, user) # Redirect to a success page. ... else: # Return an 'invalid login' error message. ...
在舊版本中,當你手工登錄一個用戶時,在調用login()
以前必須用authenticate()
成功認證這個用戶。 如今你能夠使用新的backend
參數設置後端。
選擇驗證後端
用戶登陸時,用戶的ID和用於身份驗證的後端保存在用戶的會話中。 這容許相同的身份驗證後端在未來的請求中獲取用戶的詳細信息。 要保存在會話中的認證後端選擇以下:
backend
參數的值(若是提供)。user.backend
屬性的值(若是存在)。 這容許配對authenticate()
和login()
:authenticate()
設置user.backend
屬性用戶對象返回。AUTHENTICATION_BACKENDS
中的backend
。在狀況1和2中,backend
參數或user.backend
屬性的值應爲點號導入路徑字符串(如AUTHENTICATION_BACKENDS
的字符串),而不是實際的類。
如何登出用戶
logout
(request)[source]
若要登出一個已經經過django.contrib.auth.login()
登入的用戶,能夠在你的視圖中使用django.contrib.auth.logout()
。 它接收一個HttpRequest
對象且沒有返回值。 例如:
from django.contrib.auth import logout def logout_view(request): logout(request) # Redirect to a success page.
注意,即便用戶沒有登入,logout()
也不會拋出任何錯誤。
當您調用logout()
時,當前請求的會話數據將被完全清除。 全部存在的數據都將清除。 這是爲了防止另一我的使用相同的Web瀏覽器登入並訪問前一個用戶的會話數據。 若是你想在用戶登出以後能夠當即訪問放入會話中的數據,請在調用django.contrib.auth.logout()
以後放入。
限制對登陸用戶的訪問
原始方式
限制訪問頁面的簡單原始方法是檢查request.user.is_authenticated
,並重定向到登陸頁面:
from django.conf import settings from django.shortcuts import redirect def my_view(request): if not request.user.is_authenticated: return redirect('%s?next=%s' % (settings.LOGIN_URL, request.path)) # ...
...或顯示錯誤信息:
from django.shortcuts import render def my_view(request): if not request.user.is_authenticated: return render(request, 'myapp/login_error.html') # ...
login_required
裝飾器
login_required
(redirect_field_name='next', login_url=None)[source]
做爲一個快捷方式,你能夠使用便捷的login_required()
裝飾器:
from django.contrib.auth.decorators import login_required @login_required def my_view(request): ...
login_required()
完成下面的事情:
settings.LOGIN_URL
,傳遞查詢字符串中的當前絕對路徑。 例如:/accounts/login/?next=/polls/3/
。默認狀況下,在成功認證後用戶應該被重定向的路徑存儲在查詢字符串的一個叫作"next"
的參數中。 若是對該參數你傾向使用一個不一樣的名字,login_required()
帶有一個可選的redirect_field_name
參數:
from django.contrib.auth.decorators import login_required @login_required(redirect_field_name='my_redirect_field') def my_view(request): ...
注意,若是你提供一個值給redirect_field_name
,你很是可能同時須要自定義你的登陸模板,由於存儲重定向路徑的模板上下文變量將使用"next"
值做爲它的鍵,而不是默認的redirect_field_name
。
login_required()
還帶有一個可選的login_url
參數。 例如:
from django.contrib.auth.decorators import login_required @login_required(login_url='/accounts/login/') def my_view(request): ...
注意,若是你沒有指定login_url
參數,你須要確保settings.LOGIN_URL
與你的登陸視圖正確關聯。 例如,使用默認值,能夠添加下面幾行到你的URLconf中:
from django.contrib.auth import views as auth_views url(r'^accounts/login/$', auth_views.LoginView.as_view()),
settings.LOGIN_URL
同時還接收視圖函數名和named URL patterns。 這容許你自由地從新映射你的URLconf中的登陸視圖而不用更新設置。
LoginRequired
mixin
使用class-based views時,能夠使用LoginRequiredMixin
實現與login_required
相同的行爲。 此mixin應位於繼承列表中最左側的位置。
LoginRequiredMixin
若是視圖正在使用此mixin,那麼根據raise_exception
參數,未經身份驗證的用戶的全部請求將被重定向到登陸頁面或顯示HTTP 403 Forbidden錯誤。
您能夠設置AccessMixin
的任何參數來自定義未受權用戶的處理:
from django.contrib.auth.mixins import LoginRequiredMixin class MyView(LoginRequiredMixin, View): login_url = '/login/' redirect_field_name = 'redirect_to'
限制對經過測試的登陸用戶的訪問
要根據某些權限或某些其餘測試來限制訪問權限,您能夠執行與上一節中所述基本相同的操做。
簡單的方法就是在視圖中直接運行你對request.user
的測試。 例如,視圖檢查用戶的郵件屬於特定的地址(例如@example.com),若不是,則重定向到登陸頁面。
from django.shortcuts import redirect def my_view(request): if not request.user.email.endswith('@example.com'): return redirect('/login/?next=%s' % request.path) # ...
user_passes_test
(test_func, login_url=None, redirect_field_name='next')[source]
你能夠用方便的 False
裝飾器,當回調函數返回 user_passes_test
時會執行一個重定向操做:
from django.contrib.auth.decorators import user_passes_test def email_check(user): return user.email.endswith('@example.com') @user_passes_test(email_check) def my_view(request): ...
user_passes_test()
要求一個以User
對象爲參數的回調函數,若用戶容許訪問此視圖,返回 True
。 注意,user_passes_test()
不會自動檢查 User
是否爲匿名對象。
user_passes_test()
接收兩個額外的參數:
LOGIN_URL
settings.LOGIN_URL
。
redirect_field_name
login_required()
的參數相同。 把它設置爲 None
來把它從 URL 中移除,當你想把通不過檢查的用戶重定向到沒有next page 的非登陸頁面時。
像這樣:
@user_passes_test(email_check, login_url='/login/') def my_view(request): ...
class UserPassesTestMixin
當使用class-based views時,能夠使用UserPassesTestMixin
來執行此操做。
test_func
()
您必須覆蓋類的test_func()
方法來提供執行的測試。 此外,您能夠設置AccessMixin
的任何參數來自定義未受權用戶的處理:
from django.contrib.auth.mixins import UserPassesTestMixin class MyView(UserPassesTestMixin, View): def test_func(self): return self.request.user.email.endswith('@example.com')
get_test_func
()
您也能夠覆蓋get_test_func()
方法以使mixin對其檢查使用不一樣命名的函數(而不是test_func()
)。
permission_required
(perm, login_url=None, raise_exception=False)[source]
檢查一個用戶是否有指定的權限是相對常見的需求。 爲此,Django爲這種狀況提供了一個快捷方式:permission_required()
裝飾器。:
from django.contrib.auth.decorators import permission_required @permission_required('polls.can_vote') def my_view(request): ...
就像has_perm()
方法同樣,權限名稱採用"<app label>.<permission codename>"
的形式,(例如polls.can_vote
表示polls
應用中一個模型的權限)。
裝飾器也能夠採起可迭代的權限,在這種狀況下,用戶必須具備全部權限才能訪問視圖。
請注意,permission_required()
還須要一個可選的login_url
參數:
from django.contrib.auth.decorators import permission_required @permission_required('polls.can_vote', login_url='/loginpage/') def my_view(request): ...
與login_required()
裝飾器同樣,login_url
默認爲settings.LOGIN_URL
。
若是提供了 raise_exception
參數,裝飾器拋出PermissionDenied
異常,使用 the 403 (HTTP Forbidden) view而不是重定向到登陸頁面。
若是你想使用raise_exception
,還可讓你的用戶有機會先登陸,你能夠添加login_required()
裝飾器:
from django.contrib.auth.decorators import login_required, permission_required @login_required @permission_required('polls.can_vote', raise_exception=True) def my_view(request): ...
PermissionRequiredMixin
mixin 要對class-based views應用權限檢查,能夠使用PermissionRequiredMixin
:
PermissionRequiredMixin
這個mixin,就像permission_required
裝飾器同樣,檢查訪問視圖的用戶是否具備全部給定的權限。 您應該使用permission_required
參數指定權限(或許可的迭代):
from django.contrib.auth.mixins import PermissionRequiredMixin class MyView(PermissionRequiredMixin, View): permission_required = 'polls.can_vote' # Or multiple of permissions: permission_required = ('polls.can_open', 'polls.can_edit')
您能夠設置AccessMixin
的任何參數來自定義未受權用戶的處理。
您也能夠覆蓋這些方法:
get_permission_required
()
返回由mixin使用的許可名稱的可迭代。 默認爲permission_required
屬性,若有必要,轉換爲元組。
has_permission
()
返回一個布爾值,表示當前用戶是否具備執行裝飾視圖的權限。 默認狀況下,返回使用get_permission_required()
返回的權限列表調用has_perms()
的結果。
Django自帶的認證系統足夠應付大多數狀況,但你可能有特殊的需求,現成的默認認證系統不能知足。 自定義本身的項目的權限系統須要瞭解Django中哪些部分是可以擴展或替換的。 這個文檔提供瞭如何定製權限系統的細節。
認證後端系統是可擴展的,可用於User模型存儲的用戶名和密碼與Django的默認不一樣的服務進行認證。
你可爲你的模型提供自定義權限,它們能夠經過Django認證系統進行檢查。
指定認證後端
在底層,Django維護一個「認證後端」的列表。 當調用django.contrib.auth.authenticate()
時 — 如何登入一個用戶中所描述的 — Django 會嘗試全部的認證後端進行認證。 若是第一個認證方法失敗,Django 將嘗試第二個,以此類推,直至試完全部的認證後臺。
使用的認證後臺經過AUTHENTICATION_BACKENDS
設置指定。 這應該是指向Python類的Python路徑名的列表,它們知道如何進行身份驗證。 這些類能夠位於Python 路徑上任何地方。
默認狀況下,AUTHENTICATION_BACKENDS
設置爲:
['django.contrib.auth.backends.ModelBackend']
這個基本的認證後臺會檢查Django 的用戶數據庫並查詢內建的權限。 它不會經過任何的速率限制機制防禦暴力破解。 你能夠在自定義的認證後端中實現本身的速率控制機制,或者使用大部分Web 服務器提供的機制。
AUTHENTICATION_BACKENDS
的順序很重要,因此若是用戶名和密碼在多個後臺中都是合法的,Django 將在第一個匹配成功後中止處理。
若是後臺引起PermissionDenied
異常,認證將當即失敗。 Django 不會檢查後面的認證後臺。
一旦用戶被認證過,Django會在用戶的session中存儲他使用的認證後端,而後在session有效期中一直會爲該用戶提供此後端認證。 這種高效意味着驗證源被緩存基於per-session基礎, 因此若是你改變 AUTHENTICATION_BACKENDS
, 若是你須要迫使用戶從新認證,須要清除掉 session 數據. 一個簡單的方式是使用這個方法:Session.objects.all().delete()
.
編寫認證後端
認證後端是一個類,它實現兩個必需方法:get_user(user_id)
和authenticate(request, **credentials)
,以及一組可選的與權限相關的認證方法。
get_user
方法使用一個user_id
,能夠是用戶名,數據庫ID等等,但必須是用戶對象的主鍵,並返回一個用戶對象。
authenticate
方法須要一個request
參數和一些憑據做爲關鍵字參數。 大多數狀況下,代碼以下︰
class MyBackend(object): def authenticate(self, request, username=None, password=None): # Check the username/password and return a user. ...
固然,它也能夠接收token的方式做爲參數,例如:
class MyBackend(object): def authenticate(self, request, token=None): # Check the token and return a user. ...
不管哪一種方式,authenticate()
應該檢查它得到的憑據,若是憑據有效,則返回與這些憑據匹配的用戶對象。 若是不合法,則返回 None
.
request
is an HttpRequest
and may be None
if it wasn’t provided to authenticate()
(例如密碼在後端).
Django管理員與Django User object緊密耦合。 處理這種狀況的最好方法是爲您的後端存在的每一個用戶建立一個Django User
對象(例如,在LDAP目錄,外部SQL數據庫等中) 你能夠先寫一個腳原本作這件事, 或者用你的 authenticate
方法在用戶登錄的時候完成這件事。
這裏有一個例子,後臺對你定義在 settings.py
文件裏的用戶和密碼進行驗證,而且在用第一次驗證的時候建立一個 User
對象:
from django.conf import settings from django.contrib.auth.hashers import check_password from django.contrib.auth.models import User class SettingsBackend(object): """ Authenticate against the settings ADMIN_LOGIN and ADMIN_PASSWORD. Use the login name and a hash of the password. 像這樣: ADMIN_LOGIN = 'admin' ADMIN_PASSWORD = 'pbkdf2_sha256$30000$Vo0VlMnkR4Bk$qEvtdyZRWTcOsCnI/oQ7fVOu1XAURIZYoOZ3iq8Dr4M=' """ def authenticate(self, request, username=None, password=None): login_valid = (settings.ADMIN_LOGIN == username) pwd_valid = check_password(password, settings.ADMIN_PASSWORD) if login_valid and pwd_valid: try: user = User.objects.get(username=username) except User.DoesNotExist: # Create a new user. There's no need to set a password # because only the password from settings.py is checked. user = User(username=username) user.is_staff = True user.is_superuser = True user.save() return user return 沒有 def get_user(self, user_id): try: return User.objects.get(pk=user_id) except User.DoesNotExist: return 沒有
在自定義後端處理受權
自定義驗證後端能提供本身的權限。
當認證後端完成了這些功能 (get_group_permissions()
, get_all_permissions()
, has_perm()
, and has_module_perms()
) 那麼user model就會給它授予相對應的許可。
提供給用戶的權限將是全部後端返回的全部權限的超集。 也就是說,只要任意一個backend授予了一個user權限,django就給這個user這個權限。
若是後端在has_perm()
或has_module_perms()
中引起PermissionDenied
異常,受權將當即失敗,Django不會檢查接下來的後端認證。
上述的簡單backend能夠至關容易的完成授予admin權限。
class SettingsBackend(object): ... def has_perm(self, user_obj, perm, obj=None): return user_obj.username == settings.ADMIN_LOGIN
class Task(models.Model): ... class Meta: permissions = ( ("view_task", "Can see available tasks"), ("change_task_status", "Can change the status of tasks"), ("close_task", "Can remove a task by setting its status as closed"), )
惟一的作法是在運行manage.py migrate
時建立這些額外的權限(建立權限的功能鏈接到post_migrate
信號)。 當用戶嘗試訪問應用程序提供的功能(查看任務,更改任務狀態,關閉任務)時,您的代碼負責檢查這些權限的值。 繼續上面的示例,如下檢查用戶是否能夠查看任務:
user.has_perm('app.view_task')
擴展示有的User
模型
有兩種方法來擴展默認的User
模型,而不用替換你本身的模型。 若是你須要的只是行爲上的改變,而不須要對數據庫中存儲的內容作任何改變,你能夠建立基於User
的proxy model。 代理模型提供的功能包括默認的排序、自定義管理器以及自定義模型方法。
若是您但願存儲與User
相關的信息,則能夠使用OneToOneField
到包含其餘信息字段的模型。 這種 one-to-one 模型通常被稱爲資料模型(profile model),它一般被用來存儲一些有關網站用戶的非驗證性( non-auth )資料。 例如,你能夠建立一個員工模型 (Employee model):
from django.contrib.auth.models import User class Employee(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE) department = models.CharField(max_length=100)
假設一個員工Fred Smith 既有User 模型又有Employee 模型,你能夠使用Django 標準的關聯模型訪問相關聯的信息:
>>> u = User.objects.get(username='fsmith') >>> freds_department = u.employee.department
要將我的資料模型的字段添加到管理後臺的用戶頁面中,請在應用程序的UserAdmin
定義一個InlineModelAdmin
(對於本示例,咱們將使用StackedInline
)並將其添加到admin.py
類並向User
類註冊的:
from django.contrib import admin from django.contrib.auth.admin import UserAdmin as BaseUserAdmin from django.contrib.auth.models import User from my_user_profile_app.models import Employee # Define an inline admin descriptor for Employee model # which acts a bit like a singleton class EmployeeInline(admin.StackedInline): model = Employee can_delete = False verbose_name_plural = 'employee' # Define a new User admin class UserAdmin(BaseUserAdmin): inlines = (EmployeeInline, ) # Re-register UserAdmin admin.site.unregister(User) admin.site.register(User, UserAdmin)
這些配置文件模型在任何狀況下都不是特別的 - 它們只是Django模型,與用戶模型發生一對一的連接。 所以,當建立用戶時,它們不會自動建立,但能夠使用django.db.models.signals.post_save
來適當地建立或更新相關模型。
使用相關模型會產生其餘查詢或聯接來檢索相關數據。 根據您的需求,包含相關字段的自定義用戶模型多是您更好的選擇,可是,與項目應用程序中的默認用戶模型的現有關係可能有助於額外的數據庫加載。
替換User
模型
某些類型的項目可能有特殊的認證需求,Django內建的User
模型不可能老是適用。 例如,在某些網站上使用郵件地址而不是用戶名做爲身份的標識可能更合理。
經過提供一個值給AUTH_USER_MODEL
設置,指向自定義的模型,Django容許你覆蓋默認的User模型:
AUTH_USER_MODEL = 'myapp.MyUser'
這個點式路徑包含Django應用的名稱(必須位於你的INSTALLED_APPS
中),和要用做User模型的Django模型的名稱。
在項目開始時使用自定義User模型
若是你正在開始一個新項目,強烈建議你設置一個自定義用戶模型,即便默認的User
模型對你已經足夠可用。 下面的模型的行爲與默認的用戶模型相同,可是未來若是須要你能夠自定義它:
from django.contrib.auth.models import AbstractUser class User(AbstractUser): pass
不要忘記將AUTH_USER_MODEL
指向它。 在建立任何遷移或首次運行manage.py migrate
以前執行此操做。
另外,在應用程序的admin.py
中註冊該模型:
from django.contrib import admin from django.contrib.auth.admin import UserAdmin from .models import User admin.site.register(User, UserAdmin)
在項目中期修改成自定義的User模型
建立數據庫表以後,更改AUTH_USER_MODEL
是很是困難的,由於它會影響外鍵和多對多關係。
此更改沒法自動完成,須要手動修復模式、從舊用戶表移動數據、並可能須要手動從新應用某些遷移。 有關步驟的概述,請參見#25313。
因爲Django對於可交換模型的動態依賴特性的限制,必須在其應用的第一次遷移(一般稱爲0001_initial
)中建立AUTH_USER_MODEL
引用的模型;不然你會有依賴問題。
另外,當運行遷移時,你可能遇到一個CircularDependencyError
,由於Django將沒法自動中斷因爲動態依賴關係的依賴關係循環。若是看到此錯誤,應該經過將你的用戶模型所依賴的模型移動到第二次遷移中來打破循環。 (You can try making two normal models that have a ForeignKey
to each other and seeing how makemigrations
resolves that circular dependency if you want to see how it’s usually done.)
可重用的應用和AUTH_USER_MODEL
可重用的應用不該實現自定義用戶模型。 一個項目可能使用多個應用,實現自定義用戶模型的兩個可重用應用不能一塊兒使用。 若是須要在應用中存儲用戶的信息,請使用ForeignKey
或OneToOneField
到settings.AUTH_USER_MODEL
,以下所述。
引用User
模型
若是直接引用User
(例如,經過外鍵引用),在AUTH_USER_MODEL
設置已更改成不一樣用戶模型的項目中,代碼將不能工做。
get_user_model
()[source]
你應該使用django.contrib.auth.get_user_model()
來引用用戶模型,而不要直接引用User
。 此方法將返回當前活動的用戶模型 — 若是指定了自定義用戶模型,不然返回User
。
在定義到用戶模型的外鍵或多對多關係時,應使用AUTH_USER_MODEL
設置指定自定義模型。 像這樣:
from django.conf import settings from django.db import models class Article(models.Model): author = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.CASCADE, )
當鏈接到用戶模型發送的信號時,應該使用AUTH_USER_MODEL
設置指定自定義模型。 像這樣:
from django.conf import settings from django.db.models.signals import post_save def post_save_receiver(sender, instance, created, **kwargs): pass post_save.connect(post_save_receiver, sender=settings.AUTH_USER_MODEL)
通常來講,在導入時執行的代碼中,使用AUTH_USER_MODEL
設置來引用用戶模型是最簡單的,但Django也能夠調用get_user_model()
導入模型,因此你能夠使用models.ForeignKey(get_user_model(), ...)
。
若是你的應用使用多個用戶模型進行測試,例如使用@override_settings(AUTH_USER_MODEL=...)
,並將get_user_model()
的結果緩存在模塊級別變量中,你可能須要監聽setting_changed
信號以清除緩存。 像這樣:
from django.apps import apps from django.contrib.auth import get_user_model from django.core.signals import setting_changed from django.dispatch import receiver @receiver(setting_changed) def user_model_swapped(**kwargs): if kwargs['setting'] == 'AUTH_USER_MODEL': apps.clear_cache() from myapp import some_module some_module.UserModel = get_user_model()
字段
models.
User
User
對象具備以下字段:
username
必選。 150個字符之內。 用戶名可能包含字母數字,_
,@
,+
.
和-
個字符。
對於許多用例,max_length
應該是足夠的。 若是您須要較長的長度,請使用custom user model。 若是您使用具備utf8mb4
編碼(推薦用於正確的Unicode支持)的MySQL,請 至少指定max_length=191
,由於MySQL只能建立具備191個字符的惟一索引,默認。
first_name
可選(blank=True
)。 少於等於30個字符。
last_name
可選(blank=True
)。 少於等於30個字符。
email
可選(blank=True
)。 郵箱地址。
password
必選。 密碼的哈希及元數據。 (Django 不保存原始密碼)。 原始密碼能夠無限長並且能夠包含任意字符。 參見password documentation。
groups
與Group
之間的多對多關係。
user_permissions
與Permission
之間的多對多關係。
is_staff
布爾值。 指示用戶是否能夠訪問Admin 站點。
is_active
布爾值。 指示用戶的帳號是否激活。 咱們建議您將此標誌設置爲False
而不是刪除賬戶;這樣,若是您的應用程序對用戶有任何外鍵,則外鍵不會中斷。
它不是用來控制用戶是否可以登陸。 不須要驗證後端來檢查is_active
標誌,而是默認後端(ModelBackend
)和RemoteUserBackend
。 若是要容許非活動用戶登陸,您能夠使用AllowAllUsersModelBackend
或AllowAllUsersRemoteUserBackend
。 在這種狀況下,您還須要自定義LoginView
使用的AuthenticationForm
,由於它拒絕了非活動用戶。 請注意,諸如has_perm()
等權限檢查方法,Django管理員中的身份驗證所有返回爲非活動用戶的False
。
在舊版本中,ModelBackend
和RemoteUserBackend
容許非活動用戶進行身份驗證。
is_superuser
布爾值。 指定這個用戶擁有全部的權限而不須要給他們分配明確的權限。
last_login
用戶最後一次登陸的時間。
date_joined
帳戶建立的時間。 當帳號建立時,默認設置爲當前的date/time。
屬性
models.
User
is_authenticated
始終爲True
(與AnonymousUser.is_authenticated
相對,始終爲False
)的只讀屬性。 這是區分用戶是否已經認證的一種方法。這並不表示任何權限,也不會檢查用戶是否處於活動狀態或是否具備有效的會話。 即便正常狀況下,您將在request.user
上檢查此屬性,以瞭解它是否已由AuthenticationMiddleware
填充(表示當前登陸的用戶),您應該知道對於任何User
實例,此屬性爲True
。
is_anonymous
始終爲False
的只讀屬性。 這是區別User
和AnonymousUser
對象的一種方法。 通常來講,您應該優先使用is_authenticated
到此屬性。
在舊版本中,這是一種方法。 使用它做爲方法的向後兼容性支持將在Django 2.0中被刪除。
username_validator
指向用於驗證用戶名的驗證器實例。 Python 3上的默認值爲validators.UnicodeUsernameValidator
和Python 3上的validators.ASCIIUsernameValidator
。
要更改默認用戶名驗證器,能夠將User
模型子類化,並將此屬性設置爲不一樣的驗證器實例。 例如,要在Python 3上使用ASCII用戶名:
from django.contrib.auth.models import 用戶 from django.contrib.auth.validators import ASCIIUsernameValidator class CustomUser(User): username_validator = ASCIIUsernameValidator() class Meta: proxy = True # If no new field is added.
方法
models.
User
get_username
()
返回這個User 的username。 因爲能夠將User
模型交換出來,您應該使用此方法,而不是直接引用用戶名屬性。
get_full_name
()
返回first_name
和last_name
,之間帶有一個空格。
get_short_name
()
返回first_name
。
set_password
(raw_password)
設置用戶的密碼爲給定的原始字符串,並負責密碼的哈希。 不會保存User
對象。
當None
爲raw_password
時,密碼將設置爲一個不可用的密碼,和使用set_unusable_password()
的效果同樣。
check_password
(raw_password)
Returns True
if the given raw string is the correct password for the user. (它負責在比較時密碼的哈希)。
set_unusable_password
()
標記用戶爲沒有設置密碼。 它與密碼爲空的字符串不同。 check_password()
對這種用戶永遠不會返回True
。 不會保存User
對象。
若是你的認證發生在外部例如LDAP 目錄時,可能須要這個函數。
has_usable_password
()
若是對這個用戶調用過set_unusable_password()
,則返回False
。
get_group_permissions
(obj=None)
返回一個用戶當前擁有的權限的set,經過用戶組
若是傳入obj
,則僅返回此特定對象的組權限。http://python.usyiyi.cn/translate/django_182/ref/contrib/auth.html#
get_all_permissions
(obj=None)
經過組和用戶權限返回用戶擁有的一組權限字符串。
若是傳入obj
,則僅返回此特定對象的權限。
has_perm
(perm, obj=None)
若是用戶具備指定的權限,則返回True
,其中perm的格式爲"<app label>.<permission codename>"
。 (請參閱有關permissions)。 若是用戶沒有激活,這個方法將永遠返回 False
。
若是傳入obj
,此方法將不會檢查模型的權限,而是檢查此特定對象。
has_perms
(perm_list, obj=None)
Returns True
if the user has each of the specified permissions, where each perm is in the format"<app label>.<permission codename>"
. 若是用戶沒有激活,這個方法將永遠返回 False
。
若是傳入obj
,此方法將不會檢查模型的權限,而是檢查特定對象。
has_module_perms
(package_name)
若是用戶具備給出的package_name(Django應用的標籤)中的任何一個權限,則返回True
。 若是用戶沒有激活,這個方法將永遠返回