Models
數據庫的配置
1 django默認支持sqlite,mysql, oracle,postgresql數據庫。
<1> sqlite
django默認使用sqlite的數據庫,默認自帶sqlite的數據庫驅動
引擎名稱:django.db.backends.sqlite3
<2> mysql
引擎名稱:django.db.backends.mysql
2 mysql驅動程序
MySQLdb(mysql python)
mysqlclient
MySQL
PyMySQL(純python的mysql驅動程序)
3 在django的項目中會默認使用sqlite數據庫,在settings里有如下設置:
如果我們想要更改數據庫,需要修改如下:

DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME':'django_com', 'USER':'root', 'PASSWORD':'', } }
注意:NAME即數據庫的名字,在mysql連接前該數據庫必須已經創建,而上面的sqlite數據庫下的db.sqlite3則是項目自動創建
USER和PASSWORD分別是數據庫的用戶名和密碼。
設置完后,再啟動我們的Django項目前,我們需要激活我們的mysql。
然后,啟動項目,會報錯:no module named MySQLdb
這是因為django默認你導入的驅動是MySQLdb,可是MySQLdb對於py3有很大問題,所以我們需要的驅動是PyMySQL
所以,我們只需要找到項目名文件下的__init__,在里面寫入:
import pymysql pymysql.install_as_MySQLdb()
問題解決!
ORM(對象關系映射)
用於實現面向對象編程語言里不同類型系統的數據之間的轉換,換言之,就是用面向對象的方式去操作數據庫的創建表以及增刪改查等操作。
優點: 1 ORM使得我們的通用數據庫交互變得簡單易行,而且完全不用考慮該死的SQL語句。快速開發,由此而來。
2 可以避免一些新手程序猿寫sql語句帶來的性能問題。
比如 我們查詢User表中的所有字段:
新手可能會用select * from auth_user,這樣會因為多了一個匹配動作而影響效率的。
缺點:1 性能有所犧牲,不過現在的各種ORM框架都在嘗試各種方法,比如緩存,延遲加載登來減輕這個問題。效果很顯著。
2 對於個別復雜查詢,ORM仍然力不從心,為了解決這個問題,ORM一般也支持寫raw sql。
下面要開始學習Django ORM語法了,為了更好的理解,我們來做一個基本的 書籍/作者/出版商 數據庫結構。 我們這樣做是因為 這是一個眾所周知的例子,很多SQL有關的書籍也常用這個舉例。
表(模型)的創建:
實例:我們來假定下面這些概念,字段和關系
作者模型:一個作者有姓名。
作者詳細模型:把作者的詳情放到詳情表,包含性別,email地址和出生日期,作者詳情模型和作者模型之間是一對一的關系(one-to-one)(類似於每個人和他的身份證之間的關系),在大多數情況下我們沒有必要將他們拆分成兩張表,這里只是引出一對一的概念。
出版商模型:出版商有名稱,地址,所在城市,省,國家和網站。
書籍模型:書籍有書名和出版日期,一本書可能會有多個作者,一個作者也可以寫多本書,所以作者和書籍的關系就是多對多的關聯關系(many-to-many),一本書只應該由一個出版商出版,所以出版商和書籍是一對多關聯關系(one-to-many),也被稱作外鍵。
from django.db import models
class Publisher(models.Model): name = models.CharField(max_length=30, verbose_name="名稱") address = models.CharField("地址", max_length=50) city = models.CharField('城市',max_length=60) state_province = models.CharField(max_length=30) country = models.CharField(max_length=50) website = models.URLField() class Meta: verbose_name = '出版商' verbose_name_plural = verbose_name def __str__(self): return self.name class Author(models.Model): name = models.CharField(max_length=30) def __str__(self): return self.name class AuthorDetail(models.Model): sex = models.BooleanField(max_length=1, choices=((0, '男'),(1, '女'),)) email = models.EmailField() address = models.CharField(max_length=50) birthday = models.DateField() author = models.OneToOneField(Author) class Book(models.Model): title = models.CharField(max_length=100) authors = models.ManyToManyField(Author) publisher = models.ForeignKey(Publisher) publication_date = models.DateField() price=models.DecimalField(max_digits=5,decimal_places=2,default=10) def __str__(self): return self.title
注意:記得在settings里的INSTALLED_APPS中加入'app01',然后再同步數據庫。
分析代碼:
1 每個數據模型都是django.db.models.Model的子類,它的父類Model包含了所有必要的和數據庫交互的方法。並提供了一個簡介漂亮的定義數據庫字段的語法。
2 每個模型相當於單個數據庫表(多對多關系例外,會多生成一張關系表),每個屬性也是這個表中的字段。屬性名就是字段名,它的類型(例如CharField)相當於數據庫的字段類型(例如varchar)。大家可以留意下其它的類型都和數據庫里的什么字段對應。
3 模型之間的三種關系:一對一,一對多,多對多。
一對一:實質就是在主外鍵(author_id就是foreign key)的關系基礎上,給外鍵加了一個UNIQUE=True的屬性;
一對多:就是主外鍵關系;
多對多:彼此一對多,自動創建第三張表
4 模型常用的字段類型以及參數

