用於實現面向對象編程語言裏不一樣類型系統的數據之間的轉換,換言之,就是用面向對象的方式去操做數據庫的建立表以及增刪改查等操做。html
到目前爲止,當咱們的程序涉及到數據庫相關操做時,通常操做流程以下:python
ORM是什麼?Object Relational Mapping(關係對象映射)
mysql
一、類名------>數據庫中的表名git
二、類屬性--------->數據庫的字段web
三、類實例--------->數據庫表裏的一條記錄正則表達式
四、obj.id obj.name------>獲取類實例對象的屬性sql
Django ORM的優點:shell
一、Django的orm操做本質是根據對接的數據庫引擎,翻譯成對應的sql語句, 避免新手寫sql語句帶來的性能問題,同時ORM使咱們的通用數據庫交互變得簡單易行,並且徹底不用考慮複雜的SQL語句。數據庫
二、全部使用Django開發的項目無需關心程序底層使用的是MySQL、Oracle、sqlite....等數據庫,若是數據庫須要遷移,只須要更換Django的數據庫引擎便可;
django
爲了更好的理解,咱們來作一個基本的 地名(好比某個門牌號碼)/餐館/服務員 數據庫結構,這個也是受Django官網的啓發而本身定義的,可能不是很是恰當,可是能夠幫助理解。
一、梳理關係和建模
場景以下:
在某條街上Place,有一家餐館Restaurant,在這家餐館裏面有不少服務員Waiter,每一個服務員在這條街上可能有多個住處Place,同時,這個Place可能住着多個服務員Waiter。
因此,這其中的關係能夠梳理以下:
Place---->Restaurant(一對一):一條街上對應一家餐館名
Restaurant---->Waiter(一對多):一個餐館裏面有多名服務員
Place---->Waiter(多對多):一個地方可能住着好幾個服務員,而一個服務員可能在這條街上有好幾個住所
二、定義表和肯定字段
一個place有地名和地址
一個餐館有惟一的地址和經營的餐飲項目
一個服務員有本身的姓名、住所、工做的餐館
from django.db import models
class Place(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)
def __str__(self): # __unicode__ on Python 2
return "%s the place" % self.name
class Restaurant(models.Model):
place = models.OneToOneField(
Place,
on_delete=models.CASCADE,
primary_key=True,
)
serves_hot_dogs = models.BooleanField(default=False)
serves_pizza = models.BooleanField(default=False)
def __str__(self): # __unicode__ on Python 2
return "%s the restaurant" % self.place.name
class Waiter(models.Model):
restaurant = models.ForeignKey(Restaurant, on_delete=models.CASCADE)
places = models.ManyToManyField(Place)
name = models.CharField(max_length=50)
def __str__(self): # __unicode__ on Python 2
return "%s the waiter at %s" % (self.name, self.restaurant)
三、setttings和同步數據庫
a、在settings裏的INSTALLED_APPS中加入'app'
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'blog',
]
b、同步數據庫。
一、數據同步的準備工做
python manager makemigrations
二、數據同步
python manager migrate
三、數據表結構
關係數據庫的威力體如今表之間的相互關聯。 Django 提供了三種最多見的數據庫關係:多對一(many-to-one),多對多(many-to-many),一對一(one-to-one)。其中多對一和多對多更爲常見。
一、多對一(many-to-one)
Django 使用 django.db.models.ForeignKey
定義多對一關係。 和使用其它Field
類型同樣:在模型當中把它作爲一個類屬性包含進來。
ForeignKey
須要一個位置參數:與該模型關聯的類。記住:哪一個是多,就在哪一個裏面寫Foreiginkey。
好比,一個餐館有多個服務員,可是一個服務員只能在一家餐館工做,固然這裏不考慮兼職的狀況了。
二、一對一(one-to-one)
OneToOneField
用來定義一對一關係。 和使用其它Field
類型同樣:在模型當中把它作爲一個類屬性包含進來。
當某個對象想擴展自另外一個對象時,最經常使用的方式就是在這個對象的主鍵上添加一對一關係。
OneToOneField
要一個位置參數:與模型關聯的類。
例如,若是你正在創建一個「places」的數據庫,那麼你將創建一個很是標準的地址、電話號碼等 在數據庫中。 接下來,若是你想在place數據庫的基礎上創建一個restaurant數據庫,而不想將已有的字段複製到Restaurant
模型,那你能夠在 Restaurant
添加一個OneToOneField
字段,這個字段指向Place
(由於Restaurant 自己就是一個Place;事實上,在處理這個問題的時候,你應該使用一個典型的 inheritance,它隱含一個一對一關係)。
與ForeignKey
同樣,能夠定義遞歸關係,並能夠引用還沒有定義的模型。
三、多對多(many-to-many)
ManyToManyField
用來定義多對多關係, 和使用其它Field
類型同樣:在模型當中把它作爲一個類屬性包含進來。
ManyToManyField
須要一個位置參數:和該模型關聯的類。
例如,一個Pizza
能夠有多種Topping
即一種Topping
也能夠位於多個Pizza上,並且每一個Pizza
有多個topping
四、實例
a、咱們進行對象的創建,首先進入python 的shell環境,而後導入對應的對象。創建2個place、2個restaurant、4個waiter
place1===>restaurant1(111街上有一間餐館,名爲:restaurant1)
waiter1和waiter2==>restaurant1(waiter1和waiter2工做在restaurant1)
waiter1==>hourse1和hourse2:(waiter1有hourse1和hourse2兩個住所)
waiter2==>hourse2(waiter2只有hourse2一個住所)
place2===>restaurant2(222街上有一間餐館,名爲:restaurant2)
waiter3和waiter4===>restaurant2(waiter3和waiter4工做在restaurant2)
waiter3和waiter4==>hourse3(waiter3和waiter4同居在hourse3)
(venv) D:\xuequn\venv\Scripts\firstapp>python manage.py shell
Python 2.7.15 (v2.7.15:ca079a3ea3, Apr 30 2018, 16:30:26) [MSC v.1500 64 bit (AM
D64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from blog import models
>>> from models import Place
Traceback (most recent call last):
File "<console>", line 1, in <module>
ImportError: No module named models
>>> from blog import models
>>> from blog.models import Place
>>> from blog.models import Restaurant
>>> from blog.models import Waiter
>>> place1=Place(name='place1',address='111-place1-west-road')
>>> place1.save()
>>> place2=Place(name='place2',address='222-place2-west-road')
>>> place2.save()
>>> res1=Restaurant(place=place1,serves_hot_dogs=True,serves_pizza=True)
>>> res1.save()
>>> res2=Restaurant(place=place2,serves_hot_dogs=True,serves_pizza=False)
>>> res2.save()
>>> w1=Waiter(name='waiter1',restaurant=res1)
>>> w1.save()
>>> w2=Waiter(name='waiter2',restaurant=res1)
>>>
>>> w2.save()
>>> w3=Waiter(name='waiter3',restaurant=res2)
>>> w3.save()
>>> w4=Waiter(name='waiter4',restaurant=res2)
>>> w4.save()
>>> hourse1=Place(name='hourse1',address='hourse1-east-road')
>>> hourse1.save()
>>> hourse2=Place(name='hourse2',address='hourse2-east-road')
>>> hourse2.save()
>>> hourse3=Place(name='hourse3',address='hourse3-east-road')
>>> hourse3.save()
>>> w1.places.add(hourse1)
>>> w1.save()
>>> w1.places.add(hourse2)
>>> w1.save()
>>> w2.places.add(hourse2)
>>> w2.save()
>>> w3.places.add(hourse3)
>>> w3.save()
>>> w4.places.add(hourse3)
>>> w4.save()
>>>
注意:
一、一對一使用場景:當某個對象想擴展自另外一個對象時,最經常使用的方式就是在這個對象的主鍵上添加一對一關係。這裏的餐館擴展了街道,餐館除了有街道的地址屬性之外,還有餐館名稱和經營項目等。
二、表A是表B的屬性時,表A必須先save,表B才能使用表A,否則會報錯:ValueError: save() prohibited to prevent data loss due to unsaved related object......
下面在此基礎上進行增、刪、改、查操做。
一、增
在學習增操做前,咱們先了解一下模型中經常使用的字段。
#################################################################經常使用字段
<1> CharField
字符串字段, 用於較短的字符串. CharField 要求必須有一個參數 maxlength, 用於從數據庫層和Django校驗層限制該字段所容許的最大字符數.
<2> IntegerField
用於保存一個整數.
<3> 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">)表示該字段保存的數據.
<4> AutoField
一個 IntegerField, 添加記錄時它會自動增加. 你一般不須要直接使用這個字段; 自定義一個主鍵:my_id=models.AutoField(primary_key=True) 若是你不指定主鍵的話,系統會自動添加一個主鍵字段到你的 model.
<5> BooleanField
A true/false field. admin 用 checkbox 來表示此類字段.
<6> TextField
一個容量很大的文本字段. admin 用一個 <textarea> (文本區域)表示該字段數據.(一個多行編輯框).
<7> EmailField
一個帶有檢查Email合法性的 CharField,不接受 maxlength 參數.
<8> DateField
一個日期字段. 共有下列額外的可選參數: 參數 描述 auto_now 當對象被保存時,自動將該字段的值設置爲當前時間.一般用於表示 "last-modified" 時間戳. auto_now_add 當對象首次被建立時,自動將該字段的值設置爲當前時間.一般用於表示對象建立時間.(僅僅在admin中有意義...)
<9> DateTimeField
一個日期時間字段. 相似 DateField 支持一樣的附加選項.
<10> ImageField
相似 FileField, 不過要校驗上傳對象是不是一個合法圖片. 它有兩個可選參數:height_field和width_field, 若是提供這兩個參數,則圖片將按提供的高度和寬度規格保存.
<11> 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 須要如下步驟: #(1)在你的 settings 文件中, 定義一個完整路徑給 MEDIA_ROOT 以便讓 Django在此處保存上傳文件. # (出於性能考慮,這些文件並不保存到數據庫.) 定義MEDIA_URL 做爲該目錄的公共 URL. 要確保該目錄對 # WEB服務器用戶賬號是可寫的. #(2) 在你的 model 中添加 FileField 或 ImageField, 並確保定義了 upload_to 選項,以告訴 Django # 使用 MEDIA_ROOT 的哪一個子目錄保存上傳文件.你的數據庫中要保存的只是文件的路徑(相對於 MEDIA_ROOT). # 出於習慣你必定很想使用 Django 提供的 get_<#fieldname>_url 函數.舉例來講,若是你的 ImageField # 叫做 mug_shot, 你就能夠在模板中以 {{ object.#get_mug_shot_url }} 這樣的方式獲得圖像的絕對路徑.
<12> URLField
用於保存 URL. 若 verify_exists 參數爲 True (默認), 給定的 URL 會預先檢查是否存在( 即URL是否被有效裝入且 沒有返回404響應). admin 用一個 <input type="text"> 文本框表示該字段保存的數據(一個單行編輯框)
<13> NullBooleanField
相似 BooleanField, 不過容許 NULL 做爲其中一個選項. 推薦使用這個字段而不要用 BooleanField 加 null=True 選項 admin 用一個選擇框 <select> (三個可選擇的值: "Unknown", "Yes" 和 "No" ) 來表示這種字段數據.
<14> 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.
<15> XMLField
#一個校驗值是否爲合法XML的 TextField,必須提供參數: schema_path, 它是一個用來校驗文本的 RelaxNG schema #的文件系統路徑.
<16> 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
<17> IPAddressField
一個字符串形式的 IP 地址, (i.e. "24.124.1.30").
<18>CommaSeparatedIntegerField
用於存放逗號分隔的整數值. 相似 CharField, 必需要有maxlength參數.
#######################################################Field重要參數
<1> null
數據庫中字段是否能夠爲空
<2> blank
django的 Admin 中添加數據時是否可容許空值
<3> default
設定缺省值
<4> editable
若是爲假,admin模式下將不能改寫。缺省爲真
<5> primary_key
設置主鍵,若是沒有設置django建立表時會自動加上: id = meta.AutoField('ID', primary_key=True) primary_key=True implies blank=False, null=False and unique=True. Only one primary key is allowed on an object.
<6> unique
數據惟一
<7> verbose_name
Admin中字段的顯示名稱
<8> validator_list
有效性檢查。非有效產生 django.core.validators.ValidationError 錯誤
<9>db_column
db_index 若是爲真將爲此字段建立索引
<10>choices
一個用來選擇值的2維元組。第一個值是實際存儲的值,第二個用來方便進行選擇。 如:
SEX_CHOICES= (( ‘F’,'Female’),(‘M’,'Male’),) gender = models.CharField(max_length=2,choices = SEX_CHOICES)
-------------------------------------增操做(create方式,無需顯示save) ------------------------------
>>> from blog.models import *
>>> Place.objects.create(name='place3',address='333-place3-west-road')
<Place: place3 the place>
>>> Place.objects.create(**{'name':'place4','address':'444-place4-west-road'})
<Place: place4 the place>
-------------------------------------增操做(顯示save方式) ------------------------------
>>> place1=Place(name='place1',address='111-place1-west-road')
>>> place1.save()
>>> place2=Place(name='place2',address='222-place2-west-road')
>>> place2.save()
二、刪
-------------------------------------刪操做(delete方法) ------------------------------
>>> p3=Place.objects.filter(name='place3')
>>> p3
<QuerySet [<Place: place3 the place>]>
>>> p3=Place.objects.filter(name='place3').delete()
------------------------------刪操做( remove()和clear()方法) --------------
book = models.Book.objects.filter(id=1)
book.author.clear() #清空與book中id=1 關聯的全部數據
book.author.remove(2) #能夠爲id
book.author.remove(*[1,2,3,4]) #能夠爲列表,前面加*
#反向
author = models.Author.objects.filter(id=1)
author.book_set.clear() #清空與boy中id=1 關聯的全部數據
三、改
----------------------------------------改(直接修改屬性後save) ----------------------------------------
>>> h3=Place.objects.get(id=9)
>>> h3
<Place: hourse3 the place>
>>> h3.address='hourse3-west-road'
>>> h3.save()
>>>
這種方法須要知道被修改內容的ID號。
----------------------------------------改(級聯update方法) ----------------------------------------
>>> Place.objects.filter(id=9).update(address='new_place')
1
>>> h3=Place.objects.get(id=9)
>>> h3.address
u'new_place'
>>>
第二種方式修改不能用get的緣由是:update是QuerySet對象的方法,get返回的是一個model對象,它沒有update方法,而filter返回的是一個QuerySet對象(filter裏面的條件可能有多個條件符合,好比name='alvin',可能有兩個name='alvin'的行數據)。
在「插入和更新數據」小節中,咱們有提到模型的save()方法,這個方法會更新一行裏的全部列。 而某些狀況下,咱們只須要更新行裏的某幾列。
1、update方法直接設置對應的屬性
#---------------- update方法直接設定對應屬性---------------- models.Book.objects.filter(id=3).update(title="PHP") ##sql: ##UPDATE "app01_book" SET "title" = 'PHP' WHERE "app01_book"."id" = 3; args=('PHP', 3) 2、save方法會把全部屬性都從新設定一遍 #--------------- save方法會將全部屬性從新設定一遍,效率低----------- obj=models.Book.objects.filter(id=3)[0] obj.title="Python" obj.save()
一、先查出全部數據 # SELECT "app01_book"."id", "app01_book"."title", "app01_book"."price", # "app01_book"."color", "app01_book"."page_num", # "app01_book"."publisher_id" FROM "app01_book" WHERE "app01_book"."id" = 3 LIMIT 1; 二、把全部字段都更新一次 # UPDATE "app01_book" SET "title" = 'Python', "price" = 3333, "color" = 'red', "page_num" = 556, # "publisher_id" = 1 WHERE "app01_book"."id" = 3;
在這個例子裏咱們能夠看到Django的save()方法更新了不只僅是title列的值,還有更新了全部的列。 若title之外的列有可能會被其餘的進程所改動的狀況下,只更改title列顯然是更加明智的。更改某一指定的列,咱們能夠調用結果集(QuerySet)對象的update()方法,與之等同的SQL語句變得更高效,而且不會引發競態條件。
此外,update()方法對於任何結果集(QuerySet)均有效,這意味着你能夠同時更新多條記錄update()方法會返回一個整型數值,表示受影響的記錄條數。
注意,這裏由於update返回的是一個整形,因此無法用query屬性;對於每次建立一個對象,想顯示對應的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',
},
}
}
LOGGING
注意:若是是多對多的改:
obj=Book.objects.filter(id=1)[0]
author=Author.objects.filter(id__gt=2)
obj.author.clear()
obj.author.add(*author)
四、查
---------------------------------------查(filter,value等) -------------------------------------
# 查詢相關API:
# <1>filter(**kwargs): 它包含了與所給篩選條件相匹配的對象 # <2>all(): 查詢全部結果 # <3>get(**kwargs): 返回與所給篩選條件相匹配的對象,返回結果有且只有一個,若是符合篩選條件的對象超過一個或者沒有都會拋出錯誤。
#-----------下面的方法都是對查詢的結果再進行處理:好比 objects.filter.values()--------
# <4>values(*field): 返回一個ValueQuerySet——一個特殊的QuerySet,運行後獲得的並非一系列 model的實例化對象,而是一個可迭代的字典序列 # <5>exclude(**kwargs): 它包含了與所給篩選條件不匹配的對象 # <6>order_by(*field): 對查詢結果排序 # <7>reverse(): 對查詢結果反向排序 # <8>distinct(): 從返回結果中剔除重複紀錄 # <9>values_list(*field): 它與values()很是類似,它返回的是一個元組序列,values返回的是一個字典序列 # <10>count(): 返回數據庫中匹配查詢(QuerySet)的對象數量。 # <11>first(): 返回第一條記錄 # <12>last(): 返回最後一條記錄 # <13>exists(): 若是QuerySet包含數據,就返回True,不然返回False。
擴展:
#擴展查詢,有時候DJANGO的查詢API不能方便的設置查詢條件,提供了另外的擴展查詢方法extra:
#extra(select=None, where=None, params=None, tables=None,order_by=None, select_params=None
(1) Entry.objects.extra(select={'is_recent': "pub_date > '2006-01-01'"})
(2) Blog.objects.extra(
select=SortedDict([('a', '%s'), ('b', '%s')]),
select_params=('one', 'two'))
(3) q = Entry.objects.extra(select={'is_recent': "pub_date > '2006-01-01'"})
q = q.extra(order_by = ['-is_recent'])
(4) Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
所謂惰性機制:Publisher.objects.all()或者.filter()等都只是返回了一個QuerySet(查詢結果集對象),它並不會立刻執行sql,而是當調用QuerySet的時候才執行。其實就是迭代器。
一、QuerySet特色
<1> 可迭代的
<2> 可切片
#objs=models.Book.objects.all()#[obj1,obj2,ob3...]
#QuerySet: 可迭代
# for obj in objs:#每一obj就是一個行對象
# print("obj:",obj)
# QuerySet: 可切片
# print(objs[1])
# print(objs[1:4])
# print(objs[::-1])
二、QuerySet的高效使用
<1>Django的queryset是惰性的
Django的queryset對應於數據庫的若干記錄(row),經過可選的查詢來過濾。例如,下面的代碼會得
到數據庫中名字爲‘Dave’的全部的人:person_set = Person.objects.filter(first_name="Dave")
上面的代碼並無運行任何的數據庫查詢。你可使用person_set,給它加上一些過濾條件,或者將它傳給某個函數,
這些操做都不會發送給數據庫。這是對的,由於數據庫查詢是顯著影響web應用性能的因素之一。
<2>要真正從數據庫得到數據,你能夠遍歷queryset或者使用if queryset,總之你用到數據時就會執行sql.
爲了驗證這些,須要在settings里加入 LOGGING(驗證方式)
obj=models.Book.objects.filter(id=3)
# for i in obj:
# print(i)
# if obj:
# print("ok")
<3>queryset是具備cache的
當你遍歷queryset時,全部匹配的記錄會從數據庫獲取,而後轉換成Django的model。這被稱爲執行
(evaluation).這些model會保存在queryset內置的cache中,這樣若是你再次遍歷這個queryset,
你不須要重複運行通用的查詢。
obj=models.Book.objects.filter(id=3)
# for i in obj:
# print(i)
## models.Book.objects.filter(id=3).update(title="GO")
## obj_new=models.Book.objects.filter(id=3)
# for i in obj:
# print(i) #LOGGING只會打印一次
<4>簡單的使用if語句進行判斷也會徹底執行整個queryset而且把數據放入cache,雖然你並不須要這些數據!爲了不這個,能夠用exists()方法來檢查是否有數據:
obj = Book.objects.filter(id=4)
# exists()的檢查能夠避免數據放入queryset的cache。
if obj.exists():
print("hello world!")
<5>當queryset很是巨大時,cache會成爲問題
處理成千上萬的記錄時,將它們一次裝入內存是很浪費的。更糟糕的是,巨大的queryset可能會鎖住系統
進程,讓你的程序瀕臨崩潰。要避免在遍歷數據的同時產生queryset cache,可使用iterator()方法
來獲取數據,處理完數據就將其丟棄。
objs = Book.objects.all().iterator()
# iterator()能夠一次只從數據庫獲取少許數據,這樣能夠節省內存
for obj in objs:
print(obj.name)
#BUT,再次遍歷沒有打印,由於迭代器已經在上一次遍歷(next)到最後一次了,沒得遍歷了
for obj in objs:
print(obj.name)
#固然,使用iterator()方法來防止生成cache,意味着遍歷同一個queryset時會重複執行查詢。因此使
#用iterator()的時候要小心,確保你的代碼在操做一個大的queryset時沒有重複執行查詢
總結:
queryset的cache是用於減小程序對數據庫的查詢,在一般的使用下會保證只有在須要的時候纔會查詢數據庫。
使用exists()和iterator()方法能夠優化程序對內存的使用。不過,因爲它們並不會生成queryset cache,可能
會形成額外的數據庫查詢。
一、對象查詢,單表條件查詢,多表條件關聯查詢
#--------------------對象形式的查找--------------------------
# 正向查找
ret1=models.Book.objects.first()
print(ret1.title)
print(ret1.price)
print(ret1.publisher)
print(ret1.publisher.name) #由於一對多的關係因此ret1.publisher是一個對象,而不是一個queryset集合
# 反向查找
ret2=models.Publish.objects.last()
print(ret2.name)
print(ret2.city)
#如何拿到與它綁定的Book對象呢?
print(ret2.book_set.all()) #ret2.book_set是一個queryset集合
#---------------了不得的雙下劃線(__)之單表條件查詢----------------
# models.Tb1.objects.filter(id__lt=10, id__gt=1) # 獲取id大於1 且 小於10的值
#
# models.Tb1.objects.filter(id__in=[11, 22, 33]) # 獲取id等於十一、2二、33的數據
# models.Tb1.objects.exclude(id__in=[11, 22, 33]) # not in
#
# models.Tb1.objects.filter(name__contains="ven")
# models.Tb1.objects.filter(name__icontains="ven") # icontains大小寫不敏感
#
# models.Tb1.objects.filter(id__range=[1, 2]) # 範圍bettwen and
#
# startswith,istartswith, endswith, iendswith,
#----------------了不得的雙下劃線(__)之多表條件關聯查詢---------------
# 正向查找(條件)
# ret3=models.Book.objects.filter(title='Python').values('id')
# print(ret3)#[{'id': 1}]
#正向查找(條件)之一對多
ret4=models.Book.objects.filter(title='Python').values('publisher__city')
print(ret4) #[{'publisher__city': '北京'}]
#正向查找(條件)之多對多
ret5=models.Book.objects.filter(title='Python').values('author__name')
print(ret5)
ret6=models.Book.objects.filter(author__name="alex").values('title')
print(ret6)
#注意
#正向查找的publisher__city或者author__name中的publisher,author是book表中綁定的字段
#一對多和多對多在這裏用法沒區別
# 反向查找(條件)
#反向查找之一對多:
ret8=models.Publisher.objects.filter(book__title='Python').values('name')
print(ret8)#[{'name': '人大出版社'}] 注意,book__title中的book就是Publisher的關聯表名
ret9=models.Publisher.objects.filter(book__title='Python').values('book__authors')
print(ret9)#[{'book__authors': 1}, {'book__authors': 2}]
#反向查找之多對多:
ret10=models.Author.objects.filter(book__title='Python').values('name')
print(ret10)#[{'name': 'alex'}, {'name': 'alvin'}]
#注意
#正向查找的book__title中的book是表名Book
#一對多和多對多在這裏用法沒區別
注意:條件查詢即與對象查詢對應,是指在filter,values等方法中的經過__來明確查詢條件
二、聚合查詢和分組查詢
<1> aggregate(*args,**kwargs):
經過對QuerySet進行計算,返回一個聚合值的字典。aggregate()中每個參數都指定一個包含在字典中的返回值。即在查詢集上生成聚合。(對QuerySet總體進行計算,獲得聚合函數的值的字典)
from django.db.models import Avg,Min,Sum,Max
從整個查詢集生成統計值。好比,你想要計算全部在售書的平均價錢。Django的查詢語法提供了一種方式描述全部
圖書的集合。
>>> Book.objects.all().aggregate(Avg('price'))
{'price__avg': 34.35}
aggregate()子句的參數描述了咱們想要計算的聚合值,在這個例子中,是Book模型中price字段的平均值
aggregate()是QuerySet 的一個終止子句,意思是說,它返回一個包含一些鍵值對的字典。鍵的名稱是聚合值的
標識符,值是計算出來的聚合值。鍵的名稱是按照字段和聚合函數的名稱自動生成出來的。若是你想要爲聚合值指定
一個名稱,能夠向聚合子句提供它:
>>> Book.objects.aggregate(average_price=Avg('price'))
{'average_price': 34.35}
若是你也想知道全部圖書價格的最大值和最小值,能夠這樣查詢:
>>> Book.objects.aggregate(Avg('price'), Max('price'), Min('price'))
{'price__avg': 34.35, 'price__max': Decimal('81.20'), 'price__min': Decimal('12.99')}
<2> annotate(*args,**kwargs)
能夠經過計算查詢結果中每個對象所關聯的對象集合,從而得出總計值(也能夠是平均值或總和),即爲查詢集的每一項生成聚合。(對查詢結果按條件分組後的集合進行計算,返回每一個分組的字典集合)
>>> Place.objects.filter(name='place1').aggregate(Sum('id'))
(0.001) SELECT SUM("blog_place"."id") AS "id__sum" FROM "blog_place" WHERE "blog _place"."name" = 'place1'; args=('place1',)
{'id__sum': 5}
>>>
查詢每一個地址的id總和,這裏就涉及到分組了(固然,這裏不是很合理,通常是某個name對應多個id值時,才使用相加),分組條件是name
>>> Place.objects.values("name").annotate(Sum('id'));
(0.001) SELECT "blog_place"."name", SUM("blog_place"."id") AS "id__sum" FROM "bl og_place" GROUP BY "blog_place"."name" LIMIT 21; args=()
<QuerySet [{'name': u'hourse1', 'id__sum': 7}, {'name': u'hourse2', 'id__sum': 8
}, {'name': u'hourse3', 'id__sum': 9}, {'name': u'place1', 'id__sum': 5}, {'name
': u'place2', 'id__sum': 6}]>
>>>
查詢每一個place,最小的id
>>> Place.objects.values('name').annotate(Min('id'));
(0.001) SELECT "blog_place"."name", MIN("blog_place"."id") AS "id__min" FROM "bl og_place" GROUP BY "blog_place"."name" LIMIT 21; args=()
<QuerySet [{'id__min': 7, 'name': u'hourse1'}, {'id__min': 8, 'name': u'hourse2'
}, {'id__min': 9, 'name': u'hourse3'}, {'id__min': 5, 'name': u'place1'}, {'id__
min': 6, 'name': u'place2'}]>
>>>
注意:這裏是由於settings裏面設置了Logging選項,因此你會看到每次查詢的raw SQL。
三、F查詢和Q查詢
僅僅靠單一的關鍵字參數查詢已經很難知足查詢要求。此時Django爲咱們提供了F和Q查詢。
# F 使用查詢條件的值,專門取對象中某列值的操做(每本書的價格提升20元)
# from django.db.models import F
# models.Tb1.objects.update(num=F('num')+1)
# Q 構建搜索條件(與、或、非、條件,很是靈活!!!)
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>]
# 二、能夠組合使用&,|操做符,當一個操做符是用於兩個Q的對象,它產生一個新的Q對象。
Q(title__startswith='P') | Q(title__startswith='J')
# 三、Q對象能夠用~操做符放在前面表示否認,也可容許否認與不否認形式的組合
Q(title__startswith='P') | ~Q(pub_date__year=2005)
# 四、應用範圍:
# 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))
)
#sql:
# SELECT * from polls WHERE question LIKE 'P%'
# AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06')
# import datetime
# e=datetime.date(2005,5,6) #2005-05-06
# 五、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)))
7、raw sql
django中models的操做,也是調用了ORM框架來實現的,pymysql 或者mysqldb,因此咱們也可使用原生的SQL語句來操做數據庫!