django-admin startproject [項目的名稱]
python manage.py runserver
python manage.py runserver 9000
。讓項目運行到額時候,host爲0.0.0.0。css
python manage.py runserver 0.0.0.0:8000
。0.0.0.0
。在settings.py
文件中,配置ALLOWED_HOSTS
,將本機的ip地址添加進去。示例代碼以下:html
ALLOWED_HOSTS = ['192.168.0.103']
注意:要關閉本身電腦的防火牆才行。前端
manange.py
:之後和項目交互基本上都是基於這個文件。通常都是在終端輸入python manage.py [子命令]。能夠輸入python manage.py help看下能作什麼事情。除非你知道你本身在作什麼,通常狀況下不該該編輯這個文件。settings.py
:保存項目全部的配置信息。urls.py
:用來作url與視圖函數映射的。之後來了一個請求,就會從這個文件中找到匹配的視圖函數。wsig.py
:專門用來作部署的。不須要修改。按照功能或者模塊進行分層,分紅一個個app。全部和某個模塊相關的視圖都寫在對應的app的views.py中,而且模型和其餘的也是相似。而後django已經提供了一個比較方便建立app的命令叫作python manage.py startapp [app的名稱]
。把全部的代碼寫在各自的app中。python
這個變量是用來設置之後別人只能經過這個變量中的ip地址或者域名來進行訪問。mysql
django.http.response.HttpResponseBase
的子類的對象。不使用緩存去加載頁面ctrl+shift+r
linux
settings.py
文件中配置了ROOT_URLCONF
爲urls.py
。全部django會去urls.py
中尋找。urls.py
中咱們全部的映射,都應該放在urlpatterns
這個變量中。path
函數或者是re_path
函數進行包裝的。採用在url中使用變量的方式:在path的第一個參數中,使用<參數名>
的方式能夠傳遞參數。而後在視圖函數中也要寫一個參數,視圖函數中的參數必須和url中的參數名稱保持一致,否則就找不到這個參數。另外,url中能夠傳遞多個參數。path("book/detail/<book_id>/<category_id>/",views.book_detail)
多個參數git
採用查詢字符串的方式:在url中,不須要單獨的匹配查詢字符串的部分。只須要在視圖函數中使用request.GET.get('參數名稱')
的方式來獲取。示例代碼以下:github
def author_detail(request): author_id = request.GET['id'] text = '做者的id是:%s' % author_id return HttpResponse(text)
由於查詢字符串使用的是GET
請求,因此咱們經過request.GET
來獲取參數。而且由於GET
是一個相似於字典的數據類型,全部獲取值跟字典的方式都是同樣的。web
path("book/publisher/<path:publisher_id>/",views.publisher_detail)
正則表達式
/
之外全部的字符都是能夠的。uuid.uuid4()
這個函數返回的字符串的格式。若是項目變得愈來愈大。那麼url會變得愈來愈多。若是都放在主urls.py
文件中,那麼將不太好管理。所以咱們能夠將每一個app本身的urls放到本身的app中進行管理。通常咱們會在app中新建一個urls.py文件用來存儲全部和這個app相關的子url。 須要注意的地方:
include
函數包含子urls.py
,而且這個urls.py
的路徑是相對於項目的路徑。示例代碼以下:
# 父url urlpatterns = [ path('admin/', admin.site.urls), path('book/',include('book.urls')) ] # 子url urlpatterns = [ path('reports/', credit_views.report), path('reports/<int:id>/', credit_views.report), ]
app
的urls.py
中,全部的url匹配也要放在一個叫作urlpatterns
的變量中,不然找不到。url
是會根據主urls.py
和app中的urls.py
進行拼接的,所以注意不要多加斜槓。urls.py
中添加app_name
變量。include
函數的第一個參數既能夠爲一個字符串,也能夠爲一個元組,若是是元組,那麼元組的第一個參數是子urls.py
模塊的字符串,元組的第二個參數是應用命名空間。這樣的話,namespace就可傳可不傳。也就是說,應用命名空間既能夠在子urls.py
中經過app_name
指定,也能夠在include
函數中指定。pattern_list
是一個列表。這個列表中裝的是path
或者re_path
函數。也就是說,能夠把子url中的path放在這個列表中,實例代碼以下:
path('movie/',include([ path('',views.movie), path('list/',views.movie_list), ]))
由於url是常常變化的。若是在代碼中寫死可能會常常改代碼。給url取個名字,之後使用url的時候就使用他的名字進行反轉就能夠了,就不須要寫死url了。
在path
函數中,傳遞一個name
參數就能夠指定。示例代碼以下:
urlpatterns = [ path('',views.index,name='index'), path('login/',views.login,name='login') ]
在多個app之間,有可能產生同名的url。這時候爲了不反轉url的時候產生混淆,能夠使用應用命名空間,來作區分。定義應用命名空間很是簡單,只要在app
的urls.py
中定義一個叫作app_name
的變量,來指定這個應用的命名空間便可。示例代碼以下:
# 應用命名空間 app_name = 'front' urlpatterns = [ path('',views.index,name='index'), path('login/',views.login,name='login') ]
之後在作反轉的時候就能夠使用應用命名空間:url名稱
的方式進行反轉。示例代碼以下:
login_url = reverse('front:login')
一個app,能夠建立多個實例。能夠使用多個url映射同一個app。因此這就會產生一個問題。之後在作反轉的時候,若是使用應用命名空間,那麼就會發生混淆。爲了不這個問題。咱們能夠使用實例命名空間。實例命名空間也是很是簡單,只要在include
函數中傳遞一個namespace
變量便可。示例代碼以下:
urlpatterns = [ path('',include('front.urls')), # 同一個app下有兩個實例 path('cms1/',include('cms.urls',namespace='cms1')), path('cms2/',include('cms.urls',namespace='cms2')), ]
之後在作反轉的時候,就能夠根據實例命名空間來指定具體的url。示例代碼以下:
def index(request): username = request.GET.get("username") if username: return HttpResponse('CMS首頁') else: # 獲取當前的命名空間 current_namespace = request.resolver_match.namespace return redirect(reverse("%s:login"%current_namespace))
re_path和path的做用都是同樣的。只不過re_path
是在寫url的時候能夠用正則表達式,功能更增強大。
寫正則表達式都推薦使用原生字符串。也就是以r
開頭的字符串。
在正則表達式中定義變量,須要使用圓括號括起來。這個參數是有名字的,那麼須要使用?P<參數的名字>
。而後在後面添加正則表達式的規則。示例代碼以下:
from django.urls import re_path from . import views urlpatterns = [ # r"":表明的是原生字符串(raw) re_path(r'^$',views.article), # /article/list/<year>/ re_path(r"^list/(?P<year>\d{4})/$",views.article_list), re_path(r"^list/(?P<month>\d{2})/$",views.article_list_month) ]
若是不是特別要求。直接使用path
就夠了,省的把代碼搞的很麻煩(由於正則表達式實際上是很是晦澀的,特別是一些比較複雜的正則表達式,今天寫的明天可能就不記得了)。除非是url中確實是須要使用正則表達式來解決才使用re_path
。
kwargs
參數到revers
函數中。示例代碼以下:
detail_url = reverse('detail',kwargs={"article_id":1,'page':2})
login_url = reverse('login') + "?next=/"
在「文章分類」參數傳到視圖函數以前要把這些分類分開來存儲到列表中。 好比參數是python+django
,那麼傳到視圖函數的時候就要變成['python','django']
。
之後在使用reverse反轉的時候,限制傳遞「文章分類」的參數應該是一個列表,而且要將這個列表變成python+django
的形式。
第一種辦法:
以前已經學到過一些django內置的url轉換器,包括有int、uuid等。有時候這些內置的url轉換器並不能知足咱們的需求,所以django給咱們提供了一個接口可讓咱們本身定義本身的url轉換器。
自定義url轉換器按照如下五個步驟來走就能夠了:
django.urls.converters.register_converter
方法註冊到django中。__init__.py
中引入一下這個包from . import converters
from django.urls import converters
中已經定義好的去寫。示例代碼以下:
from django.urls import register_converter class CategoryConverter(object): regex = r'\w+|(\w+\+\w+)+' def to_python(self,value): # python+django+flask # ['python','django','flask'] result = value.split("+") return result def to_url(self,value): # value:['python','django','flask'] # python+django+flask if isinstance(value,list): result = "+".join(value) return result else: raise RuntimeError("轉換url的時候,分類參數必須爲列表!") register_converter(CategoryConverter,'cate')
第二種辦法:
寫一個類,幷包含下面的成員和屬性:
例如,新建一個converters.py文件,與urlconf同目錄,寫個下面的類:
class FourDigitYearConverter:
regex = '[0-9]{4}' def to_python(self, value): return int(value) def to_url(self, value): return '%04d' % value
寫完類後,在URLconf 中註冊,並使用它,以下所示,註冊了一個xxxx:
from django.urls import register_converter, path from . import converters, views register_converter(converters.FourDigitYearConverter, 'xxxx') # 註冊 urlpatterns = [ path('articles/2003/', views.special_case_2003), path('articles/<xxxx:year>/', views.year_archive), ... ]
使用path或者是re_path的後,在route中均可以包含參數,而有時候想指定默認的參數,這時候能夠經過如下方式來完成。示例代碼以下:
from django.urls import path from . import views urlpatterns = [ path('blog/', views.page), path('blog/page<int:num>/', views.page), ] # View (in blog/views.py) def page(request, num=1): # Output the appropriate page of blog entries, according to num. ...
當在訪問blog/的時候,由於沒有傳遞num參數,因此會匹配到第一個url,這時候就執行view.page這個視圖函數,而在page函數中,又有num=1這個默認參數。所以這時候就能夠不用傳遞參數。而若是訪問blog/1的時候,由於在傳遞參數的時候傳遞了num,所以會匹配到第二個url,這時候也會執行views.page,而後把傳遞進來的參數傳給page函數中的num。
Django2.0的url雖然改‘配置’了,但它依然向老版本兼容。而這個兼容的辦法,就是用re_path()
方法代替path()
方法。re_path()
方法在骨子裏,根本就是之前的url()
方法,只不過導入的位置變了。下面是一個例子,對比一下Django1.11時代的語法,有什麼太大的差異?
from django.urls import path, re_path from . import views urlpatterns = [ path('articles/2003/', views.special_case_2003), re_path(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive), re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive), re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<slug>[\w-]+)/$', views.article_detail), ]
與path()
方法不一樣的在於兩點:
path()
方法中能夠指定轉換成某種類型。在視圖中接收參數時必定要當心。當Django找不到與請求匹配的URL時,或者當拋出一個異常時,將調用一個錯誤處理視圖。Django默認的自帶的錯誤視圖包括400、40三、404和500,分別表示請求錯誤、拒絕服務、頁面不存在和服務器錯誤。它們分別位於:
這些值能夠在根URLconf中設置。在其它app中的二級URLconf中設置這些變量無效。
Django有內置的HTML模版,用於返回錯誤頁面給用戶,可是這些403,404頁面實在醜陋,一般咱們都自定義錯誤頁面。
首先,在根URLconf中額外增長下面的條目,並導入views模塊:
from django.contrib import admin from django.urls import path from app import views urlpatterns = [ path('admin/', admin.site.urls), ] # 增長的條目 handler400 = views.bad_request handler403 = views.permission_denied handler404 = views.page_not_found handler500 = views.error
而後在,app/views.py文件中增長四個處理視圖:
def bad_request(request): return render(request, '400.html') def permission_denied(request): return render(request, '403.html') def page_not_found(request): return render(request, '404.html') def error(request): return render(request, '500.html')
再根據本身的需求,建立對應的400、40三、40四、500.html四個頁面文件,就能夠了(要注意好模板文件的引用方式,視圖的放置位置等等)。
在以前的章節中,視圖函數只是直接返回文本,而在實際生產環境中其實不多這樣用,由於實際的頁面大可能是帶有樣式的HTML代碼,這可讓瀏覽器渲染出很是漂亮的頁面。目前市面上有很是多的模板系統,其中最知名最好用的就是DTL和Jinja2。DTL是Django Template Language三個單詞的縮寫,也就是Django自帶的模板語言。固然也能夠配置Django支持Jinja2等其餘模板引擎,可是做爲Django內置的模板語言,和Django能夠達到無縫銜接而不會產生一些不兼容的狀況。所以建議你們學習好DTL。
DTL與普通的HTML文件的區別
DTL模板是一種帶有特殊語法的HTML文件,這個HTML文件能夠被Django編譯,能夠傳遞參數進去,實現數據動態化。在編譯完成後,生成一個普通的HTML文件,而後發送給客戶端。
渲染模板有多種方式。這裏講下兩種經常使用的方式。
render_to_string
:找到模板,而後將模板編譯後渲染成Python的字符串格式。最後再經過HttpResponse類包裝成一個HttpResponse對象返回回去。示例代碼以下:from django.template.loader import render_to_string from django.http import HttpResponse def book_detail(request,book_id): html = render_to_string("detail.html") return HttpResponse(html)
from django.shortcuts import render def book_list(request): return render(request,'list.html')
在項目的settings.py文件中。有一個TEMPLATES配置,這個配置包含了模板引擎的配置,模板查找路徑的配置,模板上下文的配置等。模板路徑能夠在兩個地方配置。
DIRS
:這是一個列表,在這個列表中能夠存放全部的模板路徑,之後在視圖中使用render或者render_to_string渲染模板的時候,會在這個列表的路徑中查找模板。APP_DIRS
:默認爲True,這個設置爲True後,會在INSTALLED_APPS的安裝了的APP下的templates文件加中查找模板。{{ 變量 }}
中。對象.屬性名
來進行訪問。
class Person(object): def __init__(self,username): self.username = username context = { 'person': p }
person
的username
,那麼就是經過person.username
來訪問。字典.key
的方式進行訪問,不能經過中括號[]
的形式進行訪問。
context = { 'person': { 'username':'zhiliao' } }
username
。就是如下代碼person.username
key
時候也是使用點.
來訪問,所以不能在字典中定義字典自己就有的屬性名看成key
,不然字典的那個屬性將編程字典中的key了。
context = { 'person': { 'username':'zhiliao', 'keys':'abc' } }
keys
做爲person
這個字典的key
了。所以之後在模版中訪問person.keys
的時候,返回的不是這個字典的全部key,而是對應的值。點.
的方式進行訪問,不能經過中括號[]
的形式進行訪問。這一點和python中是不同的。示例代碼以下:
{{ persons.1 }}
{%%}
之間。{% endif %}
。==、!=、<、<=、>、>=、in、not in、is、is not
這些均可以使用。elif
以及else
等標籤。for...in…
標籤
for...in...
相似於Python
中的for...in...
。能夠遍歷列表、元組、字符串、字典等一切能夠遍歷的對象。示例代碼以下:
{% for person in persons %} <p>{{ person.name }}</p> {% endfor %}
若是想要反向遍歷,那麼在遍歷的時候就加上一個reversed
。示例代碼以下:
{% for person in persons reversed %} <p>{{ person.name }}</p> {% endfor %}
遍歷字典的時候,須要使用items
、keys
和values
等方法。在DTL
中,執行一個方法不能使用圓括號的形式。遍歷字典示例代碼以下:
{% for key,value in person.items %} <p>key:{{ key }}</p> <p>value:{{ value }}</p> {% endfor %}
在for
循環中,DTL
提供了一些變量可供使用。這些變量以下:
forloop.counter
:當前循環的下標。以1做爲起始值。forloop.counter0
:當前循環的下標。以0做爲起始值。forloop.revcounter
:當前循環的反向下標值。好比列表有5個元素,那麼第一次遍歷這個屬性是等於5,第二次是4,以此類推。而且是以1做爲最後一個元素的下標。forloop.revcounter0
:相似於forloop.revcounter。不一樣的是最後一個元素的下標是從0開始。forloop.first
:是不是第一次遍歷。forloop.last
:是不是最後一次遍歷。forloop.parentloop
:若是有多個循環嵌套,那麼這個屬性表明的是上一級的for循環。** 模板中的for...in...沒有continue和break語句,這一點和Python中有很大的不一樣,必定要記清楚! **
for...in...empty`標籤
這個標籤使用跟for...in...
是同樣的,只不過是在遍歷的對象若是沒有元素的狀況下,會執行empty
中的內容。示例代碼以下:
{% for person in persons %} <li>{{ person }}</li> {% empty %} 暫時尚未任何人 {% endfor %}
with
語句來實現。with
語句有兩種使用方式,第一種是with xx=xxx
的形式,第二種是with xxx as xxx
的形式。 {% with zs=persons.0%} <p>{{ zs }}</p> <p>{{ zs }}</p> {% endwith %} 下面這個由於超過了with語句塊,所以不能使用 <p>{{ zs }}</p> {% with persons.0 as zs %} <p>{{ zs }}</p> {% endwith %}
url
標籤:在模版中,咱們常常要寫一些url
,好比某個a
標籤中須要定義href
屬性。固然若是經過硬編碼的方式直接將這個url
寫死在裏面也是能夠的。可是這樣對於之後項目維護可能不是一件好事。所以建議使用這種反轉的方式來實現,相似於django
中的reverse
同樣。示例代碼以下:
<a href="{% url 'book:list' %}">圖書列表頁面</a>
若是url
反轉的時候須要傳遞參數,那麼能夠在後面傳遞。可是參數分位置參數和關鍵字參數。位置參數和關鍵字參數不能同時使用。示例代碼以下:
# path部分 path('detail/<book_id>/',views.book_detail,name='detail') # url反轉,使用位置參數 <a href="{% url 'book:detail' 1 %}">圖書詳情頁面</a> # url反轉,使用關鍵字參數 <a href="{% url 'book:detail' book_id=1 %}">圖書詳情頁面</a>
若是想要在使用url
標籤反轉的時候要傳遞查詢字符串的參數,那麼必需要手動在在後面添加。示例代碼以下:
<a href="{% url 'book:detail' book_id=1 %}?page=1">圖書詳情頁面</a>
若是須要傳遞多個參數,那麼經過空格的方式進行分隔。示例代碼以下:
<a href="{% url 'book:detail' book_id=1 page=2 %}">圖書詳情頁面</a>
<
轉義成<
等。autoescape
標籤來關掉自動轉義。示例代碼以下:
{% autoescape off %} {{ info }} {% endautoescape %}
verbatim
標籤:默認在DTL
模板中是會去解析那些特殊字符的。和其餘模板相沖突的時候,須要關閉解析的時候,好比{%
和%}
以及{{
等。若是你在某個代碼片斷中不想使用DTL
的解析引擎。那麼你能夠把這個代碼片斷放在verbatim
標籤中。示例代碼下:
{% verbatim %} {{if dying}}Still alive.{{/if}} // 這樣就是原始字符不會被當成變量解析 {% endverbatim %}
爲何須要過濾器?
由於在DTL中,不支持函數的調用形式()
,所以不能給函數傳遞參數,這將有很大的侷限性。而過濾器其實就是一個函數,能夠對須要處理的參數進行處理,而且還能夠額外接收一個參數(也就是說,最多隻能有2個參數)。
add過濾器:
將傳進來的參數添加到原來的值上面。這個過濾器會嘗試將值
和參數
轉換成整形而後進行相加。若是轉換成整形過程當中失敗了,那麼會將值
和參數
進行拼接。若是是字符串,那麼會拼接成字符串,若是是列表,那麼會拼接成一個列表。示例代碼以下:
{{ value|add:"2" }}
若是value
是等於4,那麼結果將是6。若是value
是等於一個普通的字符串,好比abc
,那麼結果將是abc2
。add
過濾器的源代碼以下:
def add(value, arg): """Add the arg to the value.""" try: return int(value) + int(arg) except (ValueError, TypeError): try: return value + arg except Exception: return ''
cut過濾器
移除值中全部指定的字符串。相似於python
中的replace(args,"")
。示例代碼以下:
{{ value|cut:" " }}
以上示例將會移除value
中全部的空格字符。cut
過濾器的源代碼以下:
def cut(value, arg): """Remove all values of arg from the given string.""" safe = isinstance(value, SafeData) value = value.replace(arg, '') if safe and arg != ';': return mark_safe(value) return value
date
過濾器
將一個日期按照指定的格式,格式化成字符串。示例代碼以下:
# 數據 context = { "birthday": datetime.now() } # 模版 {{ birthday|date:"Y/m/d" }}
那麼將會輸出2018/02/01
。其中Y
表明的是四位數字的年份,m
表明的是兩位數字的月份,d
表明的是兩位數字的日。
還有更多時間格式化的方式。見下表。
格式字符 | 描述 | 示例 |
---|---|---|
Y | 四位數字的年份 | 2018 |
m | 兩位數字的月份 | 01-12 |
n | 月份,1-9前面沒有0前綴 | 1-12 |
d | 兩位數字的天 | 01-31 |
j | 天,可是1-9前面沒有0前綴 | 1-31 |
g | 小時,12小時格式的,1-9前面沒有0前綴 | 1-12 |
h | 小時,12小時格式的,1-9前面有0前綴 | 01-12 |
G | 小時,24小時格式的,1-9前面沒有0前綴 | 1-23 |
H | 小時,24小時格式的,1-9前面有0前綴 | 01-23 |
i | 分鐘,1-9前面有0前綴 | 00-59 |
s | 秒,1-9前面有0前綴 | 00-59 |
default
若是值被評估爲False
。好比[]
,""
,None
,{}
等這些在if
判斷中爲False
的值,都會使用default
過濾器提供的默認值。爲True 時,則會使用value的值,示例代碼以下:
{{ value|default:"nothing" }}
若是value
是等於一個空的字符串。好比""
,那麼以上代碼將會輸出nothing
。
default_if_none
若是值是None
,那麼將會使用default_if_none
提供的默認值。這個和default
有區別,default
是全部被評估爲False
的都會使用默認值。而default_if_none
則只有這個值是等於None
的時候纔會使用默認值。示例代碼以下:
{{ value|default_if_none:"nothing" }}
若是value
是等於""
也即空字符串,那麼以上會輸出空字符串。若是value
是一個None
值,以上代碼纔會輸出nothing
。
first
返回列表/元組/字符串中的第一個元素。示例代碼以下:
{{ value|first }}
若是value
是等於['a','b','c']
,那麼輸出將會是a
。
last
返回列表/元組/字符串中的最後一個元素。示例代碼以下:
{{ value|last }}
若是value
是等於['a','b','c']
,那麼輸出將會是c
。
floatformat
使用四捨五入的方式格式化一個浮點類型。若是這個過濾器沒有傳遞任何參數。那麼只會在小數點後保留一個小數,若是小數後面全是0,那麼只會保留整數。固然也能夠傳遞一個參數,標識具體要保留幾個小數。
value | 模版代碼 | 輸出 |
---|---|---|
34.23234 | `{{ value | floatformat }}` |
34.000 | `{{ value | floatformat }}` |
34.260 | `{{ value | floatformat }}` |
value | 模版代碼 | 輸出 |
---|---|---|
34.23234 | `{{value | floatformat:3}}` |
34.0000 | `{{value | floatformat:3}}` |
34.26000 | `{{value | floatformat:3}}` |
join
相似與Python
中的join
,將列表/元組/字符串用指定的字符進行拼接。示例代碼以下:
{{ value|join:"/" }}
若是value
是等於['a','b','c']
,那麼以上代碼將輸出a/b/c
。
length
獲取一個列表/元組/字符串/字典的長度。示例代碼以下:
{{ value|length }}
若是value
是等於['a','b','c']
,那麼以上代碼將輸出3
。若是value
爲None
,那麼以上將返回0
。
lower
將值中全部的字符所有轉換成小寫。示例代碼以下:
{{ value|lower }}
若是value
是等於Hello World
。那麼以上代碼將輸出hello world
。
upper
相似於lower
,只不過是將指定的字符串所有轉換成大寫。
random
在被給的列表/字符串/元組中隨機的選擇一個值。示例代碼以下:
{{ value|random }}
若是value
是等於['a','b','c']
,那麼以上代碼會在列表中隨機選擇一個。
safe
標記一個字符串是安全的。也即會關掉這個字符串的自動轉義。示例代碼以下:
{{value|safe}}
若是value
是一個不包含任何特殊字符的字符串,好比<a>
這種,那麼以上代碼就會把字符串正常的輸入。若是value
是一串html
代碼,那麼以上代碼將會把這個html
代碼渲染到瀏覽器中。
也能夠把返回的字符串經過導入from django.utils.safestring import mark_safe
mark_safe(字符串)
slice
相似於Python
中的切片操做。示例代碼以下:
{{ some_list|slice:"2:" }}
以上代碼將會給some_list
從2
開始作切片操做。
stringtags
刪除字符串中全部的html
標籤。示例代碼以下:
{{ value|striptags }}
若是value
是<strong>hello world</strong>
,那麼以上代碼將會輸出hello world
。
truncatechars
若是給定的字符串長度超過了過濾器指定的長度。那麼就會進行切割,而且會拼接三個點來做爲省略號。示例代碼以下:
{{ value|truncatechars:5 }}
若是value
是等於北京歡迎您~
,那麼輸出的結果是北京...
。可能你會想,爲何不會北京歡迎您...
呢。由於三個點也佔了三個字符,因此北京
+三個點的字符長度就是5。
truncatechars_html
相似於truncatechars
,只不過是不會切割html
標籤。示例代碼以下:
{{ value|truncatechars:5 }}
若是value
是等於<p>北京歡迎您~</p>
,那麼輸出將是<p>北京...</p>
。
查看源碼:from django.template import defaultfilters
templatetags
,注意,這個包的名字必須爲templatetags
,否則就找不到。templatetags
包下面,建立一個python文件用來存儲過濾器。register=django.template.Library() register.filter(過濾器名子,函數名子)
進行註冊。settings.INSTALLED_APS
中,否則Django也找不到這個過濾器。load
標籤加載過濾器所在的python包。django.template.Library.filter
還能夠看成裝飾器來使用。若是filter
函數沒有傳遞任何參數,那麼將會使用這個函數的名字來做爲過濾器的名字。固然若是你不想使用函數的名字來做爲過濾器的名字,也能夠傳遞一個name
參數。示例代碼以下:
@register.filter('my_greet') def greet(value,word): return value + word
@register.filter
def time_since(value): """ time距離如今的時間間隔 1.若是時間間隔小於1分鐘之內,那麼就顯示「剛剛」 2.若是是大於1分鐘小於1小時,那麼就顯示「xx分鐘前」 3.若是是大於1小時小於24小時,那麼就顯示「xx小時前」 4.若是是大於24小時小於30天之內,那麼就顯示「xx天前」 5.不然就是顯示具體的時間 2017/10/20 16:15 """ if not isinstance(value,datetime): return value now = datetime.now() # timedelay.total_seconds timestamp = (now - value).total_seconds() if timestamp < 60: return '剛剛' elif timestamp >= 60 and timestamp < 60*60: minutes = int(timestamp/60) return '%s分鐘前' % minutes elif timestamp >= 60*60 and timestamp < 60*60*24: hours = int(timestamp/60/60) return '%s小時前' % hours elif timestamp >= 60*60*24 and timestamp < 60*60*24*30: days = int(timestamp/60/60/24) return '%s天前' % days else: return value.strftime("%Y/%m/%d %H:%M")
include
進來就能夠了。include
子模版的時候,傳遞一些參數,那麼能夠使用with xxx=xxx
的形式。示例代碼以下:
{% include 'header.html' with username='zhiliao' %}
在前端頁面開發中。有些代碼是須要重複使用的。這種狀況能夠使用include
標籤來實現。也能夠使用另一個比較強大的方式來實現,那就是模版繼承。模版繼承相似於Python
中的類,在父類中能夠先定義好一些變量和方法,而後在子類中實現。模版繼承也能夠在父模版中先定義好一些子模版須要用到的代碼,而後子模版直接繼承就能夠了。而且由於子模版確定有本身的不一樣代碼,所以能夠在父模版中定義一個block接口,而後子模版再去實現。如下是父模版的代碼:
{% load static %}
<!DOCTYPE html> <html lang="en"> <head> <link rel="stylesheet" href="{% static 'style.css' %}" /> <title>{% block title %}個人站點{% endblock %}</title> </head> <body> <div id="sidebar"> {% block sidebar %} <ul> <li><a href="/">首頁</a></li> <li><a href="/blog/">博客</a></li> </ul> {% endblock %} </div> <div id="content"> {% block content %}{% endblock %} </div> </body> </html>
這個模版,咱們取名叫作base.html
,定義好一個簡單的html
骨架,而後定義好兩個block
接口,讓子模版來根據具體需求來實現。子模板而後經過extends
標籤來實現,示例代碼以下:
{% extends "base.html" %}
{% block title %}博客列表{% endblock %} {% block content %} {% for entry in blog_entries %} <h2>{{ entry.title }}</h2> <p>{{ entry.body }}</p> {% endfor %} {% endblock %}
須要注意的是:extends標籤必須放在模版的第開始的位置 子模板中的代碼必須放在block中,不然將不會被渲染。 若是在某個block
中須要使用父模版的內容,那麼能夠使用{{block.super}}
來繼承。好比上例,{%block title%}
,若是想要使用父模版的title
,那麼能夠在子模版的title block
中使用{{ block.super }}
來實現。
在定義block
的時候,除了在block
開始的地方定義這個block
的名字,還能夠在block
結束的時候定義名字。好比{% block title %}{% endblock title %}
。這在大型模版中顯得尤爲有用,能讓你快速的看到block
包含在哪裏。
在一個網頁中,不只僅只有一個html
骨架,還須要css
樣式文件,js
執行文件以及一些圖片等。所以在DTL
中加載靜態文件是一個必需要解決的問題。在DTL
中,使用static
標籤來加載靜態文件。要使用static
標籤,首先須要{% load static %}
。加載靜態文件的步驟以下:
首先確保django.contrib.staticfiles
已經添加到settings.INSTALLED_APPS
中,建立時已經默認添加好了。
確保在settings.py
中設置了STATIC_URL
。STATIC_URL=\static\
在已經安裝了的app
下建立一個文件夾叫作static
,而後再在這個static
文件夾下建立一個當前app
的名字的文件夾,再把靜態文件放到這個文件夾下。例如你的app
叫作book
,有一個靜態文件叫作zhiliao.jpg
,那麼路徑爲book/static/book/zhiliao.jpg
。(爲何在app
下建立一個static
文件夾,還須要在這個static
下建立一個同app
名字的文件夾呢?緣由是若是直接把靜態文件放在static
文件夾下,那麼在模版加載靜態文件的時候就是使用zhiliao.jpg
,若是在多個app
之間有同名的靜態文件,這時候可能就會產生混淆。而在static
文件夾下加了一個同名app
文件夾,在模版中加載的時候就是使用<img src="{% static 'app/zhiliao.jpg' %}">
,這樣就能夠避免產生混淆。)
若是有一些靜態文件是不和任何app
掛鉤的。那麼能夠在settings.py
中添加STATICFILES_DIRS
,之後DTL
就會在這個列表的路徑中查找靜態文件。那麼查找的有順序就變爲,先在本身的app裏找,找不到就會這個目錄下去找。好比能夠設置爲:
STATICFILES_DIRS = [ os.path.join(BASE_DIR,"static") ]
load
標籤加載static
標籤。好比要加載在項目的static
文件夾下的style.css
的文件。那麼示例代碼以下:{% load static %}
<link rel="stylesheet" href="{% static 'style.css' %}">
若是不想每次在模版中加載靜態文件都使用load
加載static
標籤,那麼能夠在settings.py
中的TEMPLATES/OPTIONS
添加'builtins':['django.templatetags.static']
,這樣之後在模版中就能夠直接使用static
標籤,變成一個內置的標籤,而不用手動的load
了。
若是沒有在settings.INSTALLED_APPS
中添加django.contrib.staticfiles
。那麼咱們就須要手動的將請求靜態文件的url
與靜態文件的路徑進行映射了。示例代碼以下:
from django.conf import settings from django.conf.urls.static import static urlpatterns = [ # 其餘的url映射 ] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
安裝 pip install pymysql
在Django的settings.py中設置數據庫的配置
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'book', 'USER': 'root', 'PASSWORD': '123', 'HOST': '192.168.17.88', 'PORT': '3306' } }
app配置單獨的數據庫
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME':'bms', # 要鏈接的數據庫,鏈接前須要建立好 'USER':'root', # 鏈接數據庫的用戶名 'PASSWORD':'', # 鏈接數據庫的密碼 'HOST':'127.0.0.1', # 鏈接主機,默認本級 'PORT':3306 # 端口 默認3306 }, 'app01': { #能夠爲每一個app都配置本身的數據,而且數據庫還能夠指定別的,也就是不必定就是mysql,也能夠指定sqlite等其餘的數據庫 'ENGINE': 'django.db.backends.mysql', 'NAME':'bms', # 要鏈接的數據庫,鏈接前須要建立好 'USER':'root', # 鏈接數據庫的用戶名 'PASSWORD':'', # 鏈接數據庫的密碼 'HOST':'127.0.0.1', # 鏈接主機,默認本級 'PORT':3306 # 端口 默認3306 } }
在主目錄下的__init__.py
中添加代碼以下:
import pymysql pymysql.install_as_MySQLdb()
若是想打印orm轉換過程當中的sql,須要在settings中進行以下配置:
LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'console':{ 'level':'DEBUG', 'class':'logging.StreamHandler', }, }, 'loggers': { 'django.db.backends': { 'handlers': ['console'], 'propagate': True, 'level':'DEBUG', }, } }
使用原生sql語句
從django.db中導入connection包
生成一個遊標對象 cursor=connection.cursor()
進行經常使用的操做:
cursor.execute("原生sql語句")
ret = cursor.fetchall()
拿到查詢後的結果
ret = cursor.fetchone()
拿到查詢後單個的結果
from app01 import models def add_book(request): ''' 添加表記錄 :param request: http請求信息 :return: ''' book_obj = models.Book(title='python',price=123,pub_date='2012-12-12',publish='人民出版社') book_obj.save() from django.db import connection #經過這種方式也能查看執行的sql語句 print(connection.queries) return HttpResponse('ok')
ORM
模型通常都是放在app
的models.py
文件中。每一個app
均可以擁有本身的模型。而且若是這個模型想要映射到數據庫中,那麼這個app
必需要放在settings.py
的INSTALLED_APP
中進行安裝。如下是寫一個簡單的書籍ORM
模型。示例代碼以下:
from django.db import models class Book(models.Model): name = models.CharField(max_length=20,null=False) author = models.CharField(max_length=20,null=False) pub_time = models.DateTimeField(default=datetime.now) price = models.FloatField(default=0)
以上便定義了一個模型。這個模型繼承自django.db.models.Model
,若是這個模型想要映射到數據庫中,就必須繼承自這個類。這個模型之後映射到數據庫中,表名是模型名稱的小寫形式,爲book
。在這個表中,有四個字段,一個爲name
,這個字段是保存的是書的名稱,是varchar
類型,最長不能超過20個字符,而且不能爲空。第二個字段是做者名字類型,一樣也是varchar
類型,長度不能超過20個。第三個是出版時間,數據類型是datetime
類型,默認是保存這本書籍的時間。第五個是這本書的價格,是浮點類型。
還有一個字段咱們沒有寫,就是主鍵id
,在django
中,若是一個模型沒有定義主鍵,那麼將會自動生成一個自動增加的int
類型的主鍵,而且這個主鍵的名字就叫作id
。
若是是自定義的主鍵,好比id
,給他設置了primary_key
,就會也增長自增加的屬性。
將ORM
模型映射到數據庫中,總結起來就是如下幾步:
settings.py
中,配置好DATABASES
,作好數據庫相關的配置。app
中的models.py
中定義好模型,這個模型必須繼承自django.db.models
。app
添加到settings.py
的INSTALLED_APP
中。python manage.py makemigrations
來生成遷移腳本文件。python manage.py migrate
來將遷移腳本文件映射到數據庫中。migrate怎麼判斷哪些遷移腳本須要執行
他會將代碼中的遷移腳本和數據庫中django_migrations
中的遷移腳本進行對比,若是發現數據庫中,沒有這個遷移腳本,那麼就會執行這個遷移腳本。
migrate作了什麼事情:
django_migrations
中。執行migrate命令的時候報錯的解決辦法
緣由:
執行migrate命令會報錯的緣由是。數據庫的django_migrations
表中的遷移版本記錄和代碼中的遷移腳本不一 致致使的。
錯誤信息:django.db.utils.InternalError: (1050, "Table 'django_content_type' already exists")
解決辦法
使用—fake參數
首先對比數據庫中的遷移腳本和代碼中的遷移腳本。而後找到哪一個不一樣,以後再使用--fake
,將代碼中的遷移腳本添加到django_migrations
中,可是並不會執行sql語句。這樣就能夠避免每次執行migrate
的時候,都執行一些重複的遷移腳本。
執行python manage.py migrate --fake
再次執行python manage.py migrate
終極解決方案
若是代碼中的遷移腳本和數據庫中的遷移腳本實在太多,就是搞不清了。那麼這時候就能夠使用如下終極解決方案:
django_migrations
表中將出問題的app相關的遷移記錄都刪掉。makemigrations
,從新將模型生成一個遷移腳本。migrate --fake-initial
參數,將剛剛生成的遷移腳本,標記爲已經完成(由於這些模型相對應的表,其實都已經在數據庫中存在了,不須要重複執行了。)django項目中,執行python manage migrate時 報錯WARNINGS: ?: (mysql.W002) MySQL Strict Mode is not set for data
django項目中,執行python manage migrate
時
解決
在settings中,在DATABASES變量定義處下面添加
DATABASES['OPTIONS']['init_command'] = "SET sql_mode='STRICT_TRANS_TABLES'"
或者在DATABASES變量定義時,添加上面命令中所示的鍵值對。
這種狀況出現的緣由是django版本的差別引發的,使用的是django1.9,而Django1.10後添加了一個新特性,就是在migrate
的時候會檢查歷史一致性,就像github在push前會檢查新舊版本之間是否有衝突
什麼是navie時間?什麼是aware時間?
navie和aware介紹以及在django中的用法
https://docs.djangoproject.com/en/2.0/topics/i18n/timezones/EmailField:
pytz庫
專門用來處理時區的庫。這個庫會常常更新一些時區的數據,不須要咱們擔憂。而且這個庫在安裝Django的時候會默認的安裝。若是沒有安裝,那麼能夠經過pip install pytz
的方式進行安裝。
astimezone方法
將一個時區的時間轉換爲另一個時區的時間。這個方法只能被aware
類型的時間調用。不能被navie
類型的時間調用。 示例代碼以下:
import pytz from datetime import datetime now = datetime.now() # 這是一個navie類型的時間 utc_timezone = pytz.timezone("UTC") # 定義UTC的時區對象 utc_now = now.astimezone(utc_timezone) # 將當前的時間轉換爲UTC時區的時間 >> ValueError: astimezone() cannot be applied to a naive datetime # 在linux上會拋出一個異常,緣由就是由於navie類型的時間不能調用astimezone方法,在windows和mac上不會報錯,會成功轉換。 now = now.replace(tzinfo=pytz.timezone('Asia/Shanghai')) utc_now = now.astimezone(utc_timezone) # 這時候就能夠正確的轉換。
replace方法
能夠將一個時間的某些屬性進行更改。
django.utils.timezone.now方法
會根據settings.py
中是否設置了USE_TZ=True
獲取當前的時間。若是設置了,那麼就獲取一個aware
類型的UTC
時間。若是沒有設置或者設置爲False,那麼就會獲取一個navie
類型的時間。
導包:
from django.utils.timezone import now, localtime
使用:
articlee = Article(title='abc', createe_time=now())
注意這裏的now和datetime.now()
是不同的。
def now(): """ Returns an aware or naive datetime.datetime, depending on settings.USE_TZ. """ if settings.USE_TZ: # timeit shows that datetime.now(tz=utc) is 24% slower若是設置了,就會返回一個清醒的時間aware。 return datetime.utcnow().replace(tzinfo=utc) else: return datetime.now()
django.utils.timezone.localtime方法
會根據setting.py
中的TIME_ZONE
來將一個aware
類型的時間轉換爲TIME_ZONE
指定時區的時間。好比Asia/Shanghai
在模板中使用時,先{% load tz %}
, 而後再使用過濾器的方式使用{{ create_time | localtime }}
也能夠不使用過濾器,django
也會自動的把utc
時間轉換成一個本地的時間。
字段是模型中最重要的內容之一,也是惟一必須的部分。字段在Python中表現爲一個類屬性,體現了數據表中的一個列。請不要使用clean
、save
、delete
等Django內置的模型API名字,防止命名衝突。
字段命名約束:
Django不容許下面兩種字段名:
class Example(models.Model): pass = models.IntegerField() # 'pass'是Python保留字!
class Example(models.Model): foo__bar = models.IntegerField() # 'foo__bar' 有兩個下劃線在一塊兒!
因爲你能夠自定義表名、列名,上面的規則可能被繞開,可是請養成良好的習慣,必定不要那麼起名。
SQL語言的join、where和select等保留字能夠做爲字段名,由於Django對它們都進行了轉義。
字段類型的做用:
<input type=「text」 />
Django內置了許多字段類型,它們都位於django.db.models
中,例如models.CharField
。這些類型基本知足需求,若是還不夠,你也能夠自定義字段。
下表列出了全部Django內置的字段類型,但不包括關係字段類型(字段名採用駝峯命名法,必定要注意,加粗的是經常使用的):
類型 | 說明 |
---|---|
AutoField | 一個自動增長的整數類型字段。一般你不須要本身編寫它,Django會自動幫你添加字段:id = models.AutoField(primary_key=True) ,這是一個自增字段,從1開始計數。若是你非要本身設置主鍵,那麼請務必將字段設置爲primary_key=True 。Django在一個模型中只容許有一個自增字段,而且該字段必須爲主鍵! |
BigAutoField | (1.10新增)64位整數類型自增字段,數字範圍更大,從1到9223372036854775807 |
BigIntegerField | 64位整數字段(看清楚,非自增),相似IntegerField ,-9223372036854775808 到9223372036854775807。在Django的模板表單裏體現爲一個textinput標籤。 |
BinaryField | 二進制數據類型。使用受限,少用。 |
BooleanField | 布爾值類型。默認值是None。在HTML表單中體現爲CheckboxInput標籤。若是要接收null值,請使用NullBooleanField。 |
CharField | 字符串類型。必須接收一個max_length參數,表示字符串長度不能超過該值。默認的表單標籤是input text。最經常使用的filed,沒有之一! |
CommaSeparatedIntegerField | 逗號分隔的整數類型。必須接收一個max_length參數。經常使用於表示較大的金額數目,例如1,000,000元。 |
DateField | class DateField(auto_now=False, auto_now_add=False, **options) 日期類型。一個Python中的datetime.date的實例。在HTML中表現爲TextInput標籤。在admin後臺中,Django會幫你自動添加一個JS的日曆表和一個「Today」快捷方式,以及附加的日期合法性驗證。兩個重要參數:(參數互斥,不能共存) auto_now :每當對象被保存時將字段設爲當前日期,經常使用於保存最後修改時間。auto_now_add :每當對象被建立時,設爲當前日期,經常使用於保存建立日期(注意,它是不可修改的)。設置上面兩個參數就至關於給field添加了editable=False 和blank=True 屬性。若是想具備修改屬性,請用default參數。例子:pub_time = models.DateField(auto_now_add=True) ,自動添加發布時間。 |
DateTimeField | 日期時間類型。Python的datetime.datetime的實例。與DateField相比就是多了小時、分和秒的顯示,其它功能、參數、用法、默認值等等都同樣。 |
DecimalField | 固定精度的十進制小數。至關於Python的Decimal實例,必須提供兩個指定的參數!參數max_digits :最大的位數,必須大於或等於小數點位數 。decimal_places :小數點位數,精度。 當localize=False 時,它在HTML表現爲NumberInput標籤,不然是text類型。例子:儲存最大不超過999,帶有2位小數位精度的數,定義以下:models.DecimalField(..., max_digits=5, decimal_places=2) 。 |
DurationField | 持續時間類型。存儲必定期間的時間長度。相似Python中的timedelta。在不一樣的數據庫實現中有不一樣的表示方法。經常使用於進行時間之間的加減運算。可是當心了,這裏有坑,PostgreSQL等數據庫之間有兼容性問題! |
EmailField | 郵箱類型,默認max_length最大長度254位。使用這個字段的好處是,能夠使用DJango內置的EmailValidator進行郵箱地址合法性驗證。 |
FileField | class FileField(upload_to=None, max_length=100, **options) 上傳文件類型,後面單獨介紹。 |
FilePathField | 文件路徑類型,後面單獨介紹 |
FloatField | 浮點數類型,參考整數類型 |
ImageField | 圖像類型,後面單獨介紹。 |
IntegerField | 整數類型,最經常使用的字段之一。取值範圍-2147483648到2147483647。在HTML中表現爲NumberInput標籤。 |
GenericIPAddressField | class GenericIPAddressField(protocol='both', unpack_ipv4=False, **options)[source] ,IPV4或者IPV6地址,字符串形式,例如192.0.2.30 或者2a02:42fe::4 在HTML中表現爲TextInput標籤。參數protocol 默認值爲‘both’,可選‘IPv4’或者‘IPv6’,表示你的IP地址類型。 |
NullBooleanField | 相似布爾字段,只不過額外容許NULL 做爲選項之一。 |
PositiveIntegerField | 正整數字段,包含0,最大2147483647。 |
PositiveSmallIntegerField | 較小的正整數字段,從0到32767。 |
SlugField | slug是一個新聞行業的術語。一個slug就是一個某種東西的簡短標籤,包含字母、數字、下劃線或者鏈接線,一般用於URLs中。能夠設置max_length參數,默認爲50。 |
SmallIntegerField | 小整數,包含-32768到32767。 |
TextField | 大量文本內容,在HTML中表現爲Textarea標籤,最經常使用的字段類型之一!若是你爲它設置一個max_length參數,那麼在前端頁面中會受到輸入字符數量限制,然而在模型和數據庫層面卻不受影響。只有CharField才能同時做用於二者。 |
TimeField | 時間字段,Python中datetime.time的實例。接收同DateField同樣的參數,只做用於小時、分和秒。 |
URLField | 一個用於保存URL地址的字符串類型,默認最大長度200。 |
UUIDField | 用於保存通用惟一識別碼(Universally Unique Identifier)的字段。使用Python的UUID類。在PostgreSQL數據庫中保存爲uuid類型,其它數據庫中爲char(32)。這個字段是自增主鍵的最佳替代品,後面有例子展現。 |
def __init__(self, verbose_name=None, name=None, upload_to='', storage=None, **kwargs): self._primary_key_set_explicitly = 'primary_key' in kwargs self.storage = storage or default_storage self.upload_to = upload_to kwargs['max_length'] = kwargs.get('max_length', 100) super(FileField, self).__init__(verbose_name, name, **kwargs) ..........
上傳文件字段(不能設置爲主鍵)。默認狀況下,該字段在HTML中表現爲一個ClearableFileInput標籤。在數據庫內,咱們實際保存的是一個字符串類型,默認最大長度100,能夠經過max_length參數自定義。真實的文件是保存在服務器的文件系統內的。
重要參數upload_to
用於設置上傳地址的目錄和文件名。以下例所示:
class MyModel(models.Model): # 文件被傳至`MEDIA_ROOT/uploads`目錄,MEDIA_ROOT由你在settings文件中設置 upload = models.FileField(upload_to='uploads/') # 或者 # 被傳到`MEDIA_ROOT/uploads/2015/01/30`目錄,增長了一個時間劃分 upload = models.FileField(upload_to='uploads/%Y/%m/%d/')
Django很人性化地幫咱們實現了根據日期生成目錄的方式!
upload_to參數也能夠接收一個回調函數,該函數返回具體的路徑字符串,以下例:
def user_directory_path(instance, filename): #文件上傳到MEDIA_ROOT/user_<id>/<filename>目錄中 return 'user_{0}/{1}'.format(instance.user.id, filename) class MyModel(models.Model): upload = models.FileField(upload_to=user_directory_path)
例子中,user_directory_path
這種回調函數,必須接收兩個參數,而後返回一個Unix風格的路徑字符串。參數instace
表明一個定義了FileField
的模型的實例,說白了就是當前數據記錄。filename
是本來的文件名。
def __init__(self, verbose_name=None, name=None, width_field=None, height_field=None, **kwargs): self.width_field, self.height_field = width_field, height_field super(ImageField, self).__init__(verbose_name, name, **kwargs)
用於保存圖像文件的字段。其基本用法和特性與FileField同樣,只不過多了兩個屬性height和width。默認狀況下,該字段在HTML中表現爲一個ClearableFileInput標籤。在數據庫內,咱們實際保存的是一個字符串類型,默認最大長度100,能夠經過max_length參數自定義。真實的圖片是保存在服務器的文件系統內的。
height_field
參數:保存有圖片高度信息的模型字段名。 width_field
參數:保存有圖片寬度信息的模型字段名。
使用Django的ImageField須要提早安裝pillow模塊,pip install pillow便可。
使用FileField或者ImageField字段的步驟:
MEDIA_ROOT
,做爲你上傳文件在服務器中的基本路徑(爲了性能考慮,這些文件不會被儲存在數據庫中)。再配置個MEDIA_URL
,做爲公用URL,指向上傳文件的基本路徑。請確保Web服務器的用戶帳號對該目錄具備寫的權限。upload_to
參數,文件最終會放在MEDIA_ROOT
目錄的「upload_to」子目錄中。mug_shot
,那麼在Django模板的HTML文件中,能夠使用{{ object.mug_shot.url }}
來獲取該文件。其中的object用你具體的對象名稱代替。name
和size
屬性,獲取文件的名稱和大小信息。安全建議
不管你如何保存上傳的文件,必定要注意他們的內容和格式,避免安全漏洞!務必對全部的上傳文件進行安全檢查,確保它們不出問題!若是你不加任何檢查就盲目的讓任何人上傳文件到你的服務器文檔根目錄內,好比上傳了一個CGI或者PHP腳本,極可能就會被訪問的用戶執行,這具備致命的危害。
def __init__(self, verbose_name=None, name=None, path='', match=None, recursive=False, allow_files=True, allow_folders=False, **kwargs): self.path, self.match, self.recursive = path, match, recursive self.allow_files, self.allow_folders = allow_files, allow_folders kwargs['max_length'] = kwargs.get('max_length', 100) super(FilePathField, self).__init__(verbose_name, name, **kwargs)
一種用來保存文件路徑信息的字段。在數據表內以字符串的形式存在,默認最大長度100,能夠經過max_length參數設置。
它包含有下面的一些參數:
path
:必須指定的參數。表示一個系統絕對路徑。
match
:可選參數,一個正則表達式,用於過濾文件名。只匹配基本文件名,不匹配路徑。例如foo.*\.txt$
,只匹配文件名foo23.txt
,不匹配bar.txt
與foo23.png
。
recursive
:可選參數,只能是True或者False。默認爲False。決定是否包含子目錄,也就是是否遞歸的意思。
allow_files
:可選參數,只能是True或者False。默認爲True。決定是否應該將文件名包括在內。它和allow_folders
其中,必須有一個爲True。
allow_folders
: 可選參數,只能是True或者False。默認爲False。決定是否應該將目錄名包括在內。
好比:
FilePathField(path="/home/images", match="foo.*", recursive=True)
它只匹配/home/images/foo.png
,但不匹配/home/images/foo/bar.png
,由於默認狀況,只匹配文件名,而無論路徑是怎麼樣的。
數據庫沒法本身生成uuid,所以須要以下使用default參數:
import uuid # Python的內置模塊
from django.db import models class MyUUIDModel(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) # 其它字段
class UnsignedIntegerField(models.IntegerField): def db_type(self, connection): return 'integer UNSIGNED'
自定義char類型字段:
class FixedCharField(models.Field): """ 自定義的char類型的字段類 """ def __init__(self, max_length, *args, **kwargs): super().__init__(max_length=max_length, *args, **kwargs) self.length = max_length def db_type(self, connection): """ 限定生成數據庫表的字段類型爲char,長度爲length指定的值 """ return 'char(%s)' % self.length class Class(models.Model): id = models.AutoField(primary_key=True) title = models.CharField(max_length=25) # 使用上面自定義的char類型的字段 cname = FixedCharField(max_length=25)
多對一的關係,一般被稱爲外鍵。外鍵字段類的定義以下:
def __init__(self, to, on_delete=None, related_name=None, related_query_name=None, limit_choices_to=None, parent_link=False, to_field=None, db_constraint=True, **kwargs): .........
外鍵須要兩個位置參數,一個是關聯的模型,另外一個是on_delete
選項。實際上,在目前版本中,on_delete
選項也能夠不設置,但Django極力反對如此,所以在Django2.0版本後,該選項會設置爲必填。
外鍵要定義在‘多’的一方!
from django.db import models class Book(models.Model): publisher = models.ForeignKey( 'Publisher', on_delete=models.CASCADE, ) # ... class Publisher(models.Model): # ... pass
上面的例子中,每本書都有一個出版社,出版社能夠出版N本書,因而用一個外鍵字段Publisher表示,並放在Book模型中。注意,此Publisher非彼Publisher模型類,它是一個字段的名稱。在Django的模型定義中,常常出現相似的英文單詞大小寫不一樣,必定要注意區分!
若是要關聯的對象在另一個app中,能夠顯式的指出。下例假設Publisher模型存在於production這個app中,則Book模型的定義以下:
class Book(models.Model): publisher = models.ForeignKey( 'production.Publisher', # 關鍵在這裏!! on_delete=models.CASCADE, )
若是要建立一個遞歸的外鍵,也就是本身關聯本身的的外鍵,使用下面的方法:
models.ForeignKey('self', on_delete=models.CASCADE)
核心在於‘self’這個引用。何時須要本身引用本身的外鍵呢?典型的例子就是評論系統!一條評論能夠被不少人繼續評論,以下所示:
class Comment(models.Model): title = models.CharField(max_length=128) text = models.TextField() parent_comment = models.ForeignKey('self', on_delete=models.CASCADE) # .....
注意上面的外鍵字段定義的是父評論,而不是子評論。爲何呢?由於外鍵要放在‘多’的一方!
在實際的數據庫後臺,Django會爲每個外鍵添加_id
後綴,並以此建立數據表裏的一列。在上面的出版社與書的例子中,Book模型對應的數據表中,會有一列叫作publisher_id
。但實際上,在Django
代碼中你不須要使用這個列名,除非你書寫原生的SQL語句,通常咱們都直接使用字段名publisher
。
關係字段的定義還有個小坑。在後面咱們會講到的verbose_name
參數用於設置字段的別名。不少狀況下,爲了方便,咱們都會設置這麼個值,而且做爲字段的第一位置參數。可是對於關係字段,其第一位置參數永遠是關係對象,不能是verbose_name
,必定要注意!
參數說明:
外鍵還有一些重要的參數,說明以下:
on_delete
注意:這個參數在Django2.0以後,不能夠省略了,須要顯式的指定!這也是除了路由編寫方式外,Django2和Django1.x最大的不一樣點之一!
當一個被外鍵關聯的對象被刪除時,Django將模仿on_delete
參數定義的SQL約束執行相應操做。好比,你有一個可爲空的外鍵,而且你想讓它在關聯的對象被刪除時,自動設爲null,能夠以下定義:
user = models.ForeignKey( User, models.SET_NULL, blank=True, null=True, )
該參數可選的值都內置在django.db.models
中,包括:
CASCADE
:級聯操做。若是外鍵對應的那條數據被刪除了,那麼這條數據也會被刪除。
PROTECT
:受保護。即只要這條數據引用了外鍵的那條數據,那麼就不能刪除外鍵的那條數據。
SET_NULL
:設置爲空。若是外鍵的那條數據被刪除了,那麼在本條數據上就將這個字段設置爲空null=True
。若是設置這個選項,前提是要指定這個字段能夠爲空。
SET_DEFAULT
:設置默認值。若是外鍵的那條數據被刪除了,那麼本條數據上就將這個字段設置爲默認值。若是設置這個選項,前提是要指定這個字段一個默認值。category = models.ForeignKey("Category",on_delete=models.SET_DEFAULT,default=Category.obgects.get(pk=1))
SET()
:若是外鍵的那條數據被刪除了。那麼將會獲取SET
函數中的值來做爲這個外鍵的值。SET
函數能夠接收一個能夠調用的對象(好比函數或者方法),若是是能夠調用的對象,那麼會將這個對象調用後的結果做爲值返回回去。category = models.ForeignKey("Category",on_delete=models.SET(Category.obgects.get(pk=1)))
SET(函數)
def defaule_category(): return Category.obgects.get(pk=1) .... category = models.ForeignKey("Category",on_delete=models.SET(defaule_category)
DO_NOTHING
:不採起任何行爲。一切全看數據庫級別的約束。
以上這些選項只是Django級別的,數據級別依舊是RESTRICT!
limit_choices_to
該參數用於限制外鍵所能關聯的對象,只能用於Django
的ModelForm
(Django的表單模塊)和admin後臺,對其它場合無限制功能。其值能夠是一個字典、Q對象或者一個返回字典或Q對象的函數調用,以下例所示:
staff_member = models.ForeignKey( User, on_delete=models.CASCADE, limit_choices_to={'is_staff': True}, )
能夠參考下面的方式,使用函數調用:
def limit_pub_date_choices(): return {'pub_date__lte': datetime.date.utcnow()} # ... limit_choices_to = limit_pub_date_choices # ...
又好比有這麼一個例子:
在使用Django Admin後臺時,有時候想自定義某一字段的Choice_field,例如屏蔽某些選項,只顯示某些指定的選項。
想象這樣的應用場景,我有一個網站,導航欄是這樣的:
點開「技術雜談」後,顯示成這樣:
在這裏,我在後臺設計model時,將「技術雜談」這種顯示在導航欄的分類定義成一級分類,將「C/C++」、「Python」這種隱藏在摺疊欄中的分類定義成二級分類,二級分類有一個字段存儲有連接到對應的父分類的id號。
但這裏出現了一個問題,我在後臺建立文章時分類選項卡中顯示成了這樣:
一級分類和二級分類混雜到了一塊兒,而我實際上只但願它顯示二級分類。由於當選擇「C/C++」、「python」這些二級分類時,會自動歸類到對應的一級分類之中,因此不必在選項卡里顯示一級分類。
……
那麼問題來了,如何在文章的分類選項卡中屏蔽掉一級分類呢?
咱們須要修改文章的Model類,使用 ForeignKey.limit_choices_to 限制分類顯示的內容,只顯示符合條件的選項。
舉個簡單的例子,首先建立一個文章類:
class Article(models.Model): title = models.CharField(max_length=50,verbose_name='文章標題') content = models.TextField(verbose_name='文章內容') category = models.ForeignKey(Category,blank=True,null=True,verbose_name='分類')
能夠看出,category字段導入了外鍵Category。咱們在ForeignKey函數中設置limit_choices_to參數:
class Article(models.Model): title = models.CharField(max_length=50,verbose_name='文章標題') content = models.TextField(verbose_name='文章內容') category = models.ForeignKey(Category,blank=True,null=True,verbose_name='分類',limit_choices_to={'level':2})
limit_choices_to={'level':2}的意思是隻顯示分類等級爲2的條目,即只顯示二級分類。'level'是我在Category中定義的字段,用來表明該分類的等級,通常只有一級和二級。
總的來講,limit_choices_to的做用是設置篩選條件,在admin中只顯示篩選後的內容。
設置好這個參數,Django在後臺就知道你要選擇顯示的內容了。
如此,在後臺建立文章時就會發現分類表單中只有兩個選項。
你也能夠根據你的須要,顯示你想要顯示的選項。固然對其它字段自定義Choice_field也是同樣的道理。
related_name
用於關聯對象反向引用模型的名稱。之前面書和出版社的例子解釋,就是從出版社反向關聯到書的關係名稱。
一般狀況下,這個參數咱們能夠不設置,Django會默認以模型的小寫加上_set
做爲反向關聯名,好比對於出版社就是book_set
,若是你以爲book_set
還不夠直觀,能夠以下定義:
class Book(models.Model): publisher = models.ForeignKey( 'production.Publisher', on_delete=models.CASCADE, related_name='book_producted_by_this_publisher', # 看這裏!! )
也許我定義了一個蹩腳的詞,但表達的意思很清楚。之後從工廠對象反向關聯到它所生產的汽車,就能夠使用publisher.book_producted_by_this_publisher
了。
若是你不想爲外鍵設置一個反向關聯名稱,能夠將這個參數設置爲「+」或者以「+」結尾,以下所示:
user = models.ForeignKey( User, on_delete=models.CASCADE, related_name='+', )
related_query_name
反向關聯查詢名。用於從目標模型反向過濾模型對象的名稱
class Tag(models.Model): article = models.ForeignKey( Article, on_delete=models.CASCADE, related_name="tags", related_query_name="tag", # 注意這一行 ) name = models.CharField(max_length=255) # 如今能夠使用‘tag’做爲查詢名了 Article.objects.filter(tag__name="important")
to_field
默認狀況下,外鍵都是關聯到被關聯對象的主鍵上(通常爲id)。若是指定這個參數,能夠關聯到指定的字段上,可是該字段必須具備unique=True
屬性,也就是具備惟一屬性。
db_constraint
默認狀況下,這個參數被設爲True,表示遵循數據庫約束,這也是大多數狀況下你的選擇。若是設爲False,那麼將沒法保證數據的完整性和合法性。在下面的場景中,你可能須要將它設置爲False:
當它爲False,而且你試圖訪問一個不存在的關係對象時,會拋出DoesNotExist 異常。
swappable
控制遷移框架的動做,若是當前外鍵指向一個可交換的模型。使用場景很是稀少,一般請將該參數保持默認的True。
多對多關係在數據庫中也是很是常見的關係類型。好比一本書能夠有好幾個做者,一個做者也能夠寫好幾本書。多對多的字段能夠定義在任何的一方,請儘可能定義在符合人們思惟習慣的一方,但不要同時都定義。
def __init__(self, to, related_name=None, related_query_name=None, limit_choices_to=None, symmetrical=None, through=None, through_fields=None, db_constraint=True, db_table=None, swappable=True, **kwargs): ........
多對多關係須要一個位置參數:關聯的對象模型。它的用法和外鍵多對一基本相似。
在數據庫後臺,Django實際上會額外建立一張用於體現多對多關係的中間表。默認狀況下,該表的名稱是「多對多字段名+關聯對象模型名+一個獨一無二的哈希碼」,例如‘author_books_9cdf4’,固然你也能夠經過db_table
選項,自定義表名。
參數說明:
related_name
參考外鍵的相同參數。
related_query_name
參考外鍵的相同參數。
limit_choices_to
參考外鍵的相同參數。可是對於使用through
參數自定義中間表的多對多字段無效。
symmetrical
默認狀況下,Django中的多對多關係是對稱的。看下面的例子:
from django.db import models class Person(models.Model): friends = models.ManyToManyField("self")
Django認爲,若是我是你的朋友,那麼你也是個人朋友,這是一種對稱關係,Django不會爲Person模型添加person_set
屬性用於反向關聯。若是你不想使用這種對稱關係,能夠將symmetrical設置爲False,這將強制Django爲反向關聯添加描述符。
through(定義中間表)
若是你想自定義多對多關係的那張額外的關聯表,能夠使用這個參數!參數的值爲一箇中間模型。
最多見的使用場景是你須要爲多對多關係添加額外的數據,好比兩我的創建QQ好友的時間。
一般狀況下,這張表在數據庫內的結構是這個樣子的:
中間表的id列....模型對象的id列.....被關聯對象的id列 # 各行數據
若是自定義中間表並添加時間字段,則在數據庫內的表結構以下:
中間表的id列....模型對象的id列.....被關聯對象的id列.....時間對象列 # 各行數據
看下面的例子:
from django.db import models class Person(models.Model): name = models.CharField(max_length=50) class Group(models.Model): name = models.CharField(max_length=128) members = models.ManyToManyField( Person, through='Membership', ## 自定義中間表 through_fields=('group', 'person'), ) class Membership(models.Model): # 這就是具體的中間表模型 group = models.ForeignKey(Group, on_delete=models.CASCADE) person = models.ForeignKey(Person, on_delete=models.CASCADE) inviter = models.ForeignKey( Person, on_delete=models.CASCADE, related_name="membership_invites", ) invite_reason = models.CharField(max_length=64)
上面的代碼中,經過class Membership(models.Model)
定義了一個新的模型,用來保存Person和Group模型的多對多關係,而且同時增長了‘邀請人’和‘邀請緣由’的字段。
through參數在某些使用場景中是必須的,相當重要,請務必掌握!
through_fields
接着上面的例子。Membership模型中包含兩個關聯Person的外鍵,Django沒法肯定到底使用哪一個做爲和Group關聯的對象。因此,在這個例子中,必須顯式的指定through_fields
參數,用於定義關係。
through_fields
參數接收一個二元元組('field1', 'field2'),field1是指向定義有多對多關係的模型的外鍵字段的名稱,這裏是Membership中的‘group’字段(注意大小寫),另一個則是指向目標模型的外鍵字段的名稱,這裏是Membership中的‘person’,而不是‘inviter’。
再通俗的說,就是through_fields
參數指定從中間表模型Membership中選擇哪兩個字段,做爲關係鏈接字段。
db_table
設置中間表的名稱。不指定的話,則使用默認值。
db_constraint
參考外鍵的相同參數。
swappable
參考外鍵的相同參數。
ManyToManyField多對多字段不支持Django內置的validators驗證功能。
null參數對ManyToManyField多對多字段無效!設置null=True毫無心義
一對一關係類型的定義以下:
def __init__(self, to, on_delete=None, to_field=None, **kwargs): kwargs['unique'] = True .........
從概念上講,一對一關係很是相似具備unique=True
屬性的外鍵關係,可是反向關聯對象只有一個。這種關係類型多數用於當一個模型須要從別的模型擴展而來的狀況。好比,Django自帶auth模塊的User用戶表,若是你想在本身的項目裏建立用戶模型,又想方便的使用Django的認證功能,那麼一個比較好的方案就是在你的用戶模型裏,使用一對一關係,添加一個與auth模塊User模型的關聯字段。
該關係的第一位置參數爲關聯的模型,其用法和前面的多對一外鍵同樣。
若是你沒有給一對一關係設置related_name
參數,Django將使用當前模型的小寫名做爲默認值。
看下面的例子:
from django.conf import settings from django.db import models # 兩個字段都使用一對一關聯到了Django內置的auth模塊中的User模型 class MySpecialUser(models.Model): user = models.OneToOneField( settings.AUTH_USER_MODEL, on_delete=models.CASCADE, ) supervisor = models.OneToOneField( settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='supervisor_of', )
這樣下來,你的User模型將擁有下面的屬性:
>>> user = User.objects.get(pk=1) >>> hasattr(user, 'myspecialuser') True >>> hasattr(user, 'supervisor_of') True
OneToOneField一對一關係擁有和多對一外鍵關係同樣的額外可選參數,只是多了一個parent_link參數。
跨模塊的模型:
有時候,咱們關聯的模型並不在當前模型的文件內,不要緊,就像咱們導入第三方庫同樣的從別的模塊內導入進來就好,以下例所示:
from django.db import models from geography.models import ZipCode class Restaurant(models.Model): # ... zip_code = models.ForeignKey( ZipCode, on_delete=models.SET_NULL, blank=True, null=True, )
添加數據
方式一:
只要使用ORM模型建立一個對象。而後再調用這個ORM模型的save
方法就能夠保存了。 示例代碼以下:
book = Book(name='西遊記',author='吳承恩',price=100) book.save()
方式二:
# create方法的返回值book_obj就是插入book表中的python葵花寶典這本書籍紀錄對象 book_obj=Book.objects.create(title="python葵花寶典",state=True,price=100,publish="蘋果出版社",pub_date="2012-12-12") #這個返回值就像是mysql裏面我們講的那個new對象,還記得嗎,他跟上面那種建立方式建立的那個對象是同樣的 #這個Book.objects就像是一個Book表的管理器同樣,提供了增刪改查全部的方法 print(book_obj.title) #能夠基於這個對象來取這個新添加的記錄對象的屬性值 dic1 = {'title':'linux','state'=True,'price':100,'publish'='2018-12-12'} #這樣寫的時候,注意若是你用post提交過來的請求,有個csrf_token的鍵值對要刪除,而且request.POST是不能直接在request.POST裏面進行修改和刪除的,data = request.POST.dict()轉換成普通的字典-->Book.objects.create(**data) book.objects.create(**dic1)
方式三:批量插入
book_list = [] for i in range(10): bk_obj = models.Book( name='chao%s'%i, addr='北京%s'%i ) book_list.append(bk_obj) models.Book.objects.bulk_create(book_list) #批量插入,速度快
update_or_create:有就更新,沒有就建立
obj,created = models.UserToken.objects.update_or_create( user=user, # 查找篩選條件 defaults={ # 添加或者更新的數據 "token":random_str, } )
查找數據
全部的查找工做都是使用模型上的objects
屬性來完成的。固然也能夠自定義查詢對象。這部分功能會在後面講到。
根據主鍵進行查找:使用主鍵進行查找。能夠使用objects.get
方法。而後傳遞pk=xx
的方式進行查找。示例代碼以下:
book = Book.objects.get(pk=2)
根據其餘字段進行查找:能夠使用objects.filter
方法進行查找。示例代碼以下:
books = Book.objects.filter(name='三國演義')
使用filter
方法返回來的是一個QuerySet
對象。這個對象相似於列表。咱們能夠使用這個對象的first
方法來獲取第一個值。
刪除數據
首先查找到對應的數據模型。而後再執行這個模型的delete
方法便可刪除。示例代碼以下:
book = Book.objects.get(pk=1) book.delete()
修改數據
首先查找到對應的數據模型。而後修改這個模型上的屬性的值。再執行save
方法便可修改完成。示例代碼以下:
book = Book.objects.get(pk=2) book.price = 200 book.save()
添加記錄
方式1: publish_obj=Publish.objects.get(nid=1) #拿到nid爲1的出版社對象 book_obj=Book.objects.create(title="金瓶眉",publishDate="2012-12-12",price=100,publish=publish_obj) #出版社對象做爲值給publish,其實就是自動將publish字段變成publish_id,而後將publish_obj的id給取出來賦值給publish_id字段,注意你若是不是publish類的對象確定會報錯的,別亂昂 方式2: book_obj=Book.objects.create(title="金瓶眉",publishDate="2012-12-12",price=100,publish_id=1) #直接能夠寫id值,注意字段屬性的寫法和上面不一樣,這個是publish_id=xxx,上面是publish=xxx。
**注意區別:book_obj.publish與book_obj.publish_id是什麼? **
刪除更新記錄
一對一和一對多的刪改和單表的刪改是同樣的,別忘了刪除表的時候,有些是作了級聯刪除的。
更新: book_obj = models.Book.objects.get(id=1) #獲取一個書籍對象 data = {'title':'xxx','price':100} #這個書籍對象更新後的數據 models.Book.objects.filter(id=n).update(**data) #將新數據更新到原來的記錄中 book_obj.authors.set(author_list) #將數據和做者的多對多關係加上,這麼寫也能夠,可是注意列表中的元素是字符串,列表前面沒有*,以前我測試有*,感受是版本的問題,沒事,可以用哪一個用哪一個 刪除: models.Book.objects.filter(id=1).delete()
添加
方式一: 多對多通常在前端頁面上使用的時候是多選下拉框的樣子來給用戶選擇多個數據,這裏可讓用戶選擇多個書籍,多個做者 # 當前生成的書籍對象 book_obj=Book.objects.create(title="追風箏的人",price=200,publishDate="2012-11-12",publish_id=1) # 爲書籍綁定的作做者對象 yuan=Author.objects.filter(name="yuan").first() # 在Author表中主鍵爲2的紀錄,注意取的是author的model對象 egon=Author.objects.filter(name="alex").first() # 在Author表中主鍵爲1的紀錄 #有人可能會說,咱們能夠直接給第三張表添加數據啊,這個自動生成的第三張表你能經過models獲取到嗎,是獲取不到的,用不了的,固然若是你知道了這個表的名字,那麼你經過原生sql語句能夠進行書的添加,因此要經過orm間接的給第三張表添加數據,若是是你手動添加的第三張表你是能夠直接給第三張表添加數據 # 綁定多對多關係,即向關係表book_authors中添加紀錄,給書添加兩個做者,下面的語法就是告訴orm給第三張表添加兩條數據 book_obj.authors.add(yuan,egon) # 將某些特定的 model 對象添加到被關聯對象集合中。 ======= book_obj.authors.add(*[]) #book_obj是書籍對象,authors是book表裏面那個多對多的關係字段名稱。 #其實orm就是先經過book_obj的authors屬性找到第三張表,而後將book_obj的id值和兩個做者對象的id值組合成兩條記錄添加到第三張表裏面去 方式二 book_obj.authors.add(1,2) book_obj.authors.add(*[1,2]) #這種方式用的最多,由於通常是給用戶來選擇,用戶選擇是多選的,選完給你發送過來的就是一堆的id值
理解book_obj.authors.all()是什麼
多對多關係其它經常使用API:
book_obj.authors.remove() # 將某個特定的對象從被關聯對象集合中去除。 ====== book_obj.authors.remove(*[1,2]),將多對多的關係數據刪除 book_obj.authors.clear() #清空被關聯對象集合 book_obj.authors.set() #先清空再設置 =====
刪除
book_obj = models.Book.objects.filter(nid=4)[0] # book_obj.authors.remove(2) #將第三張表中的這個book_obj對象對應的那個做者id爲2的那條記錄刪除 # book_obj.authors.clear() # book_obj.authors.set('2') #先清除掉全部的關係數據,而後只給這個書對象綁定這個id爲2的做者,因此只剩下一條記錄 3---2,好比用戶編輯數據的時候,選擇做者發生了變化,那麼須要從新選擇,因此咱們就能夠先清空,而後再從新綁定關係數據,注意這裏寫的是字符串,數字類型不能夠 book_obj.authors.set(['1',]) #這麼寫也能夠,可是注意列表中的元素是字符串,列表前面沒有*,以前我測試有*,感受是版本的問題,沒事,可以用哪一個用哪一個
跨表查詢是分組查詢的基礎,F和Q查詢是最簡單的,因此認真學習跨表查詢
一對多查詢(Publish 與 Book)
正向查詢(按字段:publish):關聯屬性字段所在的表查詢被關聯表的記錄就是正向查詢,反之就是反向查詢
# 查詢主鍵爲1的書籍的出版社所在的城市 book_obj=Book.objects.filter(pk=1).first() # book_obj.publish 是主鍵爲1的書籍對象關聯的出版社對象,book對象.外鍵字段名稱 print(book_obj.publish.city)
反向查詢(按表名:book_set,由於加上_set是由於反向查詢的時候,你查詢出來的多是多條記錄的集合):
publish=Publish.objects.get(name="蘋果出版社") #publish.book_set.all() : 與蘋果出版社關聯的全部書籍對象集合,寫法:小寫的表名_set.all(),獲得queryset類型數據 book_list=publish.book_set.all() for book_obj in book_list: print(book_obj.title)
**一對一查詢(Author與AuthorDetail) **
正向查詢(按字段:authorDetail):
egon=Author.objects.filter(name="egon").first() print(egon.authorDetail.telephone) egon.authorDeail就拿到了這個對象,由於一對一找到的就是一條記錄,注意寫法:做者對象.字段名,就拿到了那個關聯對象
反向查詢(按表名:author):不須要_set,由於一對一正向反向都是找到一條記錄
# 查詢全部住址在北京的做者的姓名 authorDet=AuthorDetail.objects.filter(addr="beijing")[0] authorDet.author.name
多對多查詢(Author與Book)
正向查詢(按字段:authors):
# 金瓶眉全部做者的名字以及手機號 book_obj=Book.objects.filter(title="金瓶眉").first() authors=book_obj.authors.all() for author_obj in authors: print(author_obj.name,author_obj.authorDetail.telephone)
反向查詢(按表名:book_set):
# 查詢egon出過的全部書籍的名字 author_obj=Author.objects.get(name="egon") book_list=author_obj.book_set.all() #與egon做者相關的全部書籍 for book_obj in book_list: print(book_obj.title)
注意:
你能夠經過在 ForeignKey() 和ManyToManyField的定義中設置 related_name 的值來覆寫 FOO_set 的名稱。例如,若是 Article model 中作一下更改:
publish = ForeignKey(Book, related_name ='bookList')
那麼接下來就會如咱們看到這般:
# 查詢 人民出版社出版過的全部書籍 publish=Publish.objects.get(name="人民出版社") book_list=publish.bookList.all() # 與人民出版社關聯的全部書籍對象集合,若是沒有related_name應該是publish.book_set.all()
Django 還提供了一種直觀而高效的方式在查詢(lookups)中表示關聯關係,它能自動確認 SQL JOIN 聯繫。要作跨關係查詢,就使用兩個下劃線來連接模型(model)間關聯字段的名稱,直到最終連接到你想要的model 爲止。
基於雙下劃線的查詢就一句話:正向查詢按字段,反向查詢按表名小寫用來告訴ORM引擎join哪張表,一對1、一對多、多對多都是一個寫法,注意,咱們寫orm查詢的時候,哪一個表在前哪一個表在後都沒問題,由於走的是join連表操做。
一對多查詢
練習: 查詢蘋果出版社出版過的全部書籍的名字與價格(一對多) # 正向查詢 按字段:publish queryResult=Book.objects .filter(publish__name="蘋果出版社") #經過__告訴orm將book表和publish表進行join,而後找到全部記錄中publish.name='蘋果出版社'的記錄(注意publish是屬性名稱),而後select book.title,book.price的字段值 .values_list("title","price") #values或者values_list # 反向查詢 按表名:book queryResult=Publish.objects .filter(name="蘋果出版社") .values_list("book__title","book__price")
多對多查詢
# 練習: 查詢yuan出過的全部書籍的名字(多對多) # 正向查詢 按字段:authors: queryResult=Book.objects .filter(authors__name="yuan") .values_list("title") # 反向查詢 按表名:book queryResult=Author.objects .filter(name="yuan") .values_list("book__title","book__price")
一對一查詢
# 查詢yuan的手機號 # 正向查詢 ret=Author.objects.filter(name="yuan").values("authordetail__telephone") # 反向查詢 ret=AuthorDetail.objects.filter(author__name="yuan").values("telephone")
進階練習(連續跨表)
# 練習: 查詢人民出版社出版過的全部書籍的名字以及做者的姓名 # 正向查詢 queryResult=Book.objects .filter(publish__name="人民出版社") .values_list("title","authors__name") # 反向查詢 queryResult=Publish.objects .filter(name="人民出版社") .values_list("book__title","book__authors__age","book__authors__name") # 練習: 手機號以151開頭的做者出版過的全部書籍名稱以及出版社名稱 # 方式1: queryResult=Book.objects .filter(authors__authorDetail__telephone__regex="151") .values_list("title","publish__name") # 方式2: ret=Author.objects .filter(authordetail__telephone__startswith="151") .values("book__title","book__publish__name")
related_name
publish = ForeignKey(Blog, related_name='bookList')
反向查詢時,若是定義了related_name ,則用related_name替換 表名,例如:
# 練習: 查詢人民出版社出版過的全部書籍的名字與價格(一對多) # 反向查詢 再也不按表名:book,而是related_name:bookList queryResult=Publish.objects .filter(name="人民出版社") .values_list("bookList__title","bookList__price")
null
若是設置爲True
,Django
將會在映射表的時候指定是否爲空。默認是爲False
。就是不能爲空,在使用字符串相關的Field
(CharField/TextField)的時候,官方推薦儘可能不要使用這個參數,也就是保持默認值False
。由於Django
在處理字符串相關的Field
的時候,即便這個Field
的null=False
,若是你沒有給這個Field
傳遞任何值,那麼Django
也會使用一個空的字符串""
來做爲默認值存儲進去。所以若是再使用null=True
,Django
會產生兩種空值的情形(NULL或者空字符串)。若是想要在表單驗證的時候容許這個字符串爲空,那麼建議使用blank=True
。若是你的Field
是BooleanField
,那麼對應的可空的字段則爲NullBooleanField
。
blank
標識這個字段在表單驗證的時候是否能夠爲空。默認是False
。 這個和null
是有區別的,null
是一個純數據庫級別的。而blank
是表單驗證級別的。
db_column
這個字段在數據庫中的名字。若是沒有設置這個參數,那麼將會使用模型中屬性的名字。
default
默認值。能夠爲一個值,或者是一個函數,可是不支持lambda
表達式。而且不支持列表/字典/集合等可變的數據結構。
from django.utils.timezone import now class Author(models.Model): create_time = models.DateTimeFField(default=now()) # time.now()返回的是一個幼稚的時間,而這裏的now返回的是一個清醒的時 間。
primary_key
若是你沒有給模型的任何字段設置這個參數爲True,Django將自動建立一個AutoField自增字段,名爲‘id’,並設置爲主鍵。也就是id = models.AutoField(primary_key=True)
。
若是你爲某個字段設置了primary_key=True,則當前字段變爲主鍵,並關閉Django自動生成id主鍵的功能。
primary_key=True隱含null=False和unique=True的意思。一個模型中只能有一個主鍵字段!
另外,主鍵字段不可修改,若是你給某個對象的主鍵賦個新值其實是建立一個新對象,並不會修改原來的對象。
from django.db import models class Fruit(models.Model): name = models.CharField(max_length=100, primary_key=True) ############### >>> fruit = Fruit.objects.create(name='Apple') >>> fruit.name = 'Pear' >>> fruit.save() >>> Fruit.objects.values_list('name', flat=True) ['Apple', 'Pear']
unique
在表中這個字段的值是否惟一。通常是設置手機號碼/郵箱等。
注意:對於ManyToManyField和OneToOneField關係類型,該參數無效。
注意: 當unique=True時,db_index參數無須設置,由於unqiue隱含了索引。
注意:自1.11版本後,unique參數能夠用於FileField字段。
choices
用於頁面上的選擇框標籤,須要先提供一個二維的二元元組,第一個元素表示存在數據庫內真實的值,第二個表示頁面上顯示的具體內容。在瀏覽器頁面上將顯示第二個元素的值。例如:
YEAR_IN_SCHOOL_CHOICES = ( ('FR', 'Freshman'), ('SO', 'Sophomore'), ('JR', 'Junior'), ('SR', 'Senior'), ('GR', 'Graduate'), )
通常來講,最好將選項定義在類裏,並取一個直觀的名字,以下所示:
from django.db import models class Student(models.Model): FRESHMAN = 'FR' SOPHOMORE = 'SO' JUNIOR = 'JR' SENIOR = 'SR' YEAR_IN_SCHOOL_CHOICES = ( (FRESHMAN, 'Freshman'), (SOPHOMORE, 'Sophomore'), (JUNIOR, 'Junior'), (SENIOR, 'Senior'), ) year_in_school = models.CharField( max_length=2, choices=YEAR_IN_SCHOOL_CHOICES, default=FRESHMAN, ) def is_upperclass(self): return self.year_in_school in (self.JUNIOR, self.SENIOR)
要獲取一個choices的第二元素的值,能夠使用get_FOO_display()
方法,其中的FOO用字段名代替。對於下面的例子:
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'
db_index
該參數接收布爾值。若是爲True,數據庫將爲該字段建立索引。
db_tablespace
用於字段索引的數據庫表空間的名字,前提是當前字段設置了索引。默認值爲工程的DEFAULT_INDEX_TABLESPACE
設置。若是使用的數據庫不支持表空間,該參數會被忽略。
editable
若是設爲False,那麼當前字段將不會在admin後臺或者其它的ModelForm表單中顯示,同時還會被模型驗證功能跳過。參數默認值爲True。
error_messages
用於自定義錯誤信息。參數接收字典類型的值。字典的鍵能夠是null
、 blank
、 invalid
、 invalid_choice
、 unique
和unique_for_date
其中的一個。
help_text
額外顯示在表單部件上的幫助文本。使用時請注意轉義爲純文本,防止腳本攻擊。
unique_for_date
日期惟一。可能不太好理解。舉個栗子,若是你有一個名叫title的字段,並設置了參數unique_for_date="pub_date"
,那麼Django將不容許有兩個模型對象具有一樣的title和pub_date。有點相似聯合約束。
unique_for_month
同上,只是月份惟一。
unique_for_year
同上,只是年份惟一。
verbose_name
爲字段設置一我的類可讀,更加直觀的別名。
對於每個字段類型,除了ForeignKey
、ManyToManyField
和OneToOneField
這三個特殊的關係類型,其第一可選位置參數都是verbose_name
。若是沒指定這個參數,Django會利用字段的屬性名自動建立它,並將下劃線轉換爲空格。
下面這個例子的verbose name
是"第一個名子":
first_name = models.CharField("第一個名子", max_length=30)
下面這個例子的verbose name
是"first name":
first_name = models.CharField(max_length=30)
對於外鍵、多對多和一對一字字段,因爲第一個參數須要用來指定關聯的模型,所以必須用關鍵字參數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自動爲你完成這一工做。
validators
運行在該字段上的驗證器的列表。
關於auto_now,你須要知道的事情
當須要更新時間的時候,咱們儘可能經過datetime模塊來建立當前時間,並保存或者更新到數據庫裏面,看下面的分析: 假如咱們的表結構是這樣的
class User(models.Model):
username = models.CharField(max_length=255, unique=True, verbose_name='用戶名') is_active = models.BooleanField(default=False, verbose_name='激活狀態')
那麼咱們修改用戶名和狀態能夠使用以下兩種方法:
方法一:
User.objects.filter(id=1).update(username='nick',is_active=True)
方法二:
_t = User.objects.get(id=1) _t.username='nick' _t.is_active=True _t.save()
方法一適合更新一批數據,相似於mysql語句update user set username='nick' where id = 1
方法二適合更新一條數據,也只能更新一條數據,當只有一條數據更新時推薦使用此方法,另外此方法還有一個好處,咱們接着往下看
具備auto_now屬性字段的更新 咱們一般會給表添加三個默認字段
就像下邊這樣的表結構
class User(models.Model): create_time = models.DateTimeField(auto_now_add=True, verbose_name='建立時間') update_time = models.DateTimeField(auto_now=True, verbose_name='更新時間') username = models.CharField(max_length=255, unique=True, verbose_name='用戶名') is_active = models.BooleanField(default=False, verbose_name='激活狀態')
當表有字段具備auto_now屬性且你但願他能自動更新時,必須使用上邊方法二的更新,否則auto_now字段不會更新,也就是:
_t = User.objects.get(id=1) _t.username='nick' _t.is_active=True _t.save()
json/dict類型數據更新字段 目前主流的web開放方式都講究先後端分離,分離以後先後端交互的數據格式大都用通用的jason型,那麼如何用最少的代碼方便的更新json格式數據到數據庫呢?一樣能夠使用以下兩種方法:
方法一:
data = {'username':'nick','is_active':'0'} User.objects.filter(id=1).update(**data)
一樣這種方法不能自動更新具備auto_now屬性字段的值 一般咱們再變量前加一個星號(*)表示這個變量是元組/列表,加兩個星號表示這個參數是字典 方法二:
data = {'username':'nick','is_active':'0'} _t = User.objects.get(id=1) _t.__dict__.update(**data) _t.save()
方法二和方法一一樣沒法自動更新auto_now字段的值 注意這裏使用到了一個__dict__方法 方法三:
_t = User.objects.get(id=1) _t.role=Role.objects.get(id=3) _t.save()
想讓auto_now更新數據時自動更新時間,必須使用save方法來更新數據,因此很不方便,因此這個建立時自動添加時間或者更新時間的auto_now方法咱們最好就別用了,比較噁心,而且支持咱們本身來給這個字段更新時間:
models.py: class Book(models.Model): name = models.CharField(max_length=32) date1 = models.DateTimeField(auto_now=True,null=True) date2 = models.DateTimeField(auto_now_add=True,null=True) views.py: import datetime models.Book.objects.filter(id=1).update( name='chao', date1=datetime.datetime.now(), date2=datetime.datetime.now(), )
更多Field
參數請參考官方文檔:https://docs.djangoproject.com/zh-hans/2.0/ref/models/fields/
咱們都知道對於ManyToMany字段,Django採用的是第三張中間表的方式。經過這第三張表,來關聯ManyToMany的雙方。下面咱們根據一個具體的例子,詳細解說中間表的使用。
首先,模型是這樣的:
class Person(models.Model): name = models.CharField(max_length=128) def __str__(self): return self.name class Group(models.Model): name = models.CharField(max_length=128) members = models.ManyToManyField(Person) def __str__(self): return self.name
在Group模型中,經過members字段,以ManyToMany方式與Person模型創建了關係。
讓咱們到數據庫內看一下實際的內容,Django爲咱們建立了三張數據表,其中的app1是應用名。
而後我在數據庫中添加了下面的Person對象:
再添加下面的Group對象:
讓咱們來看看,中間表是個什麼樣子的:
首先有一列id,這是Django默認添加的,沒什麼好說的。而後是Group和Person的id列,這是默認狀況下,Django關聯兩張表的方式。若是你要設置關聯的列,能夠使用to_field參數。
可見在中間表中,並非將兩張表的數據都保存在一塊兒,而是經過id的關聯進行映射。
通常狀況,普通的多對多已經夠用,無需本身建立第三張關係表。可是某些狀況可能更復雜一點,好比若是你想保存某我的加入某個分組的時間呢?想保存進組的緣由呢?
Django提供了一個through
參數,用於指定中間模型,你能夠將相似進組時間,邀請緣由等其餘字段放在這個中間模型內。例子以下:
from django.db import models class Person(models.Model): name = models.CharField(max_length=128) def __str__(self): return self.name class Group(models.Model): name = models.CharField(max_length=128) members = models.ManyToManyField(Person, through='Membership') def __str__(self): return self.name class Membership(models.Model): person = models.ForeignKey(Person, on_delete=models.CASCADE) group = models.ForeignKey(Group, on_delete=models.CASCADE) date_joined = models.DateField() # 進組時間 invite_reason = models.CharField(max_length=64) # 邀請緣由
在中間表中,咱們至少要編寫兩個外鍵字段,分別指向關聯的兩個模型。在本例中就是‘Person’和‘group’。 這裏,咱們額外增長了‘date_joined’字段,用於保存人員進組的時間,‘invite_reason’字段用於保存邀請進組的緣由。
下面咱們依然在數據庫中實際查看一下(應用名爲app2):
注意中間表的名字已經變成「app2_membership」了。
Person和Group沒有變化。
可是中間表就大相徑庭了!它完美的保存了咱們須要的內容。
針對上面的中間表,下面是一些使用例子(以歐洲著名的甲殼蟲樂隊成員爲例):
>>> ringo = Person.objects.create(name="Ringo Starr") >>> paul = Person.objects.create(name="Paul McCartney") >>> beatles = Group.objects.create(name="The Beatles") >>> m1 = Membership(person=ringo, group=beatles, ... date_joined=date(1962, 8, 16), ... invite_reason="Needed a new drummer.") >>> m1.save() >>> beatles.members.all() <QuerySet [<Person: Ringo Starr>]> >>> ringo.group_set.all() <QuerySet [<Group: The Beatles>]> >>> m2 = Membership.objects.create(person=paul, group=beatles, ... date_joined=date(1960, 8, 1), ... invite_reason="Wanted to form a band.") >>> beatles.members.all() <QuerySet [<Person: Ringo Starr>, <Person: Paul McCartney>]>
與普通的多對多不同,使用自定義中間表的多對多不能使用add(), create(),remove(),和set()方法來建立、刪除關係,看下面:
>>> # 無效 >>> beatles.members.add(john) >>> # 無效 >>> beatles.members.create(name="George Harrison") >>> # 無效 >>> beatles.members.set([john, paul, ringo, george])
爲何?由於上面的方法沒法提供加入時間、邀請緣由等中間模型須要的字段內容。惟一的辦法只能是經過建立中間模型的實例來建立這種類型的多對多關聯。可是,clear()方法是有效的,它能清空全部的多對多關係。
>>> # 甲殼蟲樂隊解散了 >>> beatles.members.clear() >>> # 刪除了中間模型的對象 >>> Membership.objects.all() <QuerySet []>
一旦你經過建立中間模型實例的方法創建了多對多的關聯,你馬上就能夠像普通的多對多那樣進行查詢操做:
# 查找組內有Paul這我的的全部的組(以Paul開頭的名字) >>> Group.objects.filter(members__name__startswith='Paul') <QuerySet [<Group: The Beatles>]>
能夠使用中間模型的屬性進行查詢:
# 查找甲殼蟲樂隊中加入日期在1961年1月1日以後的成員 >>> Person.objects.filter( ... group__name='The Beatles', ... membership__date_joined__gt=date(1961,1,1)) <QuerySet [<Person: Ringo Starr]>
能夠像普通模型同樣使用中間模型:
>>> ringos_membership = Membership.objects.get(group=beatles, person=ringo) >>> ringos_membership.date_joined datetime.date(1962, 8, 16) >>> ringos_membership.invite_reason 'Needed a new drummer.' >>> ringos_membership = ringo.membership_set.get(group=beatles) >>> ringos_membership.date_joined datetime.date(1962, 8, 16) >>> ringos_membership.invite_reason 'Needed a new drummer.'
對於中間表,有一點要注意(在前面章節已經介紹過,再次重申一下),默認狀況下,中間模型只能包含一個指向源模型的外鍵關係,上面例子中,也就是在Membership中只能有Person和Group外鍵關係各一個,不能多。不然,你必須顯式的經過ManyToManyField.through_fields
參數指定關聯的對象。參考下面的例子:
from django.db import models class Person(models.Model): name = models.CharField(max_length=50) class Group(models.Model): name = models.CharField(max_length=128) members = models.ManyToManyField( Person, through='Membership', through_fields=('group', 'person'), ) class Membership(models.Model): group = models.ForeignKey(Group, on_delete=models.CASCADE) person = models.ForeignKey(Person, on_delete=models.CASCADE) inviter = models.ForeignKey( Person, on_delete=models.CASCADE, related_name="membership_invites", ) invite_reason = models.CharField(max_length=64)
Meta
配置對於一些模型級別的配置。咱們能夠在模型中定義一個類,叫作Meta
。而後在這個類中添加一些類屬性來控制模型的做用。好比咱們想要在數據庫映射的時候使用本身指定的表名,而不是使用模型的名稱。那麼咱們能夠在Meta
類中添加一個db_table
的屬性。示例代碼以下:
class Book(models.Model): name = models.CharField(max_length=20,null=False) desc = models.CharField(max_length=100,name='description',db_column="description1") class Meta: # 注意,是模型的子類,要縮進! db_table = 'book_model'
如下將對Meta
類中的一些經常使用配置進行解釋。
db_table
這個模型映射到數據庫中的表名。若是沒有指定這個參數,那麼在映射的時候將會使用模型名來做爲默認的表名。
db_tablespace
自定義數據庫表空間的名字。默認值是工程的DEFAULT_TABLESPACE
設置。
ordering
設置在提取數據的排序方式。後面章節會講到如何查找數據。好比我想在查找數據的時候根據添加的時間排序,那麼示例代碼以下:
class Book(models.Model): name = models.CharField(max_length=20,null=False) desc = models.CharField(max_length=100,name='description',db_column="description1") pub_date = models.DateTimeField(auto_now_add=True) class Meta: db_table = 'book_model' ordering = ['pub_date'] # 相取反的話['-pub_date']加個負號
abstract
若是abstract=True
,那麼模型會被認爲是一個抽象模型。抽象模型自己不實際生成數據庫表,而是做爲其它模型的父類,被繼承使用。具體內容能夠參考Django模型的繼承。
app_label
若是定義了模型的app沒有在INSTALLED_APPS
中註冊,則必須經過此元選項聲明它屬於哪一個app,例如:
app_label = 'myapp'
base_manager_name
自定義模型的_base_manager
管理器的名字。模型管理器是Django爲模型提供的API所在。Django1.10新增。
default_manager_name
自定義模型的_default_manager
管理器的名字。Django1.10新增。
default_related_name
默認狀況下,從一個模型反向關聯設置有關係字段的源模型,咱們使用<model_name>_set
,也就是源模型的名字+下劃線+set
。
這個元數據選項可讓你自定義反向關係名,同時也影響反向查詢關係名!看下面的例子:
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) >>> # 而要使用你本身定義的"bars"了。 >>> Foo.objects.get(bars=bar)
get_latest_by
Django管理器給咱們提供有latest()和earliest()方法,分別表示獲取最近一個和最前一個數據對象。可是,如何來判斷最近一個和最前面一個呢?也就是根據什麼來排序呢?
get_latest_by
元數據選項幫你解決這個問題,它能夠指定一個相似 DateField
、DateTimeField
或者IntegerField
這種能夠排序的字段,做爲latest()和earliest()方法的排序依據,從而得出最近一個或最前面一個對象。例如:
get_latest_by = "order_date"
managed
該元數據默認值爲True,表示Django將按照既定的規則,管理數據庫表的生命週期。
若是設置爲False,將不會針對當前模型建立和刪除數據庫表。在某些場景下,這可能有用,但更多時候,你能夠忘記該選項。
order_with_respect_to
這個選項很差理解。其用途是根據指定的字段進行排序,一般用於關係字段。看下面的例子:
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'
上面在Answer模型中設置了order_with_respect_to = 'question'
,這樣的話,Django會自動提供兩個API,get_RELATED_order()
和set_RELATED_order()
,其中的RELATED
用小寫的模型名代替。假設如今有一個Question對象,它關聯着多個Answer對象,下面的操做返回包含關聯的Anser對象的主鍵的列表[1,2,3]:
>>> question = Question.objects.get(id=1) >>> question.get_answer_order() [1, 2, 3]
咱們能夠經過set_RELATED_order()
方法,指定上面這個列表的順序:
>>> question.set_answer_order([3, 1, 2])
一樣的,關聯的對象也得到了兩個方法get_next_in_order()
和get_previous_in_order()
,用於經過特定的順序訪問對象,以下所示:
>>> answer = Answer.objects.get(id=2) >>> answer.get_next_in_order() <Answer: 3> >>> answer.get_previous_in_order() <Answer: 1>
這個元數據的做用......還沒用過,囧。
ordering
最經常使用的元數據之一了!
用於指定該模型生成的全部對象的排序方式,接收一個字段名組成的元組或列表。默認按升序排列,若是在字段名前加上字符「-」則表示按降序排列,若是使用字符問號「?」表示隨機排列。請看下面的例子:
ordering = ['pub_date'] # 表示按'pub_date'字段進行升序排列 ordering = ['-pub_date'] # 表示按'pub_date'字段進行降序排列 ordering = ['-pub_date', 'author'] # 表示先按'pub_date'字段進行降序排列,再按`author`字段進行升序排列。
permissions
該元數據用於當建立對象時增長額外的權限。它接收一個全部元素都是二元元組的列表或元組,每一個元素都是(權限代碼, 直觀的權限名稱)
的格式。好比下面的例子:
permissions = (("can_deliver_pizzas", "能夠送披薩"),)
default_permissions
Django默認給全部的模型設置('add', 'change', 'delete')的權限,也就是增刪改。你能夠自定義這個選項,好比設置爲一個空列表,表示你不須要默認的權限,可是這一操做必須在執行migrate命令以前。
proxy
若是設置了proxy = True
,表示使用代理模式的模型繼承方式。具體內容與abstract選項同樣,參考模型繼承章節。
required_db_features
聲明模型依賴的數據庫功能。好比['gis_enabled'],表示模型的創建依賴GIS功能。
required_db_vendor
聲明模型支持的數據庫。Django默認支持sqlite, postgresql, mysql, oracle
。
select_on_save
決定是否使用1.6版本以前的django.db.models.Model.save()
算法保存對象。默認值爲False。這個選項咱們一般不用關心。
indexes
Django1.11新增的選項。
接收一個應用在當前模型上的索引列表,以下例所示:
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
這個元數據是很是重要的一個!它等同於數據庫的聯合約束!
舉個例子,假設有一張用戶表,保存有用戶的姓名、出生日期、性別和籍貫等等信息。要求是全部的用戶惟一不重複,可如今有好幾個叫「張偉」的,如何區別它們呢?(不要和我說主鍵惟一,這裏討論的不是這個問題)
咱們能夠設置不能有兩個用戶在同一個地方同一時刻出生而且都叫「張偉」,使用這種聯合約束,保證數據庫能不能重複添加用戶(也不要和我談小几率問題)。在Django的模型中,如何實現這種約束呢?
使用unique_together
,也就是聯合惟一!
好比:
unique_together = (('name', 'birth_day', 'address'),)
聯合惟一沒法做用於普通的多對多字段。
index_together
即將廢棄,使用index
元數據代替。
verbose_name
最經常使用的元數據之一!用於設置模型對象的直觀、人類可讀的名稱。能夠用中文。例如:
verbose_name = "story" verbose_name = "披薩"
verbose_name_plural
英語有單數和複數形式。這個就是模型對象的複數名,好比「apples」。由於咱們中文一般不區分單複數,因此保持和verbose_name
一致也能夠。
verbose_name_plural = "stories" verbose_name_plural = "披薩"
若是不指定該選項,那麼默認的複數名字是verbose_name
加上‘s’
label
前面介紹的元數據都是可修改和設置的,但還有兩個只讀的元數據,label就是其中之一。
label等同於app_label.object_name
。例如polls.Question
,polls是應用名,Question是模型名。
label_lower
同上,不過是小寫的模型名
官方文檔:https://docs.djangoproject.com/en/2.0/ref/models/options/
在window機器上mysql在排序的時候不管排序規則怎麼樣,都對大小寫不敏感,可是在linux上,mysql的排序規則若是是utf8_bin,就對大小寫敏感
下表列出了全部的字段查詢參數:
字段名 | 說明 |
---|---|
exact | 精確匹配 |
iexact | 不區分大小寫的精確匹配 |
contains | 包含匹配 |
icontains | 不區分大小寫的包含匹配 |
in | 在..以內的匹配 |
gt | 大於 |
gte | 大於等於 |
lt | 小於 |
lte | 小於等於 |
startswith | 從開頭匹配 |
istartswith | 不區分大小寫從開頭匹配 |
endswith | 從結尾處匹配 |
iendswith | 不區分大小寫從結尾處匹配 |
range | 範圍匹配 |
date | 日期匹配 |
year | 年份 |
month | 月份 |
day | 日期 |
week | 第幾周 |
week_day | 周幾 |
time | 時間 |
hour | 小時 |
minute | 分鐘 |
second | 秒 |
isnull | 判斷是否爲空 |
search | 1.10中被廢棄 |
regex | 區分大小寫的正則匹配 |
iregex | 不區分大小寫的正則匹配 |
exact:在底層會被翻譯成=
。
iexact:在底層會被翻譯成LIKE
。
field__exact=xxx
其實等價於filed=xxx
,所以咱們直接使用filed=xxx
就能夠了,而且由於大部分狀況exact
和iexact
又是等價的,所以咱們之後直接使用field=xxx
就能夠了。QuerySet.query:query
能夠用來查看這個ORM
查詢語句最終被翻譯成的SQL
語句。可是query
只能被用在QuerySet
對象上,不能用在普通的ORM模型
上。所以若是你的查詢語句是經過get
來獲取數據的,那麼就不能使用query
,由於get
返回的是知足條件的ORM
模型,而不是QuerySet
。若是你是經過filter
等其餘返回QuerySet
的方法查詢的,那麼就能夠使用query
。
contains:使用大小寫敏感的判斷,某個字符串是否在指定的字段中。這個判斷條件會使用大小敏感,所以在被翻譯成SQL
語句的時候,會使用like binary
,而like binary
就是使用大小寫敏感的。
icontains:使用大小寫不敏感的判斷,某個字符串是否被包含在指定的字段中。這個查詢語句在被翻譯成SQL
的時候,使用的是like
,而like
在MySQL
層面就是不區分大小寫的。
contains和icontains:在被翻譯成SQL
的時候使用的是%hello%
,就是隻要整個字符串中出現了hello
都能過夠被找到,而iexact
沒有百分號,那麼意味着只有徹底相等的時候纔會被匹配到。
in:能夠直接指定某個字段的是否在某個集合中。示例代碼以下:
articles = Article.objects.filter(id__in=[1,2,3])
也能夠經過其餘的表的字段來判斷是否在某個集合中。示例代碼以下:
categories = Category.objects.filter(article__id__in=[1,2,3])
若是要判斷相關聯的表的字段,那麼也是經過__
來鏈接。而且在作關聯查詢的時候,不須要寫models_set
,直接使用模型的名字的小寫化
就能夠了。好比經過分類去查找相應的文章,那麼經過article__id__in
就能夠了,而不是寫成article_set__id__in
的形式。固然若是你不想使用默認的形式,能夠在外鍵定義的時候傳遞related_query_name
來指定反向查詢的名字。示例代碼以下:
class Category(models.Model): name = models.CharField(max_length=100) class Meta: db_table = 'category'
class Article(models.Model): title = models.CharField(max_length=200) content = models.TextField() cateogry = models.ForeignKey("Category",on_delete=models.CASCADE,null=True,related_query_name='articles') class Meta: db_table = 'article'
由於在cateogry
的ForeignKey
中指定了related_query_name
爲articles
,所以你不能再使用article
來進行反向查詢了。這時候就須要經過articles__id__in
來進行反向查詢。
反向查詢是將模型名字小寫化。好比article__in
。能夠經過related_query_name
來指定本身的方式,而不使用默認的方式。 反向引用是將模型名字小寫化,而後再加上_set
,好比article_set
,能夠經過related_name
來指定本身的方式,而不是用默認的方式。
而且,若是在作反向查詢的時候,若是查詢的字段就是模型的主鍵,那麼能夠省略掉這個字段,直接寫成article__in
就能夠了,不須要這個id
了。
in
不只僅能夠指定列表/元組,還能夠爲QuerySet
。好比要查詢「文章標題中包含有hello的全部分類」,那麼能夠經過如下代碼來實現:
articles = Article.objects.filter(title__icontains='hello') categories = Category.objects.filter(articles__in=articles) for cateogry in categories: print(cateogry)
gt、gte、lt、lte:表明的是大於、大於等於、小於、小於等於的條件。示例代碼以下:
articles = Article.objects.filter(id__lte=3)
startswith、istartswith、endswith、iendswith:表示以某個值開始,不區分大小寫的以某個值開始、以某個值結束、不區分大小寫的以某個值結束。示例代碼以下:
articles = Article.objects.filter(title__endswith="hello")
關於時間的查詢條件:
range:能夠指定一個時間段。而且時間應該標記爲aware
時間,否則django會報警告。示例代碼以下:
from django.utils.timezone import make_aware start_time = make_aware(datetime(year=2018,month=4,day=4,hour=17,minute=0,second=0)) end_time = make_aware(datetime(year=2018,month=4,day=4,hour=18,minute=0,second=0)) articles = Article.objects.filter(create_time__range=(start_time,end_time)) print(articles.query) print(articles)
date:用年月日來進行過濾。若是想要使用這個過濾條件,那麼前提必需要在MySQL
中添加好那些時區文件。如何添加呢?參考教案。示例代碼以下:
articles = Article.objects.filter(create_time__date=datetime(year=2018,month=4,day=4))
year/month/day:表示根據年/月/日進行查找。示例代碼以下:
articles = Article.objects.filter(create_time__year__gte=2018)
week_day:根據星期來進行查找。1表示星期天,7表示星期六,2-6表明的是星期一到星期五。好比要查找星期三的全部文章,那麼能夠經過如下代碼來實現:
articles = Article.objects.filter(create_time__week_day=4)
time:根據分時秒來進行查找。若是要具體到秒,通常比較難匹配到,能夠使用區間的方式來進行查找。區間使用range
條件。好比想要獲取17時/10分/27-28秒之間的文章,那麼能夠經過如下代碼來實現:
start_time = time(hour=17,minute=10,second=27) end_time = time(hour=17,minute=10,second=28) articles = Article.objects.filter(create_time__time__range=(start_time,end_time))
根據值是否爲空進行查找。示例代碼以下:
articles = Article.objects.filter(pub_date__isnull=False)
以上的代碼的意思是獲取全部發布日期不爲空的文章。 未來翻譯成SQL
語句以下:
select ... where pub_date is not null;
大小寫敏感和大小寫不敏感的正則表達式。示例代碼以下:
articles = Article.objects.filter(title__regex=r'^hello')
以上代碼的意思是提取全部標題以hello
字符串開頭的文章。 將翻譯成如下的SQL
語句:
select ... where title regexp binary '^hello';
iregex
是大小寫不敏感的。
根據關聯的表進行查詢
假如如今有兩個ORM
模型,一個是Article
,一個是Category
。代碼以下:
class Category(models.Model): """文章分類表""" name = models.CharField(max_length=100) class Article(models.Model): """文章表""" title = models.CharField(max_length=100,null=True) category = models.ForeignKey("Category",on_delete=models.CASCADE)
好比想要獲取文章標題中包含"hello"的全部的分類。那麼能夠經過如下代碼來實現:
categories = Category.object.filter(article__title__contains("hello"))
全部的聚合函數都是放在django.db.models
下面。
聚合函數不可以單獨的執行,須要放在一些能夠執行聚合函數的方法下面中去執行。好比aggregate
。示例代碼以下:
result = Book.objects.aggregate(Avg("price"))
聚合函數執行完成後,給這個聚合函數的值取個名字。取名字的規則,默認是filed+__+聚合函數名字
造成的。好比以上代碼造成的名字叫作price__avg
。若是不想使用默認的名字,那麼能夠在使用聚合函數的時候傳遞關鍵字參數進去,參數的名字就是聚合函數執行完成的名字。實示例代碼以下:
result = Book.objects.aggregate(avg=Avg("price"))
以上傳遞了關鍵字參數avg=Avg("price")
,那麼之後Avg
聚合函數執行完成的名字就叫作avg
。
aggregate
:這個方法不會返回一個QuerySet
對象,而是返回一個字典。這個字典中的key就是聚合函數的名字,值就是聚合函數執行後的結果。
aggregate
和annotate
的相同和不一樣:
aggregate
返回的是一個字典,在這個字典中存儲的是這個聚合函數執行的結果。而annotate
返回的是一個QuerySet
對象,而且會在查找的模型上添加一個聚合函數的屬性。aggregate
不會作分組,而annotate
會使用group by
子句進行分組,只有調用了group by
子句,才能對每一條數據求聚合函數的值。Count
:用來求某個數據的個數。好比要求全部圖書的數量,那麼能夠使用如下代碼:
result = Book.objects.aggregate(book_nums=Count("id"))
而且Count
能夠傳遞distinct=True
參數,用來剔除那些重複的值,只保留一個。好比要獲取做者表中,不一樣郵箱的個數,那麼這時候能夠使用distinct=True
。示例代碼以下:
result = Author.objects.aggregate(email_nums=Count('email',distinct=True))
也能夠傳遞一個參數filter,用來過濾須要統計的條件,能夠用Q作爲表達式
Max
和Min
:求指定字段的最大值和最小值。示例代碼以下:
result = Author.objects.aggregate(max=Max("age"),min=Min("age"))
Sum
:求某個字段值的總和。示例代碼以下:
result = BookOrder.objects.aggregate(total=Sum('price'))
aggregate
和annotate
方法能夠在任何的QuerySet
對象上調用。所以只要是返回了QuerySet
對象,那麼就能夠進行鏈式調用。好比要獲取2018年度的銷售總額,那麼能夠先過濾年份,再求聚合函數。示例代碼以下:
BookOrder.objects.filter(create_time__year=2018).aggregate(total=Sum('price'))
F表達式
: 動態的獲取某個字段上的值。而且這個F表達式,不會真正的去數據庫中查詢數據,他至關於只是起一個標識的做用。好比想要將原來每本圖書的價格都在原來的基礎之上增長10元,那麼能夠使用如下代碼來實現:
from django.db.models import F Book.objects.update(price=F("price")+10)
Q表達式
:使用Q
表達式包裹查詢條件,能夠在條件之間進行多種操做。與/或非等,從而實現一些複雜的查詢操做。例子以下:
# 不使用Q表達式的 books = Book.objects.filter(price__gte=100,rating__gte=4.85) # 使用Q表達式的 books = Book.objects.filter(Q(price__gte=100)&Q(rating__gte=4.85))
books = Book.objects.filter(Q(price__gte=100)&Q(rating__gte=4.85))
books = Book.objects.filter(Q(price__gte=100)&~Q(name__icontains='傳'))
這個對象是django.db.models.manager.Manager
的對象,這個類是一個空殼類,他上面的全部方法都是從QuerySet
這個類上面拷貝過來的。所以咱們只要學會了QuerySet
,這個objects
也就知道該如何使用了。 Manager
源碼解析:
class_name = "BaseManagerFromQuerySet" class_dict = { '_queryset_class': QuerySet } class_dict.update(cls._get_queryset_methods(QuerySet)) # type動態的時候建立類 # 第一個參數是用來指定建立的類的名字。建立的類名是:BaseManagerFromQuerySet # 第二個參數是用來指定這個類的父類。 # 第三個參數是用來指定這個類的一些屬性和方法 return type(class_name,(cls,),class_dict) _get_queryset_methods:這個方法就是將QuerySet中的一些方法拷貝出來
**如下的方法都將返回一個新的QuerySets。**重點是加粗的幾個API,其它的使用場景不多。
方法名 | 解釋 |
---|---|
filter() | 過濾查詢對象。 |
exclude() | 排除知足條件的對象 |
annotate() | 使用聚合函數 |
order_by() | 對查詢集進行排序 |
reverse() | 反向排序 |
distinct() | 對查詢集去重 |
values() | 返回包含對象具體值的字典的QuerySet |
values_list() | 與values()相似,只是返回的是元組而不是字典。 |
dates() | 根據日期獲取查詢集 |
datetimes() | 根據時間獲取查詢集 |
none() | 建立空的查詢集 |
all() | 獲取全部的對象 |
union() | 並集 |
intersection() | 交集 |
difference() | 差集 |
select_related() | 附帶查詢關聯對象 |
prefetch_related() |
預先查詢 |
extra() | 附加SQL查詢 |
defer() | 不加載指定字段 |
only() | 只加載指定的字段 |
using() | 選擇數據庫 |
select_for_update() |
鎖住選擇的對象,直到事務結束。 |
raw() | 接收一個原始的SQL查詢 |
filter/exclude/annotate
過濾/排除知足條件的/給模型添加新的字段。
order_by
# 根據建立的時間正序排序 articles = Article.objects.order_by("create_time") # 根據建立的時間倒序排序 articles = Article.objects.order_by("-create_time") # 根據做者的名字進行排序 articles = Article.objects.order_by("author__name") # 首先根據建立的時間進行排序,若是時間相同,則根據做者的名字進行排序 articles = Article.objects.order_by("create_time",'author__name')
必定要注意的一點是,多個order_by
,會把前面排序的規則給打亂,而使用後面的排序方式。好比如下代碼:
articles = Article.objects.order_by("create_time").order_by("author__name")
他會根據做者的名字進行排序,而不是使用文章的建立時間。 固然,也能夠在模型定義的在Meta
類中定義ordering
來指定默認的排序方式。示例代碼以下:
class Meta: db_table = 'book_order' ordering = ['create_time','-price']
還能夠根據annotate
定義的字段進行排序。好比要實現圖書的銷量進行排序,那麼示例代碼以下:
books = Book.objects.annotate(order_nums=Count("bookorder")).order_by("-order_nums") for book in books: print('%s/%s'%(book.name,book.order_nums))
values
有時候咱們在表中查找數據的時候,並非想把全部的字段都提取出來,咱們有可能只想要其中的幾個字段,這時候就能夠使用values
來實現,須要什麼字段,就把這個字段的名子傳到這個方法中,示例代碼以下:
books = Book.objects.values("id", "name")
values
的返回值一樣也是一個QuerySet
, 可是這個QuerySet
中裝的就再也不是模型了,而是字典。
若是咱們想要提取的是這個模型上關聯對象的屬性,那麼也是能夠的,查找順序跟fiter
的用法是同樣的。
books = Book.objects.values('id', "name", 'author__name')
以上將會提取author
的name
字段,若是咱們不想要這個名字,想要更改一個名字,那麼能夠使用關鍵字參數。注意這個名子不能和模型自己有的名子同樣。
books = Book.object.values("id", "name", author_name=F("author__name"))
在values
中也能夠使用聚合函數來造成一個新的字段,好比,我想要獲取每本書的銷量,那麼示例代碼以下:
books = Book.objects.values("id", "name", order_nums=Count('bookorder'))
若是 使用values
方法的時候,沒有傳遞任何的參數,那麼會獲取這個模型上的全部字段以及對應的值造成的字典。
values_list
跟values
是同樣的做用,只不過這個方法返回的QuerySet
中,裝的不是字典,而是元組
books = Book.objects.values_list("id", "name")
那麼以上代碼的返回結果是:
(1, "三國演義")
若是 給values_list
只指定一個字段 ,那麼咱們能夠指定flat=True
,這樣返回回來的結果就再也不是一個元組,而是這個字段的值。
books = Book.objects.values_list(name', flat=True)
那麼以上返回 的結果 是:
'三國演義'
必定要注意的是,flat
只能用在只有一個字段的狀況下,不然就會報錯。
all
這個返回簡單的返回一個QuerySet
對象,這個QuerySet
對象沒有通過任何的修改,好比:過濾等
select_related
在查找某個表的數據的時候,能夠一次性把相關聯的其餘表的數據都提取出來,這樣能夠在之後訪問相關聯的表的數據的時候,不用再次查找數據庫,能夠節省一些開銷,注意的是隻能用在外鍵關聯的對象上,也就是正向查找,對於多對多,反向查詢的狀況下不能使用。而應該使用prefetch_relate
來實現。
print(models.Book.objects.select_related('publisher')[0].publisher.name)
prefetch_related
這個方法相似於select_related
方法,也是用來在查詢語句的時候,提早將指定的數據查找出來,不過這個方法是用來解決我對多,或者多對一的狀況,就是反向查找的時候,這個方法會產生兩個查詢語句。因此在這個方法中查詢使用外鍵關聯的模型的時候,也會產生兩個查詢語句,所以若是查詢的是外鍵關聯的模型,建議使用select_related
,在查詢多對多或者多對一的關聯的對象的時候,你在使用模型怎麼訪問這個多對多,那麼就在這個方法中傳遞什麼字符串,好比要獲取圖書上的全部訂單,那麼示例以下
books = Book.objects.prefetch_related('bookorder_set')
須要注意的是:在使用prefetch_related
查找出來的bookorder_set
,建議不要再對他進行任何操做,好比:filter
,否則雙會產生n多的查詢語句,好比以下代碼是不對的
books = Book.objects.prefetch_related("bookorder_set") for book in books: print('='*30) print(book.name) # 在這個地方若是對bookorder_set進行了操做,那麼就又會產生新的sql語句,前面的prefetch_related就至關於白作了 orders = book.bookorder_set.all() for order in orders: print(order.id)
那麼若是確實是想要對預先查找的集合進行操做,那麼咱們能夠使用django.db.models.Prefetch
來完成。
from django.db.models import Prefetch prefetch = Prefetch("bookorder_set",queryset=BookOrder.objects.filter(price__gte=90)) books = Book.objects.prefetch_related(prefetch) # 先使用prefetch把查找的條件寫好,再放到prefetch_related for book in books: print('='*30) print(book.name) orders = book.bookorder_set.all() for order in orders: print(order.id)
defer和only
這兩個方法都會返回一個QuerySet
對象,而且這個QuerySet
中裝的都是模型,而不是字典
一、defer
:這個方法用來告訴ORM
,在查詢某個模型的時候,過濾掉某些字段。若是使用了過濾掉的字段,會從新發起一次請求,所以要謹慎。
二、only
這個方法用來告訴ORM
在查詢 某個模型的時候,只提取某幾個字段 。注意,沒有加在only
中的字段,之後若是使用了,那麼也會從新發起一次請求。所以要謹慎。
如下的方法不會返回QuerySets,可是做用很是強大,尤爲是粗體顯示的方法,須要背下來。
方法名 | 解釋 |
---|---|
get() | 獲取單個對象 |
create() | 建立對象,無需save() |
get_or_create() | 查詢對象,若是沒有找到就新建對象 |
update_or_create() | 更新對象,若是沒有找到就建立對象 |
bulk_create() |
批量建立對象 |
count() | 統計對象的個數 |
in_bulk() |
根據主鍵值的列表,批量返回對象 |
iterator() |
獲取包含對象的迭代器 |
latest() | 獲取最近的對象 |
earliest() | 獲取最先的對象 |
first() | 獲取第一個對象 |
last() | 獲取最後一個對象 |
aggregate() | 聚合操做 |
exists() | 判斷queryset中是否有對象 |
update() | 批量更新對象 |
delete() | 批量刪除對象 |
as_manager() | 獲取管理器 |
get方法
這個方法給定的條件,只能匹配到一條數據,若是匹配到多條數據,會報錯,若是沒有匹配到數據也會報錯。
create 方法
建立一條數據 ,而且將這個數據保存到數據庫中
publisher = Publisher(name='出版社') publisher.save() # 下面的這行代碼等價於上面2條 Publisher.objects.create(name='出版社')
get_or_create
若是給定的條件有數據,那麼就會把這個數據直接提取出來,若是給定的條件沒有數據,那麼就會先建立數據,而後再把數據返回回來。
bulk_create
一次性建立多個數據,示例以下:
Tag.objects.bulk_create([ Tag(name='111'), Tag(name='222'), ])
count
獲取提取的數據的個數。若是想要知道總共有多少條數據,那麼建議使用count
,而不是使用len(articles)
這種。由於count
在底層是使用select count(*)
來實現的,這種方式比使用len
函數更加的高效。
first和last
返回QuerySet
中的第一條和最後一條數據。
**aggregate **
使用聚合函數。
exists
判斷某個條件的數據是否存在。若是要判斷某個條件的元素是否存在,那麼建議使用exists
,這比使用count
或者直接判斷QuerySet
更有效得多。示例代碼以下:
if Article.objects.filter(title__contains='hello').exists(): print(True) 比使用count更高效: if Article.objects.filter(title__contains='hello').count() > 0: print(True) 也比直接判斷QuerySet更高效: if Article.objects.filter(title__contains='hello'): print(True)
distinct
去除掉那些重複的數據。這個方法若是底層數據庫用的是MySQL
,那麼不能傳遞任何的參數。好比想要提取全部銷售的價格超過80元的圖書,而且刪掉那些重複的,那麼能夠使用distinct
來幫咱們實現,示例代碼以下
books = Book.objects.filter(bookorder__price__gte=80).distinct()
須要注意的是,若是在distinct
以前使用了order_by
,那麼由於order_by
會提取order_by
中指定的字段,所以再使用distinct
就會根據多個字段來進行惟一化,因此就不會把那些重複的數據刪掉。示例代碼以下:
orders = BookOrder.objects.order_by("create_time").values("book_id").distinct()
那麼以上代碼由於使用了order_by
,即便使用了distinct
,也會把重複的book_id
提取出來。
update
一次性能夠把全部的數據都更新完
delete
一次性能夠把全部知足條件的數據都刪除掉,可是須要注意,on_delete指定的處理方式。
熊掌狀況下,咱們須要本身手動在HTML頁面中,編寫form標籤和基內的其它元素,比較費力,數據驗證也比較麻煩,django
在內部集成了一個表單模塊,專門幫助咱們快速處理表單相關的內容,Django
的表單模塊給咱們提供了下面三個主要功能
* 前端頁面的渲染,爲數據建立HTML表單元素
* 接收和處理用戶從表單發送過來的數據的驗證 * 保留上次輸入內容
首先,在你當前app內新建一個forms.py
文件(這個套路是Django的慣用手法,就像views.py
,models.py
等等),而後輸入下面的內容:
# forms.py class MessageBoardForm(forms.Form): title = forms.CharField(max_length=3,label='標題',min_length=2,error_messages={"min_length":'標題字符段不符合要求!'}) content = forms.CharField(widget=forms.Textarea,label='內容') email = forms.EmailField(label='郵箱') reply = forms.BooleanField(required=False,label='回覆')
要點:
<form>
元素中的表單元素。這一點和Django模型系統的設計很是類似。max_length
限制最大長度爲100。它同時起到兩個做用,一是在瀏覽器頁面限制用戶輸入不可超過100個字符,二是在後端服務器驗證用戶輸入的長度不可超過100。(警告:因爲瀏覽器頁面是能夠被篡改、僞造、禁用、跳過的,全部的HTML手段的數據驗證只能防止意外不能防止惡意行爲,是沒有安全保證的,破壞分子徹底能夠跳過瀏覽器的防護手段僞造發送請求!因此,在服務器後端,必須將前端當作「裸機」來對待,再次進行徹底完全的數據驗證和安全防禦!)
每一個Django表單的實例都有一個內置的is_valid()
方法,用來驗證接收的數據是否合法。若是全部數據都合法,那麼該方法將返回True,並將全部的表單數據轉存到它的一個叫作cleaned_data
的屬性中,該屬性是以個字典類型數據。
# views.py class IndexView(View): def get(self,request): form = MessageBoardForm() return render(request,'index.html',{'form':form}) def post(self,request): form = MessageBoardForm(request.POST) if form.is_valid(): title = form.cleaned_data.get('title') content = form.cleaned_data.get('content') email = form.cleaned_data.get('email') reply = form.cleaned_data.get('reply') return HttpResponse('success') else: print(form.errors) return HttpResponse('fail')
要點是:
經過表單的is_bound
屬性能夠獲知一個表單已經綁定了數據,仍是一個空表。
is_valid()
方法和is_bound
屬性的區別:
is_bound
當form
對象中有數據時能正確判斷數據是有的、而這個有與數據是否能經過校驗無關。
is_valid()
方法是看提交的數據能不能經過驗證。
{{ form.as_table }}
將表單渲染成一個表格元素,每一個輸入框做爲一個<tr>
標籤# html
<table> <form action="" method="post"> {{ form.as_table }} <tr> <td></td> <td><input type="submit" value="提交"></td> </tr> </form> </table>
{{ form.as_p }}
將表單的每一個輸入框包裹在一個<p>
標籤內 tags<div> <form action="" method="post"> {{ form.as_p }} <p><input type="submit" value="提交"></p> </form> </div>
{{ form.as_ul }}
將表單渲染成一個列表元素,每一個輸入框做爲一個<li>
標籤<form action="" method="post"> <ul> {{ form.as_ul }} <li><input type="submit" value="提交"></li> </ul> </form>
注意:你要本身手動編寫<table>
和<ul>
標籤。Django自動爲每一個input元素設置了一個id名稱,對應label的for參數
直接{{ form }}
雖然好,啥都不用操心,可是每每並非你想要的,好比你要使用CSS和JS,好比你要引入Bootstarps框架,這些都須要對錶單內的input元素進行額外控制,那怎麼辦呢?手動渲染字段就能夠了。
能夠經過{{ form.name_of_field }}
獲取每個字段,而後分別渲染,以下例所示:
<form action="" method="post" > {% csrf_token %} {{ form.title.errors }} <label for="{{ form.title.id_for_label }}">{{ form.title.label }}</label> {{ form.title }} {{ form.content.errors }} <label for="{{ form.content }}">{{ form.content.label }}</label> {{ form.content }} {{ form.email.errors }} <label for="{{ form.email.id_for_label }}">{{ form.email.label }}</label> {{ form.email }} {{ form.reply.errors }} <label for="{{ form.reply.id_for_label }}">{{ form.reply.label }}</label> {{ form.reply }} <input type="submit" value="提交"> </form>
其中的label標籤甚至能夠用label_tag()
方法來生成,因而能夠簡寫成下面的樣子
<div class="fieldWrapper"> {{ form.title.errors }} {{ form.title.label_tag }} {{ form.tiele }} </div>
若是你的表單字段有相同格式的HMTL表現,那麼徹底能夠循環生成,沒必要要手動的編寫每一個字段,減小冗餘和重複代碼,只須要使用模板語言中的{% for %}
循環,以下所示:
<form action="" method="post"> <table> {% for foo in form %} <tr> <td></td> <td>{{ foo.errors.0 }}</td> </tr> <tr> <td>{{ foo.label_tag }}</td> <td>{{ foo }}</td> </tr> {% endfor %} </table> <input type="submit" value="提交"> </form>
下表是{{ field }}
中很是有用的屬性,這些都是Django內置的模板語言給咱們提供的方便:
屬性 | 說明 |
---|---|
{{ field.label }} |
字段對應的label信息 |
{{ field.label_tag }} |
自動生成字段的label標籤,注意與{{ field.label }} 的區別。 |
{{ field.id_for_label }} |
自定義字段標籤的id |
{{ field.value }} |
當前字段的值,好比一個Email字段的值someone@example.com |
{{ field.html_name }} |
指定字段生成的input標籤中name屬性的值 |
{{ field.help_text }} |
字段的幫助信息 |
{{ field.errors }} |
包含錯誤信息的元素 |
{{ field.is_hidden }} |
用於判斷當前字段是否爲隱藏的字段,若是是,返回True |
{{ field.field }} |
返回字段的參數列表。例如{{ char_field.field.max_length }} |
不少時候,咱們的表單中會有一些隱藏的不可見的字段,好比honeypot。咱們須要讓它在任什麼時候候都彷彿不存在通常,好比有錯誤的時候,若是你在頁面上顯示了不可見字段的錯誤信息,那麼用戶會很迷惑,這是哪來的呢?因此,一般咱們是不顯示不可見字段的錯誤信息的。
Django提供了兩種獨立的方法,用於循環那些不可見的和可見的字段,hidden_fields()
和visible_fields()
。這裏,咱們能夠稍微修改一下前面的例子:
{# 循環那些不可見的字段 #}
{% for hidden in form.hidden_fields %} {{ hidden }} {% endfor %} {# 循環可見的字段 #} {% for field in form.visible_fields %} <div class="fieldWrapper"> {{ field.errors }} {{ field.label_tag }} {{ field }} </div> {% endfor %}
若是你在本身的HTML文件中,屢次使用同一種表單模板,那麼你徹底能夠把表單模板存成一個獨立的HTML文件,而後在別的HTML文件中經過include模板語法將其包含進來,以下例所示:
# 實際的頁面文件中:
{% include "form_snippet.html" %} ----------------------------------------------------- # 單獨的表單模板文件form_snippet.html: {% for field in form %} <div class="fieldWrapper"> {{ field.errors }} {{ field.label_tag }} {{ field }} </div> {% endfor %}
若是你的頁面同時引用了好幾個不一樣的表單模板,那麼爲了防止衝突,你能夠使用with參數,給每一個表單模板取個別名,以下所示:
{% include "form_snippet.html" with form=comment_form %}
在使用的時候就是:
{% for field in comment_form %}
......
Form.is_bound:
若是你須要區分綁定的表單和未綁定的表單,能夠檢查下表單的is_bound
屬性值:
>>> f = ContactForm() >>> f.is_bound False >>> f = ContactForm({'subject': 'hello'}) >>> f.is_bound True
注意,傳遞一個空的字典將建立一個帶有空數據的綁定的表單:
>>> f = ContactForm({}) >>> f.is_bound True
若是你有一個綁定的Form實例可是想改下數據,或者你想給一個未綁定的Form表單綁定某些數據,你須要建立另一個Form實例。由於,Form實例的數據沒是自讀的,Form實例一旦建立,它的數據將不可變。
在驗證某個字段的時候,能夠傳遞一個validators
參數用來指定驗證器,進一步對數據進行過濾。驗證器有不少,可是不少驗證器咱們其實已經經過這個Field
或者一些參數就能夠指定了。好比EmailValidator
,咱們能夠經過EmailField
來指定,好比MaxValueValidator
,咱們能夠經過max_value
參數來指定。如下是一些經常使用的驗證器:
MaxValueValidator
:驗證最大值。
MinValueValidator
:驗證最小值。
MinLengthValidator
:驗證最小長度。
MaxLengthValidator
:驗證最大長度。
EmailValidator
:驗證是不是郵箱格式。
URLValidator
:驗證是不是URL
格式。
RegexValidator
:若是還須要更加複雜的驗證,那麼咱們能夠經過正則表達式的驗證器:RegexValidator
。好比如今要驗證手機號碼是否合格,那麼咱們能夠經過如下代碼實現:
class MyForm(forms.Form): telephone = forms.CharField(validators=[validators.RegexValidator("1[345678]\d{9}",message='請輸入正確格式的手機號碼!')])
有時候對一個字段驗證,不是一個長度,一個正則表達式可以寫清楚的,還須要一些其餘複雜的邏輯,那麼咱們能夠對某個字段,進行自定義的驗證。好比在註冊的表單驗證中,咱們想要驗證手機號碼是否已經被註冊過了,那麼這時候就須要在數據庫中進行判斷才知道。對某個字段進行自定義的驗證方式是,定義一個方法,這個方法的名字定義規則是:clean_fieldname
。若是驗證失敗,那麼就拋出一個驗證錯誤。好比要驗證用戶表中手機號碼以前是否在數據庫中存在,那麼能夠經過如下代碼實現:
clean_字段名()
的方法,也叫局部鉤子from django import forms from django.forms.utils import ErrorDict from django.core import validators from app01 import models class MyForms(forms.Form): user = forms.CharField(min_length=2, max_length=32) tel = forms.CharField(validators=[validators.RegexValidator(r'1[3456789]\d{9}', message='手機號碼格式不對')]) pw1 = forms.CharField(min_length=2, max_length=12) pw2 = forms.CharField(min_length=2, max_length=12) def clean_tel(self): tel = self.cleaned_data.get('tel') ret = models.UserInfo.objects.filter(tel=tel).exists() if ret: raise forms.ValidationError('手機號碼已經存在') return tel
clean()
的方法,也叫全局鉤子def clean(self): cleaned_data = super().clean() pw1 = cleaned_data.get('pw1') pw2 = cleaned_data.get('pw2') if pw1 != pw2: raise forms.ValidationError('兩次密碼不同') return cleaned_data
import re from django.forms import Form from django.forms import widgets from django.forms import fields from django.core.exceptions import ValidationError # 自定義驗證規則 def mobile_validate(value): mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$') if not mobile_re.match(value): raise ValidationError('手機號碼格式錯誤') #自定義驗證規則的時候,若是不符合你的規則,須要本身發起錯誤 class PublishForm(Form): title = fields.CharField(max_length=20, min_length=5, error_messages={'required': '標題不能爲空', 'min_length': '標題最少爲5個字符', 'max_length': '標題最多爲20個字符'}, widget=widgets.TextInput(attrs={'class': "form-control", 'placeholder': '標題5-20個字符'})) # 使用自定義驗證規則 phone = fields.CharField(validators=[mobile_validate, ], error_messages={'required': '手機不能爲空'}, widget=widgets.TextInput(attrs={'class': "form-control", 'placeholder': u'手機號碼'})) email = fields.EmailField(required=False, error_messages={'required': u'郵箱不能爲空','invalid': u'郵箱格式錯誤'}, widget=widgets.TextInput(attrs={'class': "form-control", 'placeholder': u'郵箱'}))
Form.errors
表單的errors屬性保存了錯誤信息字典
>>> f.errors {'sender': ['Enter a valid email address.'], 'subject': ['This field is required.']}
Form.errors.as_data()
在源碼中的位置from django.forms.utils import ErrorDict
返回一個字典,它將字段映射到原始的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"}]}
Form.add_error(field, error)
向表單特定字段添加錯誤信息。
field參數爲字段的名稱。若是值爲None,error將做爲Form.non_field_errors()
的一個非字段錯誤。
Form.has_error(field, code=None)
判斷某個字段是否具備指定code的錯誤。當code爲None時,若是字段有任何錯誤它都將返回True。
Form.non_field_errors()
返回Form.errors中不是與特定字段相關聯的錯誤。
Form.has_changed()
當你須要檢查表單的數據是否從初始數據發生改變時,能夠使用has_changed()
方法。
>>> data = {'subject': 'hello', ... 'message': 'Hi there', ... 'sender': 'foo@example.com', ... 'cc_myself': True} >>> f = ContactForm(data, initial=data) >>> f.has_changed() False
提交表單後,咱們能夠從新構建表單並提供初始值,進行比較:
>>> f = ContactForm(request.POST, initial=data) >>> f.has_changed()
若是request.POST與initial中的數據有區別,則返回True,不然返回False。
Form.changed_data
返回有變化的字段的列表。
>>> f = ContactForm(request.POST, initial=data) >>> if f.has_changed(): ... print("The following fields changed: %s" % ", ".join(f.changed_data))
經過fileds屬性訪問表單的字段:
>>> for row in f.fields.values(): print(row) ... <django.forms.fields.CharField object at 0x7ffaac632510> <django.forms.fields.URLField object at 0x7ffaac632f90> <django.forms.fields.CharField object at 0x7ffaac3aa050> >>> f.fields['name'] <django.forms.fields.CharField object at 0x7ffaac6324d0>
能夠修改Form實例的字段來改變字段在表單中的表示:
form.fields['title'].label = '琦村伯' print(form.as_table().split('\n')[0]) ## <tr><th><label for="id_title">琦村伯:</label></th><td><input type="text" name="title" maxlength="3" minlength="2" required id="id_title" /></td></tr>
注意不要改變base_fields
屬性,由於一旦修改將影響同一個Python進程中接下來全部的Form實例:
form = MessageBoardForm() # 已經實例了 form.base_fields['title'].label = '你的力量' print(form.as_table().split('\n')[0]) # 已經實例化了,因此無法改變 another_f = MessageBoardForm(auto_id=False) # 可是影響了下一下實例化的對象 print(another_f.as_table().split('\n')[0]) ################ <tr><th><label for="id_title">標題:</label></th><td><input type="text" name="title" maxlength="3" minlength="2" required id="id_title" /></td></tr> <tr><th>你的力量:</th><td><input type="text" name="title" maxlength="3" minlength="2" required /></td></tr>
Form.cleaned_data
Form類中的每一個字段不只負責驗證數據,還負責將它們轉換爲正確的格式。例如,DateField將輸入轉換爲Python的datetime.date對象。不管你傳遞的是普通字符串'1994-07-15'、DateField格式的字符串、datetime.date對象、仍是其它格式的數字,Django將始終把它們轉換成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'}
若是你的數據沒有經過驗證,cleaned_data字典中只包含合法的字段:
>>> data = {'subject': '', # 是個空的字符串 ... 'message': 'Hi there', ... 'sender': 'invalid email address', ... 'cc_myself': True} >>> f = ContactForm(data) >>> f.is_valid() False >>> f.cleaned_data {'cc_myself': True, 'message': 'Hi there'} # 因此cleaned_date字典中就沒有subject的鍵
cleaned_data
字典始終只包含Form中定義的字段或者使用ModelForm時模型中的字段,即便你在構建Form時傳遞了額外的數據。 在下面的例子中,咱們傳遞了一組額外的字段給ContactForm構造函數,可是cleaned_data將只包含表單的字段:
>>> data = {'subject': 'hello', ... 'message': 'Hi there', ... 'sender': 'foo@example.com', ... 'cc_myself': True, ... 'extra_field_1': 'foo', ... 'extra_field_2': 'bar', ... 'extra_field_3': 'baz'} >>> f = ContactForm(data) >>> f.is_valid() True >>> f.cleaned_data # Doesn't contain extra_field_1, etc. {'cc_myself': True, 'message': 'Hi there', 'sender': 'foo@example.com', 'subject': 'hello'}
當Form經過驗證後,cleaned_data
將包含全部字段的鍵和值,即便傳遞的數據中沒有提供某些字段的值。 在下面的例子中,提供的實際數據中不包含nick_name
字段,可是cleaned_data
任然包含它,只是值爲空:
>>> from django import forms >>> class OptionalPersonForm(forms.Form): ... first_name = forms.CharField() ... last_name = forms.CharField() ... nick_name = forms.CharField(required=False) # 字段中有nick_name字段,可是能夠不傳 >>> data = {'first_name': 'John', 'last_name': 'Lennon'} # 在字典中並無傳這個nick_name >>> f = OptionalPersonForm(data) >>> f.is_valid() True >>> f.cleaned_data {'nick_name': '', 'first_name': 'John', 'last_name': 'Lennon'} # 驗證後的數據這個字段爲空
Form.error_css_class 爲錯誤的信息添加類
Form.required_css_class 爲必填的字段添加類
爲一些特別強調的或者須要額外顯示的內容設置醒目的CSS樣式是一種經常使用作法,也是很是有必要的。好比給必填字段加粗顯示,設置錯誤文字爲紅色等等。
Form.error_css_class
和Form.required_css_class
屬性就是作這個用的:
from django import forms class ContactForm(forms.Form): error_css_class = 'error' required_css_class = 'required' # ... and the rest of your fields here
屬性名是固定的,不可變(廢話),經過賦值不一樣的字符串,表示給這兩類屬性添加不一樣的CSS的class屬性。之後Django在渲染form成HTML時將自動爲error和required行添加對應的CSS樣式。
處理帶有FileField和ImageField字段的表單比普通的表單要稍微複雜一點。
首先,爲了上傳文件,你須要確保你的<form>
元素定義enctype
爲"multipart/form-data":
<form enctype="multipart/form-data" method="post" action="/foo/">
其次,當你使用表單時,你須要綁定文件數據。文件數據的處理與普通的表單數據是分開的,因此若是表單包含FileField和ImageField,綁定表單時你須要指定第二個參數,參考下面的例子。
# 爲表單綁定image字段 >>> from django.core.files.uploadedfile import SimpleUploadedFile >>> data = {'subject': 'hello', ... 'message': 'Hi there', ... 'sender': 'foo@example.com', ... 'cc_myself': True} >>> file_data = {'mugshot': SimpleUploadedFile('face.jpg', <file data>)} >>> f = ContactFormWithMugshot(data, file_data)
實際上,通常使用request.FILES
做爲文件數據的源:
# Bound form with an image field, data from the request >>> f = ContactFormWithMugshot(request.POST, request.FILES)
構造一個未綁定的表單和往常同樣,將表單數據和文件數據同時省略:
# Unbound form with an image field >>> f = ContactFormWithMugshot()
Field.clean(value)[source]
雖然表單字段的Field類主要使用在Form類中,但也能夠直接實例化它們來使用,以便更好地瞭解它們是如何工做的。每一個Field的實例都有一個clean()方法,它接受一個參數,而後返回「清潔的」數據或者拋出一個django.forms.ValidationError
異常:
>>> 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.']
這個clean方法常常被咱們用來在開發或測試過程當中對數據進行驗證和測試。
建立Form類時,主要涉及到 【字段】 和 【插件】,字段用於對用戶請求數據的驗證,插件用於自動生成HTML;
初始值,input框裏面的初始值。
class LoginForm(forms.Form): username = forms.CharField( min_length=8, label="用戶名", initial="張三" # 設置默認值 ) pwd = forms.CharField(min_length=6, label="密碼")
error_messages
重寫錯誤信息
class LoginForm(forms.Form): username = forms.CharField( min_length=8, label="用戶名", initial="張三", error_messages={ "required": "不能爲空", "invalid": "格式錯誤", "min_length": "用戶名最短8位" } ) pwd = forms.CharField(min_length=6, label="密碼")
password
class LoginForm(forms.Form): ... pwd = forms.CharField( min_length=6, label="密碼", widget=forms.widgets.PasswordInput(attrs={'class': 'c1'}, render_value=True) #這個密碼字段和其餘字段不同,默認在前端輸入數據錯誤的時候,點擊提交以後,默認是不保存的原來數據的,可是能夠經過這個render_value=True讓這個字段在前端保留用戶輸入的數據 )
radioSelect
單radio值爲字符串
class LoginForm(forms.Form): username = forms.CharField( #其餘選擇框或者輸入框,基本都是在這個CharField的基礎上經過插件來搞的 min_length=8, label="用戶名", initial="張三", error_messages={ "required": "不能爲空", "invalid": "格式錯誤", "min_length": "用戶名最短8位" } ) pwd = forms.CharField(min_length=6, label="密碼") gender = forms.fields.ChoiceField( choices=((1, "男"), (2, "女"), (3, "保密")), label="性別", initial=3, widget=forms.widgets.RadioSelect() )
單選Select
class LoginForm(forms.Form): ... hobby = forms.fields.ChoiceField( #注意,單選框用的是ChoiceField,而且裏面的插件是Select,否則驗證的時候會報錯, Select a valid choice的錯誤。 choices=((1, "籃球"), (2, "足球"), (3, "雙色球"), ), label="愛好", initial=3, widget=forms.widgets.Select() )
多選Select
class LoginForm(forms.Form): ... hobby = forms.fields.MultipleChoiceField( #多選框的時候用MultipleChoiceField,而且裏面的插件用的是SelectMultiple,否則驗證的時候會報錯。 choices=((1, "籃球"), (2, "足球"), (3, "雙色球"), ), label="愛好", initial=[1, 3], widget=forms.widgets.SelectMultiple() )
單選checkbox
class LoginForm(forms.Form): ... keep = forms.fields.ChoiceField( label="是否記住密碼", initial="checked", widget=forms.widgets.CheckboxInput() )
單選checkbox示例:
#單選的checkbox class TestForm2(forms.Form): keep = forms.ChoiceField( choices=( ('True',1), ('False',0), ), label="是否7天內自動登陸", initial="1", widget=forms.widgets.CheckboxInput(), ) 選中:'True' #form只是幫咱們作校驗,校驗選擇內容的時候,就是看在沒在咱們的choices裏面,裏面有這個值,表示合法,沒有就不合法 沒選中:'False' ---保存到數據庫裏面 keep:'True' if keep == 'True': session 設置有效期7天 else: pass
多選checkbox
class LoginForm(forms.Form): ... hobby = forms.fields.MultipleChoiceField( choices=((1, "籃球"), (2, "足球"), (3, "雙色球"),), label="愛好", initial=[1, 3], widget=forms.widgets.CheckboxSelectMultiple() )
date類型
from django import forms from django.forms import widgets class BookForm(forms.Form): date = forms.DateField(widget=widgets.TextInput(attrs={'type':'date'})) #必須指定type,否則不能渲染成選擇時間的input框
choice字段注意事項
在使用選擇標籤時,須要注意choices的選項能夠配置從數據庫中獲取,可是因爲是靜態字段 獲取的值沒法實時更新,須要重寫構造方法從而實現choice實時更新。
方式一:
from django.forms import Form from django.forms import widgets from django.forms import fields class MyForm(Form): user = fields.ChoiceField( # choices=((1, '上海'), (2, '北京'),), initial=2, widget=widgets.Select ) def __init__(self, *args, **kwargs): super(MyForm,self).__init__(*args, **kwargs) #注意重寫init方法的時候,*args和**kwargs必定要給人家寫上,否則會出問題,而且驗證老是不能經過,還不顯示報錯信息 # self.fields['user'].choices = ((1, '上海'), (2, '北京'),) # 或 self.fields['user'].choices = models.Classes.objects.all().values_list('id','caption')
方式二:
from django import forms from django.forms import fields from django.forms import models as form_model class FInfo(forms.Form): authors = forms.ModelMultipleChoiceField(queryset=models.NNewType.objects.all()) # 多選 #或者下面這種方式,經過forms裏面的models中提供的方法也是同樣的。 authors = form_model.ModelMultipleChoiceField(queryset=models.NNewType.objects.all()) # 多選 #authors = form_model.ModelChoiceField(queryset=models.NNewType.objects.all()) # 單選 #或者,forms.ModelChoiceField(queryset=models.Publisth.objects.all(),widget=forms.widgets.Select()) 單選 # authors = forms.ModelMultipleChoiceField( queryset=models.Author.objects.all(), widget = forms.widgets.Select(attrs={'class': 'form-control'} )) #若是用這種方式,別忘了model表中,NNEWType的__str__方法要寫上,否則選擇框裏面是一個個的object對象
label
label參數用來給字段添加‘人類友好’的提示信息。若是沒有設置這個參數,那麼就用字段的首字母大寫名字。好比:
下面的例子,前兩個字段有,最後的comment沒有label參數:
>>> 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
Django默認爲上面的label參數後面加個冒號後綴,若是想自定義,能夠使用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>
help_text
該參數用於設置字段的輔助描述文本。
>>> 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