# AutoField # 一個 IntegerField, 添加記錄時它會自動增長. 你通常不需要直接使用這個字段; 如果你不指定主鍵的話,系統會自動添加一個主鍵字段到你的 model.(參閱 _自動主鍵字段) # BooleanField # A true/false field. admin 用 checkbox 來表示此類字段. # CharField # 字符串字段, 用於較短的字符串. # # 如果要保存大量文本, 使用 TextField. # # admin 用一個 <input type="text"> 來表示此類字段 (單行輸入). # # CharField 要求必須有一個參數 maxlength, 用於從數據庫層和Django校驗層限制該字段所允許的最大字符數. # # CommaSeparatedIntegerField # 用於存放逗號分隔的整數值. 類似 CharField, 必須要有 maxlength 參數. # DateField # 一個日期字段. 共有下列額外的可選參數: # # Argument 描述 # auto_now 當對象被保存時,自動將該字段的值設置為當前時間.通常用於表示 "last-modified" 時間戳. # auto_now_add 當對象首次被創建時,自動將該字段的值設置為當前時間.通常用於表示對象創建時間. # admin 用一個文本框 <input type="text"> 來表示該字段數據(附帶一個 JavaScript 日歷和一個"Today"快鍵. # # DateTimeField # 一個日期時間字段. 類似 DateField 支持同樣的附加選項. # admin 用兩上文本框 <input type="text"> 表示該字段順序(附帶JavaScript shortcuts). # # EmailField # 一個帶有檢查 Email 合法性的 CharField,不接受 maxlength 參數. # FileField # 一個文件上傳字段. # # 要求一個必須有的參數: upload_to, 一個用於保存上載文件的本地文件系統路徑. 這個路徑必須包含 strftime formatting, 該格式將被上載文件的 date/time 替換(so that uploaded files don't fill up the given directory). # # admin 用一個``<input type="file">``部件表示該字段保存的數據(一個文件上傳部件) . # # 在一個 model 中使用 FileField 或 ImageField 需要以下步驟: # # 在你的 settings 文件中, 定義一個完整路徑給 MEDIA_ROOT 以便讓 Django在此處保存上傳文件. (出於性能考慮,這些文件並不保存到數據庫.) 定義 MEDIA_URL 作為該目錄的公共 URL. 要確保該目錄對 WEB 服務器用戶帳號是可寫的. # 在你的 model 中添加 FileField 或 ImageField, 並確保定義了 upload_to 選項,以告訴 Django 使用 MEDIA_ROOT 的哪個子目錄保存上傳文件. # 你的數據庫中要保存的只是文件的路徑(相對於 MEDIA_ROOT). 出於習慣你一定很想使用 Django 提供的 get_<fieldname>_url 函數.舉例來說,如果你的 ImageField 叫作 mug_shot, 你就可以在模板中以 {{ object.get_mug_shot_url }} 這樣的方式得到圖像的絕對路徑. # FilePathField # 可選項目為某個特定目錄下的文件名. 支持三個特殊的參數, 其中第一個是必須提供的. # # 參數 描述 # path 必需參數. 一個目錄的絕對文件系統路徑. FilePathField 據此得到可選項目. Example: "/home/images". # match 可選參數. 一個正則表達式, 作為一個字符串, FilePathField 將使用它過濾文件名. 注意這個正則表達式只會應用到 base filename 而不是路徑全名. Example: "foo.*\.txt^", 將匹配文件 foo23.txt 卻不匹配 bar.txt 或 foo23.gif. # recursive 可選參數.要么 True 要么 False. 默認值是 False. 是否包括 path 下面的全部子目錄. # 這三個參數可以同時使用. # # 我已經告訴過你 match 僅應用於 base filename, 而不是路徑全名. 那么,這個例子: # # FilePathField(path="/home/images", match="foo.*", recursive=True) # ...會匹配 /home/images/foo.gif 而不匹配 /home/images/foo/bar.gif # # FloatField # 一個浮點數. 必須 提供兩個 參數: # # 參數 描述 # max_digits 總位數(不包括小數點和符號) # decimal_places 小數位數 # 舉例來說, 要保存最大值為 999 (小數點后保存2位),你要這樣定義字段: # # models.FloatField(..., max_digits=5, decimal_places=2) # 要保存最大值一百萬(小數點后保存10位)的話,你要這樣定義: # # models.FloatField(..., max_digits=19, decimal_places=10) # admin 用一個文本框(<input type="text">)表示該字段保存的數據. # # ImageField # 類似 FileField, 不過要校驗上傳對象是否是一個合法圖片.它有兩個可選參數:height_field 和 width_field,如果提供這兩個參數,則圖片將按提供的高度和寬度規格保存. # # 該字段要求 Python Imaging Library. # # IntegerField # 用於保存一個整數. # # admin 用一個``<input type="text">``表示該字段保存的數據(一個單行編輯框) # # IPAddressField # 一個字符串形式的 IP 地址, (i.e. "24.124.1.30"). # # admin 用一個``<input type="text">``表示該字段保存的數據(一個單行編輯框) # # NullBooleanField # 類似 BooleanField, 不過允許 NULL 作為其中一個選項. 推薦使用這個字段而不要用 BooleanField 加 null=True 選項. # # admin 用一個選擇框 <select> (三個可選擇的值: "Unknown", "Yes" 和 "No" ) 來表示這種字段數據. # # PhoneNumberField # 一個帶有合法美國風格電話號碼校驗的 CharField``(格式: ``XXX-XXX-XXXX). # PositiveIntegerField # 類似 IntegerField, 但取值范圍為非負整數(這個字段應該是允許0值的....所以字段名字取得不太好,無符號整數就對了嘛). # PositiveSmallIntegerField # 類似 PositiveIntegerField, 取值范圍較小(數據庫相關) # SlugField # "Slug" 是一個報紙術語. slug 是某個東西的小小標記(短簽), 只包含字母,數字,下划線和連字符.它們通常用於URLs. # # 若你使用 Django 開發版本,你可以指定 maxlength. 若 maxlength 未指定, Django 會使用默認長度: 50. 在以前的 Django 版本,沒有任何辦法改變 50 這個長度. # # 這暗示了 db_index=True. # # 它接受一個額外的參數: prepopulate_from, which is a list of fields from which to auto-populate the slug, via JavaScript, in the object's admin form: # # models.SlugField(prepopulate_from=("pre_name", "name")) # prepopulate_from 不接受 DateTimeFields. # # admin 用一個``<input type="text">``表示 SlugField 字段數據(一個單行編輯框) # # SmallIntegerField # 類似 IntegerField, 不過只允許某個取值范圍內的整數.(依賴數據庫) # # TextField # 一個容量很大的文本字段. # # admin 用一個 <textarea> (文本區域)表示該字段數據.(一個多行編輯框). # # TimeField # A time. Accepts the same auto-population options as DateField 和 DateTimeField. # # admin 用一個 <input type="text"> 文本框表示該字段保存的數據(附加一些JavaScript shortcuts). # # URLField # 用於保存 URL. 若 verify_exists 參數為 True (默認), 給定的 URL 會預先檢查是否存在(即URL是否被有效裝入且沒有返回404響應). # # admin 用一個 <input type="text"> 文本框表示該字段保存的數據(一個單行編輯框) # # USStateField # 一個兩字母的美國州名縮寫. # # admin 用一個 <input type="text"> 文本框表示該字段保存的數據(一個單行編輯框) # # XMLField # 一個校驗值是否為合法XML的 TextField,必須提供參數: schema_path, 它是一個用來校驗文本的 RelaxNG schema 的文件系統路徑.
表的操作(增刪改查):
1 增(create save)

>>> from app01.models import * >>> Author.objects.create(name='Alvin') <Author: Alvin> >>> AuthorDetail.objects.create(sex=False,email='916852314@qq.com',address='bejing',birthday='1995-3-16',author_id=1) <AuthorDetail: AuthorDetail object> >>> pub=Publisher() >>> pub.name='河大出版社' >>> pub.address='保定' >>> pub.city='保定' >>> pub.state_province='河北' >>> pub.country='China' >>> pub.website='http://www.beida.com' >>> pub.save()
注意:如果每次創建一個對象,想顯示對應的raw 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', # }, # } # }
那么如何創建存在一對多或多對多關系的一本書的信息呢?

>>> Book.objects.create(title='php',publisher=pub,publication_date='2017-7-7') <Book: php> >>> author1=Author.objects.get(id=1) >>> author2=Author.objects.get(name='alvin') >>> book=Book.objects.get(id=1) >>> book.authors.add(author1,author1) >>> book.authors.add(author1,author2)
2 改:
update和save方法
實例:
注意:<1> 不能用get的原因是:update是QuerySet對象的方法,get返回的是一個model對象,它沒有update方法,而filter返回的是一個QuerySet對象(filter里面的條件可能有多個條件符合,比如name='alvin',可能有兩個name='alvin'的行數據)。
<2> filter:
>>> Publisher.objects.filter(name__contains="press") [ <Publisher: Apress>]
在 name 和 contains 之間有雙下划線。和Python一樣,Django也使用雙下划線來表明會進行一些魔術般的操作。這里,contains部分會被Django翻譯成LIKE語句:
SELECT id, name, address, city, state_province, country, website FROM books_publisher WHERE name LIKE '%press%';
其他的一些查找類型有:icontains(大小寫不敏感的LIKE),startswith和endswith, 還有range
<3> 在“插入和更新數據”小節中,我們有提到模型的save()方法,這個方法會更新一行里的所有列。 而某些情況下,我們只需要更新行里的某幾列。例如說我們現在想要將Apress Publisher的名稱由原來的”Apress”更改為”Apress Publishing”。若使用save()方法,如:
>>> p = Publisher.objects.get(name='Apress') >>> p.name = 'Apress Publishing' >>> p.save()
這等同於如下SQL語句:
SELECT id, name, address, city, state_province, country, website FROM books_publisher WHERE name = 'Apress'; UPDATE books_publisher SET name = 'Apress Publishing', address = '2855 Telegraph Ave.', city = 'Berkeley', state_province = 'CA', country = 'U.S.A.', website = 'http://www.apress.com' WHERE id = 52;
注意在這里我們假設Apress的ID為52)
在這個例子里我們可以看到Django的save()方法更新了不僅僅是name列的值,還有更新了所有的列。 若name以外的列有可能會被其他的進程所改動的情況下,只更改name列顯然是更加明智的。 更改某一指定的列,我們可以調用結果集(QuerySet)對象的update()方法: 示例如下:
>>> Publisher.objects.filter(id=52).update(name='Apress Publishing')
與之等同的SQL語句變得更高效,並且不會引起競態條件。
UPDATE books_publisher SET name = 'Apress Publishing' WHERE id = 52;
update()方法對於任何結果集(QuerySet)均有效,這意味着你可以同時更新多條記錄。 以下示例演示如何將所有Publisher的country字段值由’U.S.A’更改為’USA’:
>>> Publisher.objects.all().update(country='USA') 2
update()方法會返回一個整型數值,表示受影響的記錄條數。 在上面的例子中,這個值是2。
多對多關系的構建
我們當然可以直接在第三張表里插入數據來構建,那數據庫如果沒有第三張表還有沒有別的辦法呢?

# db: 兩本書Python和Go 兩個作者;alex alvin ret=models.Book.objects.get(id=1) obj_authors=models.Author.objects.all() ret.authors.add(*obj_authors)#ret.authors.add(*[1,2,3]) ret.authors.remove(1) # 反過來呢? ret=models.Author.objects.get(id=2) obj_book=models.Book.objects.get(id=1) ret.book_set.add(obj_book)
3 查
>>> Publisher.objects.all()
[<Publisher: 中國機械出版社>, <Publisher: American publisher>]
注意:
這相當於這個SQL語句:
SELECT id, name, address, city, state_province, country, website FROM books_publisher;
注意到Django在選擇所有數據時並沒有使用 SELECT* ,而是顯式列出了所有字段。
惰性機制:
所謂惰性機制:Publisher.objects.all()只是返回了一個QuerySet(查詢結果集對象),並不會馬上執行sql,而是當調用QuerySet的時候才執行。
QuerySet特點:
1 可迭代的
2 可切片
3.1 查詢API

查詢相關API: # # <1>get(**kwargs): 返回與所給篩選條件相匹配的對象,返回結果有且只有一個,如果符合篩選條件的對象超過一個或者沒有都會拋出錯誤。 # # <2>all(): 查詢所有結果 # # <3>filter(**kwargs): 它包含了與所給篩選條件相匹配的對象 # # <4>exclude(**kwargs): 它包含了與所給篩選條件不匹配的對象 # # <5>order_by(*field): 對查詢結果排序 # # <6>reverse(): 對查詢結果反向排序 # # <7>distinct(): 從返回結果中剔除重復紀錄 # # <8>values(*field): 返回一個ValueQuerySet——一個特殊的QuerySet,運行后得到的並不是一 # # 系列 model的實例化對象,而是一個可迭代的字典序列 # # <9>values_list(*field): 它與values()非常相似,它返回的是一個元組序列,values返回的是一個字典序列 # # <10>count(): 返回數據庫中匹配查詢(QuerySet)的對象數量。 # # <11>first(): 返回第一條記錄,等價於[:1][0] # # <12>last(): 返回最后一條記錄,等價於[::1][0] # # <13>exists(): 如果QuerySet包含數據,就返回True,否則返回False。
實例測試:
1 查詢id為2的書籍信息,並只顯示書籍名稱和出版日期

>>> Book.objects.filter(id=2).values("title","publication_date") [{'title': 'python Gone', 'publication_date': datetime.date(2019, 5, 24)}]
2 查詢所有的出版信息,並按id降序排列,並嘗試使用reverse方法進行反向排序。

>>> Publisher.objects.all().order_by("id") [<Publisher: 中國機械出版社>, <Publisher: Boo>, <Publisher: 人民出版社>] >>> Publisher.objects.all().order_by("id").reverse() [<Publisher: 人民出版社>, <Publisher: Boo>, <Publisher: 中國機械出版社>] >>> Publisher.objects.all().order_by("-id") [<Publisher: 人民出版社>, <Publisher: Boo>, <Publisher: 中國機械出版社>]
3 查詢出版社所在的城市信息,城市信息不要重復

>>> Publisher.objects.all().values("city").distinct() [{'city': '北京'}, {'city': 'beijing'}]
4 查詢城市是北京的出版社,嘗試使用exclude方法

>>> Publisher.objects.all().filter(city='beijing') [<Publisher: Boo>]
5 查詢男作者的數量

>>> AuthorDetail.objects.filter(sex=0).count()
1
總結:

# 獲取個數 # # models.Tb1.objects.filter(name='seven').count() # 大於,小於 # # models.Tb1.objects.filter(id__gt=1) # 獲取id大於1的值 # models.Tb1.objects.filter(id__lt=10) # 獲取id小於10的值 # models.Tb1.objects.filter(id__lt=10, id__gt=1) # 獲取id大於1 且 小於10的值 # in # # models.Tb1.objects.filter(id__in=[11, 22, 33]) # 獲取id等於11、22、33的數據 # models.Tb1.objects.exclude(id__in=[11, 22, 33]) # not in # contains # # models.Tb1.objects.filter(name__contains="ven") # models.Tb1.objects.filter(name__icontains="ven") # icontains大小寫不敏感 # models.Tb1.objects.exclude(name__icontains="ven") # range # # models.Tb1.objects.filter(id__range=[1, 2]) # 范圍bettwen and # 其他類似 # # startswith,istartswith, endswith, iendswith, # order by # # models.Tb1.objects.filter(name='seven').order_by('id') # asc # models.Tb1.objects.filter(name='seven').order_by('-id') # desc # limit 、offset # # models.Tb1.objects.all()[10:20] # group by from django.db.models import Count, Min, Max, Sum # models.Tb1.objects.filter(c1=1).values('id').annotate(c=Count('num')) # SELECT "app01_tb1"."id", COUNT("app01_tb1"."num") AS "c" FROM "app01_tb1" WHERE "app01_tb1"."c1" = 1 GROUP BY "app01_tb1"."id"
3.2 多表關聯查詢(******)

from django.shortcuts import render,HttpResponse # Create your views here. from app01 import models def hello(req): #db存儲:Python和Go兩本書同時綁定了人大一家出版社 # id=1的書即Python有兩個作者:alex,alvin # id=2的書即Go有一個作者: alex ret=models.Book.objects.all() print(ret)#[<Book: Book object>, <Book: Book object>] print(type(ret))#<class 'django.db.models.query.QuerySet'> ret2=models.Book.objects.first() print(type(ret2))#<class 'app01.models.Book'> #正向查找(對象) print(ret2.title) #Python print(ret2.publisher) # Publisher object print(ret2.publisher.name)# 人大出版社 print(ret2.publisher.city)# 北京 #反向查找(對象) ret3=models.Publisher.objects.first() ret3_bookobj=ret3.book_set.all() print(ret3_bookobj)#[<Book: Book object>, <Book: Book object>] print(type(ret3_bookobj))#<class 'django.db.models.query.QuerySet'> #####****************######### ret4=models.Book.objects.filter(title='Python').values('id') print(type(ret4)) print(ret4)#[{'id': 1}] #正向查找(條件) ret5=models.Book.objects.filter(title='Python').values('publisher__city') print(ret5)#[{'publisher__city': '北京'}] ret6=models.Book.objects.filter(publisher__address='北京').values('publisher__name') print(ret6)#[{'publisher__name': '人大出版社'}, {'publisher__name': '人大出版社'}] #反向查找(條件) ret7=models.Publisher.objects.filter(book__title='Python').values('name') print(ret7)#[{'name': '人大出版社'}] 注意,book__title中的book就是Publisher的關聯表名 ret8=models.Publisher.objects.filter(book__title='Python').values('book__authors') print(ret8)#[{'book__authors': 1}, {'book__authors': 2}] # 多對多關系的 ret9=models.Author.objects.filter(book__title='Python').values('name') print(ret9)#[{'name': 'alex'}, {'name': 'alvin'}] return HttpResponse("ok")
3.3 聚合查詢和分組查詢
1 aggregate(*args,**kwargs):通過對QuerySet進行計算,返回一個聚合值的字典。aggregate()中每一個參數都指定一個包含在字典中的返回值。用於聚合查詢。
2 annotate(*args,**kwargs):可以為QuerySet每一個對象添加注解。可以通過計算查詢結果中每一個對象所關聯的對象集合,從而得出總計值(也可以是平均值或總和),用於分組查詢
聚合函數(Aggregation Functions) 所在位置:django.db.models
1 Avg: 返回所給字段的平均值
2 Count: 根據所給的關聯字段返回被關聯的model的數量
3 Max:返回所給字段的最大值
4 Sum:計算所給字段的總和
實例:
1 查詢中國郵電大學出版社出版了多少本書?
2 查詢alex出的書總價格
3 查詢各個作者出的書的總價格
這里就涉及到分組了,分組條件是authors__name,
4 查詢各個出版社最便宜的書價是多少
3.4
僅僅靠單一的關鍵字參數查詢已經很難滿足查詢要求。此時Django為我們提供了F和Q查詢:

# F 使用查詢條件的值 # # from django.db.models import F # models.Tb1.objects.update(num=F('num')+1) # Q 構建搜索條件 from django.db.models import Q # con = Q() # # q1 = Q() # q1.connector = 'OR' # q1.children.append(('id', 1)) # q1.children.append(('id', 10)) # q1.children.append(('id', 9)) # # q2 = Q() # q2.connector = 'OR' # q2.children.append(('c1', 1)) # q2.children.append(('c1', 10)) # q2.children.append(('c1', 9)) # # con.add(q1, 'AND') # con.add(q2, 'AND') # # models.Tb1.objects.filter(con) # # from django.db import connection # cursor = connection.cursor() # cursor.execute("""SELECT * from tb where name = %s""", ['Lennon']) # row = cursor.fetchone()

from django.db.models import Q #1 Q對象(django.db.models.Q)可以對關鍵字參數進行封裝,從而更好地應用多個查詢 q1=models.Book.objects.filter(Q(title__startswith='P')).all() print(q1)#[<Book: Python>, <Book: Perl>] # 2、可以組合使用&,|操作符,當一個操作符是用於兩個Q的對象,它產生一個新的Q對象。 Q(title__startswith='P') | Q(title__startswith='J') # 3、Q對象可以用~操作符放在前面表示否定,也可允許否定與不否定形式的組合 Q(title__startswith='P') | ~Q(pub_date__year=2005) # 4、應用范圍: # # Each lookup function that takes keyword-arguments (e.g. filter(), # exclude(), get()) can also be passed one or more Q objects as # positional (not-named) arguments. If you provide multiple Q object # arguments to a lookup function, the arguments will be “AND”ed # together. For example: Book.objects.get( Q(title__startswith='P'), Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)) ) ''' ... roughly translates into the SQL: SELECT * from polls WHERE question LIKE 'P%' AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06') ''' # 5、Q對象可以與關鍵字參數查詢一起使用,不過一定要把Q對象放在關鍵字參數查詢的前面。 # 正確: Book.objects.get( Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)), title__startswith='P') # 錯誤: Book.objects.get( question__startswith='P', Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)))
4 刪除
>>> Book.objects.filter(id=1).delete() (3, {'app01.Book_authors': 2, 'app01.Book': 1})
我們表面上刪除了一條信息,實際卻刪除了三條,因為我們刪除的這本書在Book_authors表中有兩條相關信息,這種刪除方式就是django默認的級聯刪除。
Template基礎
你可能已經注意到我們在例子視圖中返回文本的方式有點特別。 也就是說,HTML被直接硬編碼在 Python 代碼之中。
def current_datetime(request): now = datetime.datetime.now() html = "<html><body>It is now %s.</body></html>" % now return HttpResponse(html)
盡管這種技術便於解釋視圖是如何工作的,但直接將HTML硬編碼到你的視圖里卻並不是一個好主意。 讓我們來看一下為什么:
-
對頁面設計進行的任何改變都必須對 Python 代碼進行相應的修改。 站點設計的修改往往比底層 Python 代碼的修改要頻繁得多,因此如果可以在不進行 Python 代碼修改的情況下變更設計,那將會方便得多。
-
Python 代碼編寫和 HTML 設計是兩項不同的工作,大多數專業的網站開發環境都將他們分配給不同的人員(甚至不同部門)來完成。 設計者和HTML/CSS的編碼人員不應該被要求去編輯Python的代碼來完成他們的工作。
-
程序員編寫 Python代碼和設計人員制作模板兩項工作同時進行的效率是最高的,遠勝於讓一個人等待另一個人完成對某個既包含 Python又包含 HTML 的文件的編輯工作。
基於這些原因,將頁面的設計和Python的代碼分離開會更干凈簡潔更容易維護。 我們可以使用 Django的 模板系統 (Template System)來實現這種模式,這就是本章要具體討論的問題。
Django 模版基本語法
>>> from django.template import Context, Template >>> t = Template('My name is {{ name }}.') >>> c = Context({'name': 'Stephane'}) >>> t.render(c) u'My name is Stephane.'
同一模板,多個上下文
一旦有了 模板 對象,你就可以通過它渲染多個context, 例如:
>>> from django.template import Template, Context >>> t = Template('Hello, {{ name }}') >>> print t.render(Context({'name': 'John'})) Hello, John >>> print t.render(Context({'name': 'Julie'})) Hello, Julie >>> print t.render(Context({'name': 'Pat'})) Hello, Pat
無論何時我們都可以像這樣使用同一模板源渲染多個context,只進行 一次模板創建然后多次調用render()方法渲染會更為高效:
# Low for name in ('John', 'Julie', 'Pat'): t = Template('Hello, {{ name }}') print t.render(Context({'name': name})) # Good t = Template('Hello, {{ name }}') for name in ('John', 'Julie', 'Pat'): print t.render(Context({'name': name}))
Django 模板解析非常快捷。 大部分的解析工作都是在后台通過對簡短正則表達式一次性調用來完成。 這和基於 XML 的模板引擎形成鮮明對比,那些引擎承擔了 XML 解析器的開銷,且往往比 Django 模板渲染引擎要慢上幾個數量級。

from django.shortcuts import render,HttpResponse from django.template.loader import get_template #記得導入 # Create your views here. import datetime from django.template import Template,Context # def current_time(req): #原始的視圖函數 # now=datetime.datetime.now() # html="<html><body>現在時刻:<h1>%s.</h1></body></html>" %now # return HttpResponse(html) # def current_time(req): #django模板修改的視圖函數 # now=datetime.datetime.now() # t=Template('<html><body>現在時刻是:<h1 style="color:red">{{current_date}}</h1></body></html>') #t=get_template('current_datetime.html') # c=Context({'current_date':now}) # html=t.render(c) # return HttpResponse(html) #另一種寫法(推薦) def current_time(req): now=datetime.datetime.now() return render(req, 'current_datetime.html', {'current_date':now})
深度變量的查找
在到目前為止的例子中,我們通過 context 傳遞的簡單參數值主要是字符串,然而,模板系統能夠非常簡潔地處理更加復雜的數據結構,例如list、dictionary和自定義的對象。
在 Django 模板中遍歷復雜數據結構的關鍵是句點字符 (.)。
最好是用幾個例子來說明一下。 比如,假設你要向模板傳遞一個 Python 字典。 要通過字典鍵訪問該字典的值,可使用一個句點:
>>> from django.template import Template, Context >>> person = {'name': 'Sally', 'age': '43'} >>> t = Template('{{ person.name }} is {{ person.age }} years old.') >>> c = Context({'person': person}) >>> t.render(c) u'Sally is 43 years old.'
同樣,也可以通過句點來訪問對象的屬性。 比方說, Python 的 datetime.date 對象有 year 、 month 和 day 幾個屬性,你同樣可以在模板中使用句點來訪問這些屬性:
>>> from django.template import Template, Context >>> import datetime >>> d = datetime.date(1993, 5, 2) >>> d.year 1993 >>> d.month 5 >>> d.day 2 >>> t = Template('The month is {{ date.month }} and the year is {{ date.year }}.') >>> c = Context({'date': d}) >>> t.render(c) u'The month is 5 and the year is 1993.'
這個例子使用了一個自定義的類,演示了通過實例變量加一點(dots)來訪問它的屬性,這個方法適用於任意的對象。
>>> from django.template import Template, Context >>> class Person(object): ... def __init__(self, first_name, last_name): ... self.first_name, self.last_name = first_name, last_name >>> t = Template('Hello, {{ person.first_name }} {{ person.last_name }}.') >>> c = Context({'person': Person('John', 'Smith')}) >>> t.render(c) u'Hello, John Smith.'
點語法也可以用來引用對象的* 方法*。 例如,每個 Python 字符串都有 upper() 和 isdigit() 方法,你在模板中可以使用同樣的句點語法來調用它們:
>>> from django.template import Template, Context >>> t = Template('{{ var }} -- {{ var.upper }} -- {{ var.isdigit }}') >>> t.render(Context({'var': 'hello'})) u'hello -- HELLO -- False' >>> t.render(Context({'var': '123'})) u'123 -- 123 -- True'
注意這里調用方法時並* 沒有* 使用圓括號 而且也無法給該方法傳遞參數;你只能調用不需參數的方法。 (我們將在本章稍后部分解釋該設計觀。)
最后,句點也可用於訪問列表索引,例如:
>>> from django.template import Template, Context >>> t = Template('Item 2 is {{ items.2 }}.') >>> c = Context({'items': ['apples', 'bananas', 'carrots']}) >>> t.render(c) u'Item 2 is carrots.'
include 模板標簽
在講解了模板加載機制之后,我們再介紹一個利用該機制的內建模板標簽: {% include %} 。該標簽允許在(模板中)包含其它的模板的內容。 標簽的參數是所要包含的模板名稱,可以是一個變量,也可以是用單/雙引號硬編碼的字符串。 每當在多個模板中出現相同的代碼時,就應該考慮是否要使用 {% include %} 來減少重復。
extend(繼承)模板標簽
到目前為止,我們的模板范例都只是些零星的 HTML 片段,但在實際應用中,你將用 Django 模板系統來創建整個 HTML 頁面。 這就帶來一個常見的 Web 開發問題: 在整個網站中,如何減少共用頁面區域(比如站點導航)所引起的重復和冗余代碼?
解決該問題的傳統做法是使用 服務器端的 includes ,你可以在 HTML 頁面中使用該指令將一個網頁嵌入到另一個中。 事實上, Django 通過剛才講述的 {% include %} 支持了這種方法。 但是用 Django 解決此類問題的首選方法是使用更加優雅的策略—— 模板繼承 。
本質上來說,模板繼承就是先構造一個基礎框架模板,而后在其子模板中對它所包含站點公用部分和定義塊進行重載。
讓我們通過修改 current_datetime.html 文件,為 current_datetime 創建一個更加完整的模板來體會一下這種做法:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> <html lang="en"> <head> <title>The current time</title> </head> <body> <h1>My helpful timestamp site</h1> <p>It is now {{ current_date }}.</p> <hr> <p>Thanks for visiting my site.</p> </body> </html>
這看起來很棒,但如果我們要為 hours_ahead 視圖創建另一個模板會發生什么事情呢?
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> <html lang="en"> <head> <title>Future time</title> </head> <body> <h1>My helpful timestamp site</h1> <p>In {{ hour_offset }} hour(s), it will be {{ next_time }}.</p> <hr> <p>Thanks for visiting my site.</p> </body> </html>
很明顯,我們剛才重復了大量的 HTML 代碼。 想象一下,如果有一個更典型的網站,它有導航條、樣式表,可能還有一些 JavaScript 代碼,事情必將以向每個模板填充各種冗余的 HTML 而告終。
解決這個問題的服務器端 include 方案是找出兩個模板中的共同部分,將其保存為不同的模板片段,然后在每個模板中進行 include。 也許你會把模板頭部的一些代碼保存為 header.html 文件:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> <html lang="en"> <head>
你可能會把底部保存到文件 footer.html :
<hr> <p>Thanks for visiting my site.</p> </body> </html>
對基於 include 的策略,頭部和底部的包含很簡單。 麻煩的是中間部分。 在此范例中,每個頁面都有一個<h1>My helpful timestamp site</h1> 標題,但是這個標題不能放在 header.html 中,因為每個頁面的 <title> 是不同的。 如果我們將 <h1> 包含在頭部,我們就不得不包含 <title> ,但這樣又不允許在每個頁面對它進行定制。 何去何從呢?
Django 的模板繼承系統解決了這些問題。 你可以將其視為服務器端 include 的逆向思維版本。 你可以對那些不同 的代碼段進行定義,而不是 共同 代碼段。
第一步是定義 基礎模板 , 該框架之后將由 子模板 所繼承。 以下是我們目前所講述范例的基礎模板:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> <html lang="en"> <head> <title>{% block title %}{% endblock %}</title> </head> <body> <h1>My helpful timestamp site</h1> {% block content %}{% endblock %} {% block footer %} <hr> <p>Thanks for visiting my site.</p> {% endblock %} </body> </html>
這個叫做 base.html 的模板定義了一個簡單的 HTML 框架文檔,我們將在本站點的所有頁面中使用。 子模板的作用就是重載、添加或保留那些塊的內容。 (如果你一直按順序學習到這里,保存這個文件到你的template目錄下,命名為 base.html .)
我們使用模板標簽: {% block %} 。 所有的 {% block %} 標簽告訴模板引擎,子模板可以重載這些部分。 每個{% block %}標簽所要做的是告訴模板引擎,該模板下的這一塊內容將有可能被子模板覆蓋。
現在我們已經有了一個基本模板,我們可以修改 current_datetime.html 模板來 使用它:
{% extends "base.html" %} {% block title %}The current time{% endblock %} {% block content %} <p>It is now {{ current_date }}.</p> {% endblock %}
再為 hours_ahead 視圖創建一個模板,看起來是這樣的:
{% extends "base.html" %} {% block title %}Future time{% endblock %} {% block content %} <p>In {{ hour_offset }} hour(s), it will be {{ next_time }}.</p> {% endblock %}
看起來很漂亮是不是? 每個模板只包含對自己而言 獨一無二 的代碼。 無需多余的部分。 如果想進行站點級的設計修改,僅需修改 base.html ,所有其它模板會立即反映出所作修改。
以下是其工作方式。 在加載 current_datetime.html 模板時,模板引擎發現了 {% extends %} 標簽, 注意到該模板是一個子模板。 模板引擎立即裝載其父模板,即本例中的 base.html 。
此時,模板引擎注意到 base.html 中的三個 {% block %} 標簽,並用子模板的內容替換這些 block 。因此,引擎將會使用我們在 { block title %} 中定義的標題,對 {% block content %} 也是如此。 所以,網頁標題一塊將由{% block title %}替換,同樣地,網頁的內容一塊將由 {% block content %}替換。
注意由於子模板並沒有定義 footer 塊,模板系統將使用在父模板中定義的值。 父模板 {% block %} 標簽中的內容總是被當作一條退路。
繼承並不會影響到模板的上下文。 換句話說,任何處在繼承樹上的模板都可以訪問到你傳到模板中的每一個模板變量。
你可以根據需要使用任意多的繼承次數。 使用繼承的一種常見方式是下面的三層法:
-
創建 base.html 模板,在其中定義站點的主要外觀感受。 這些都是不常修改甚至從不修改的部分。
-
為網站的每個區域創建 base_SECTION.html 模板(例如, base_photos.html 和 base_forum.html )。這些模板對base.html 進行拓展,並包含區域特定的風格與設計。
-
為每種類型的頁面創建獨立的模板,例如論壇頁面或者圖片庫。 這些模板拓展相應的區域模板。
這個方法可最大限度地重用代碼,並使得向公共區域(如區域級的導航)添加內容成為一件輕松的工作。
以下是使用模板繼承的一些訣竅:
-
如果在模板中使用 {% extends %} ,必須保證其為模板中的第一個模板標記。 否則,模板繼承將不起作用。
-
一般來說,基礎模板中的 {% block %} 標簽越多越好。 記住,子模板不必定義父模板中所有的代碼塊,因此你可以用合理的缺省值對一些代碼塊進行填充,然后只對子模板所需的代碼塊進行(重)定義。 俗話說,鈎子越多越好。
-
如果發覺自己在多個模板之間拷貝代碼,你應該考慮將該代碼段放置到父模板的某個 {% block %} 中。
-
如果你需要訪問父模板中的塊的內容,使用 {{ block.super }}這個標簽吧,這一個魔法變量將會表現出父模板中的內容。 如果只想在上級代碼塊基礎上添加內容,而不是全部重載,該變量就顯得非常有用了。
-
不允許在同一個模板中定義多個同名的 {% block %} 。 存在這樣的限制是因為block 標簽的工作方式是雙向的。 也就是說,block 標簽不僅挖了一個要填的坑,也定義了在父模板中這個坑所填充的內容。如果模板中出現了兩個相同名稱的 {% block %} 標簽,父模板將無從得知要使用哪個塊的內容。
Template進階
一模版的組成
HTML代碼+邏輯控制代碼
二 邏輯控制代碼的組成
1 變量(使用雙大括號來引用變量)
{{var_name}}
2 標簽(tag)的使用(使用大括號和百分比的組合來表示使用tag)
{%load staticfiles%}
3 過濾器(filter)的使用
{{ship_date|date:"Fj,Y"}},ship_date變量傳給data過濾器,date過濾器通過使用"FJ,Y"這幾個參數來格式化日期數據。"|"代表類似Unix命令中的管道操作。
三 常用標簽
1 {%if%} 的使用
可以使用你的and,or,not來組織的邏輯。但不允許and和or同時出現在條件語句中。新版本中已經支持了{%elif%} 這樣的用法。
2 {% for %}的使用
用來循環一個序列, 還可以使用reserser關鍵字來進行倒序遍歷,一般可以先用if語句判斷序列是否為空,再進行遍歷;還可以使用empty關鍵字來進行為空時的跳轉
{{li }} {% for i in li %} <li>{{ forloop.counter0 }}----{{ i }}</li> {% empty %} <li>this is empty!</li> {% endfor %} {# [11, 22, 33, 44, 55]#} {# 0----11#} {# 1----22#} {# 2----33#} {# 3----44#} {# 4----55#}
3 {%csrf_token%}
用於生成csrf_token的標簽,用於防治跨站攻擊驗證。
注意如果你在view的index里用的是render_to_response方法,此時標簽由於沒有上下文的關系所以沒有效果,需要:
from django.template import RequestContext ,Template return render_to_response("index.html",locals(),context_instance=RequestContext(req))
或者直接使用render。
其實,這里是會生成一個input標簽,和其他表單標簽一起提交給后台的。
4 {% load %}: 加載標簽庫
5 {% url %}: 引用路由配置的地址
<form action="{% url "bieming"%}" > <input type="text"> <input type="submit"value="提交"> {%csrf_token%} </form>
6 {% with %}:用更簡單的變量名替代復雜的變量名
{% with total=fhjsaldfhjsdfhlasdfhljsdal %} {{ total }} {% endwith %}
7 {% verbatim %}: 禁止render
{% verbatim %} {{ hello }} {% endverbatim %}
四 常用過濾器
1 add : 給變量加上相應的值
2 addslashes: 給變量中的引號前加上斜線
3 capfirst : 首字母大寫
4 cut : 從字符串中移除指定的字符
5 date : 格式化日期字符串
6 default : 如果值是False,就替換成設置的默認值,否則就是用本來的值
7 default_if_none:如果值是None,就替換成設置的默認值,否則就使用本來的值
{#value1="aBcDe" #} {{ value1|upper }}<br> {# value2=5#} {{ value2|add:3 }}<br> {# value3='he llo wo r ld'#} {{ value3|cut:' ' }}<br> {# import datetime#} {# value4=datetime.datetime.now()#} {{ value4|date:'Y-m-d' }}<br> {# value5=[]#} {{ value5|default:'空的' }}<br>

{# value6='<a href="#">跳轉</a>'#}
{{ value6 }} {% autoescape off %} {{ value6 }} {% endautoescape %} {{ value6|safe }}<br> {{ value6|striptags }} {# value7='1234'#} {{ value7|filesizeformat }}<br> {{ value7|first }}<br> {{ value7|length }}<br> {{ value7|slice:":-1" }}<br> {# value8='http://www.baidu.com/?a=1&b=3'#} {{ value8|urlencode }}<br> {# value9='hello I am yuan'#} {{ value9|truncatechars:'6' }}<br> {{ value9|truncatewords:'2' }}<br>
自定義simple_tag
a、在app中創建templatetags模塊
b、創建任意 .py 文件,如:add100.py
#!/usr/bin/env python #coding:utf-8 from django import template from django.utils.safestring import mark_safe from django.template.base import resolve_variable, Node, TemplateSyntaxError register = template.Library() @register.simple_tag def my_add100(v1): return v1 + 100 @register.simple_tag def my_input(id,arg): result = "<input type='text' id='%s' class='%s' />" %(id,arg,) return mark_safe(result)
c、在使用自定義simple_tag的html文件中導入之前創建的 add100.py 文件名
{% load add100 %}
d、使用simple_tag
{% my_add100 3%} {% my_input 'id_username' 'hide'%}
e、在settings中配置當前app,不然django無法找到自定義的simple_tag

INSTALLED_APPS = ( 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'app01', )
FORM
form
一 什么是Form?什么是DjangoForm?
Django表單系統中,所有的表單類都作為django.forms.Form的子類創建,包括ModelForm
關於django的表單系統,主要分兩種
基於django.forms.Form:所有表單類的父類
基於django.forms.ModelForm:可以和模型類綁定的Form
需求:向數據庫的Info表中添加一些新的個人信息
二 不使用Django Form的情況

#***************urls.py urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^add_info/', views.add_info,name='add_info'), ] ##************************view.py from django.shortcuts import render,HttpResponse # Create your views here. from app01.models import * def add_info(req): if req.method=='POST': name=req.POST.get('name') age=req.POST.get('age') sex=req.POST.get('sex') birthday=req.POST.get('birthday') qualification=req.POST.get('qualification') job=req.POST.get('job') Info.objects.create(name=name, age=age, sex=sex, birthday=birthday, qualification=qualification, job=job ) return HttpResponse('添加成功!') return render(req,'add_info.html') #*****************************add_info.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>添加個人信息</title> </head> <body> <form action="{% url 'add_info' %}" method="post"> 姓名<input type="text" name="name"><br> 性別<input type="text" name="sex"><br> 年齡<input type="text" name="age"><br> 生日<input type="text" name="birthday"><br> 學歷<input type="text" name="qualification"><br> 工作<input type="text" name="job"><br> <input type="submit" value="提交"><br> {% csrf_token %} </form> </body> </html>
三 使用Form的情況

#-----------------------------------------urls.py #----------------------------------------- from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^add_info/', views.add_info,name='add_info'), ] #-----------------------------------------models.py #----------------------------------------- from django.db import models # Create your models here. class Info(models.Model): name = models.CharField(max_length=64) sex = models.CharField(max_length=64) birthday = models.CharField(max_length=64) age=models.CharField(max_length=64) qualification=models.CharField(max_length=64) job=models.CharField(max_length=64) email=models.CharField(max_length=64,default='') class Hobby(models.Model): item=models.CharField(max_length=64) #-----------------------------------------form.py #----------------------------------------- from django import forms from app01 import models from django.core.exceptions import ValidationError class Info_form(forms.Form): def validate_name(value): try: models.Info.objects.get(name=value) raise ValidationError('%s 的信息已經存在!'%value) except models.Info.DoesNotExist: pass sex_choice=((0,'男'), (1,'女'))#select的數據可以像這樣寫,也可以在另外一張表中動態去拿 name = forms.CharField(validators=[validate_name],label='姓名',error_messages={'required':'必填'}) age = forms.CharField(label='年齡',error_messages={'required':'必填'}) # sex = forms.CharField(label='性別',error_messages={'required':'必填',},) sex=forms.IntegerField(widget=forms.widgets.Select(choices=sex_choice, attrs={'class':'setform2'} )) birthday = forms.CharField(label='生日',error_messages={'required':'必填'}) qualification = forms.CharField(label='學歷',error_messages={'required':'必填'}, widget=forms.TextInput(attrs={'class':'formset', 'placeholder':'本科' } )) email=forms.EmailField(max_length=100,min_length=10) job = forms.CharField(label='工作',error_messages={'required':'必填'}) def __init__(self,*args,**kwargs): super(Info_form,self).__init__(*args,**kwargs) self.fields['hobby']=forms.CharField(widget=forms.widgets.Select(choices=models.Hobby.objects.values_list('id','item'))) #-------------------------------------------------------views.py #------------------------------------------------------- from django.shortcuts import render,HttpResponse # Create your views here. from app01.models import * from app01.forms import * def add_info(req): if req.method=='POST': Info_form_obj=Info_form(req.POST) if Info_form_obj.is_valid(): Info.objects.create(name=Info_form_obj.cleaned_data['name'], age=Info_form_obj.cleaned_data['age'], sex=Info_form_obj.cleaned_data['sex'], birthday=Info_form_obj.cleaned_data['birthday'], qualification=Info_form_obj.cleaned_data['qualification'], job=Info_form_obj.cleaned_data['job'] ) return HttpResponse('添加成功!') else: error_obj=Info_form_obj.errors print('***************') print(type(error_obj))#<class 'django.forms.utils.ErrorDict'> print(error_obj['name'][0])#必填 print(error_obj.get('age'))#<ul class="errorlist"><li>必填</li></ul> return render(req,'add_info.html',{'form_obj':Info_form_obj,'error_obj':error_obj}) Info_form_obj=Info_form() return render(req,'add_info.html',{'form_obj':Info_form_obj}) #------------------------------------------------------add_info.html #------------------------------------------------------- <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>添加個人信息</title> <style> .formset{ color: rebeccapurple; border: dashed cadetblue; } </style> </head> <body> <form action="{% url 'add_info' %}" method="post"> <p>姓名{{ form_obj.name }}{{ error_obj.name.0 }}</p> <p>年齡{{ form_obj.age }}{{ error_obj.age.0 }}</p> <p>生日{{ form_obj.birthday }}{{ error_obj.birthday.0 }}</p> <p>工作{{ form_obj.job }}<span>{{ error_obj.job }}</span></p> <p>學歷{{ form_obj.qualification }}<span>{{ error_obj.qualification }}</span></p> <p>性別{{ form_obj.sex }}<span>{{ error_obj.sex }}</span></p> <p>郵箱{{ form_obj.email }}<span>{{ error_obj.email }}</span></p> <p>愛好{{ form_obj.hobby }}<span>{{ error_obj.hobby }}</span></p> {# {{ form_obj.as_p }}#} <input type="submit" value="提交"><br> {% csrf_token %} </form> </body> </html>
四 使用ModelForm的情況

# #-----------------------------------------form.py # #----------------------------------------- from django import forms from app01 import models class Info_form(forms.ModelForm): class Meta: model=models.Info exclude=('id',) def __init__(self,*args,**kwargs): super(Info_form,self).__init__(*args,**kwargs) self.fields['hobby']=forms.CharField(widget=forms.widgets.Select(choices=models.Hobby.objects.values_list('id','item'))) # #-------------------------------------------------------views.py # #------------------------------------------------------- from django.shortcuts import render,HttpResponse from app01.forms import * def add_info(req): if req.method=='POST': Info_form_obj=Info_form(req.POST) if Info_form_obj.is_valid(): Info_form_obj.save()#替換create return HttpResponse('添加成功!') else: error_obj=Info_form_obj.errors return render(req,'add_info.html',{'form_obj':Info_form_obj,'error_obj':error_obj}) Info_form_obj=Info_form() return render(req,'add_info.html',{'form_obj':Info_form_obj})
Model Form下的驗證
django提供了3種方式來驗證表單
實例:自定義驗證,不能插入重名的出版社名稱。
一 表單字段的驗證器(和form是一樣的)
二 clean_filedname,驗證字段,針對某個字段進行驗證。
三 表單clean方法,可針對整個表單進行驗證
像注冊時需要輸入兩次密碼的驗證,用clean來做就非常好,因為前兩種都只是針對某一個字段進行驗證,而確認密碼則需要將兩個字段信息都拿來進行匹配。
文件上傳
自定義文件上傳
def postfile(req): if req.method=='POST': obj=req.FILES.get('Yuan_file') print(obj) f=open(obj.name,'wb') for i in obj: f.write(i) return render(req,'post.html')
Form上傳文件

#-----------------------------------------------------------urls.py urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^postfile/', views.postfile), ] #-----------------------------------------------------------models.py from django.db import models # Create your models here. from django.db import models class UploadFile(models.Model): file = models.FileField(upload_to = './upload/') date = models.DateTimeField(auto_now_add=True) #-----------------------------------------------------------views.py from django.shortcuts import render,HttpResponse # Create your views here. from django import forms from app01 import models class FileForm(forms.Form): PostFile = forms.FileField() def postfile(req): if req.method=="POST": obj = FileForm(req.POST,req.FILES) if obj.is_valid(): upload = models.UploadFile() upload.file = obj.cleaned_data['PostFile'] upload.save() print (upload.file) return HttpResponse('上傳成功!') obj=FileForm() return render(req,'post.html',{'obj':obj}) #-----------------------------------------------------------post.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="/postfile/" method="post" enctype="multipart/form-data"> {{ obj.PostFile }} {% csrf_token %} <input type="submit" value="提交"> </form> </body> </html>