一般情況下,我們在寫Django項目需要操作QuerySet時一些常用的方法已經滿足我們日常大多數需求,比如get、filter、exclude、delete神馬的感覺就已經無所不能了,但隨着項目但業務邏輯越來越復雜,這幾個方法可能就不能很好但滿足我們了,所以這時候,最好的辦法是神馬??對,讀文檔!這里的讀文檔不是有業務需求時去查文檔,而是要為了閱讀文檔而閱讀文檔。以下也是作為我的文檔閱讀筆記,我記下了一些我以后可能會用到或者一些技巧性提升的東西,好,不廢話,正文開始:
首先,我們假設有以下兩個model:
class Entry(Model.models): ip = models.CharField(max_length=20) time = models.DateTimeField() black = models.BooleanField(default=False) class Blog(Model.models): title = models.CharField(max_length=100) content = models.CharField(max_length=500) publish = models.BooleanField(default=False) entry = models.ForeignField(Entry, relate_name='entrys')
1、annotate(args, **kwargs)
為queryset增加注解,神馬是注解?就是你讀出queryset可能會需要一些額外數據要添加進去的時候,你就可以用這個東東咯,使用方法看代碼:
>>> q = Blog.objects.annotate(Count('entry')) # The name of the first blog >>> q[0].name 'Blogasaurus' # The number of entries on the first blog >>> q[0].entry__count 42
Blog model 類本身並沒有定義 entry__count 屬性,但可以使用注解函式的關系字參數,從而改變注解的命名:
>>> q = Blog.objects.annotate(number_of_entries=Count('entry')) # The number of entries on the first blog, using the name provided >>> q[0].number_of_entries 42
2、aggregate(args, *kwargs)
這個參數有點像annotate的反義,annotate返回的是一個包含注解值的queryset,而aggregate則單獨返回注解值,返回類型是一個dict,當然,這種方式在文檔中叫做聚合查詢,具體使用如下:
>>> q = Blog.objects.aggregate(Count('entry')) {'entry__count': 16}
通過在 aggregate 指定關鍵字參數,你可以控制返回的聚合名稱:
>>> q = Blog.objects.aggregate(number_of_entries=Count('entry')) {'number_of_entries': 16}
3、defer(*fields)
延后讀取字段。啥意思?我讀文檔時就這感覺。。后來發現是醬紫滴,一個復雜滴model可能你從數據庫中讀出后根本不需要某些字段,讀了又浪費時間浪費空間,怎么辦?對!用defer,延后讀取,你可以在defer中指定一個或多個字段,也可用鏈式方法使用defer,它返回對依然是個完整對queryset但其中defer指定但字段並沒有真但從數據庫讀出來,只有當你訪問這些延后字段時django才會從數據庫讀取這些數據,感覺在數據量變大后用這個方法很nice,具體用法如下:
Blog.objects.defer("content").filter(publish=True).defer("title")
不過要注意的是,不能用defer過的字段進行order_by操作,這樣做木有作用滴,如果需要清楚defer,只要加個defer(None)
就ok啦。
你還闊以defer model中的外鍵,但是你需要提使用 select_related() 載入關聯 model,具體用法:
Blog.objects.select_related().defer("entry__ip", "entry__time")
4、only(*fields)
我想你已經猜到了,一定是defer相反的咯,是啊是啊,沒錯。only會立即查詢指定的字段,但是要注意了,這有坑,only只返回指定的字段,其他木有指定的默認就給defer了喲,所以以下寫法是等價滴:
Entry.objects.only('ip') Entry.objects.defer('time', 'black')
當你使用鏈式方法調用only時只有最后一個only內的參數會立即返回,其他參數都會被defer,注意這里only的覆蓋性~
5、create(**kwargs)
創建並保存對象。一般我們要新建一個model對象時直接使用他的構造函數或者使用.
語法賦值,最后調用.save()
方法保存。那么在我們已經知道新建這個對象所有必須數據的情況下,其實用create會更快捷,代碼看着更干凈,起使用方法與構造方法類似,只是不需要調用.save()
啦, 例子如下:
p = Entry.objects.create(ip='127.0.0.1', time=<a datetime type object>, black=False)
6、get_or_create(kwargs) 和 update_or_create(kwargs)
嗯,看看就知道這個是create
的升級版,沒錯,他們倆一個是在查無此數據后新建一個是更新不存在數據時新建,具體用法同create
,get_or_create
等效如下過程
try: obj = Blog.objects.get(title='test', content='test') except Blog.DoesNotExist: obj = Blog(title='test', content='test') obj.save()
注意這兩個方法的返回值,他們返回兩個東東:
created, obj = get_or_create(**kwargs)
其中created
是個bool值,當此方法生成了一個新的model object,此值為True,反之為False,obj
則是生成的object或者查到的object實例。
7、latest(field_name=None) 和 earliest(field_name=None)
分別返回指定字段的最新數據與最早數據。
8、first() 和 last()
分別返回queryset的第一項與最后一項,具體用法如下:
p = Blog.objects.order_by('title').first()
等同於:
try: p = Blog.objects.order_by('title')[0] except IndexError: p = None
9、update(**kwargs)
用於更新一組數據,但要注意,它不能更新外鍵, 不能更新切片過的queryset以及不能再被切片的set,用法如下:
Entry.objects.filter(black=False).update(ip='0.0.0.0')
10、delete()
有人肯定要說了,博主你再逗我,這個方法抬頭不見低頭見,還用你說?!是啊是啊,刪除普通數據的時候當然木有什么,但是如果刪除外健關系很復雜的object時有木有想過細節?是不是細思極恐 啊#_#
比如,以我們開頭的model為例,我刪了一個entry實例,那么與它有外健關聯的blog實例會怎樣?一同被刪了?還是保留?保留的話那他對應的entry外健是神馬?WTF!
嗯,實話告訴你,默認情況下調用delete()
是會刪除所有有關的外鍵對象的(是不是突然感覺自己之前代碼里有坑了)所以我們需要詳細說說這個方法,如何做才能讓他不刪除對應的外鍵或者說按照我們想象的方式進行刪除呢?
答案在這里:
django.models 的 on_delete
參數,此參數有以下幾個可選值:
- CASCADE:這就是delete()的默認選項,也就是關聯刪除
- PROTECT:如果刪除的model obj含有外鍵則引起 ProtectedError
- SET_NULL:就是把外鍵置空咯,當然前提是你得設置外鍵的
null=True
- SET_DEFAULT:就是把外鍵設為默認咯,當然前提是你得設置外鍵的
default=xxx
-
SET():SET內應是一個函數,用來返回一個外鍵實例,用法如下:
def get_sentinel_user(): return get_user_model().objects.get_or_create(username='deleted')[0] class MyModel(models.Model): user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET(get_sentinel_user))
11、fields lookups
強大滴django fields lookups,具體可選參數有:
iexact icontains in istartwith gt gte lt lte endwith iendwith range
year month day week_day hour minute second insole (ex: search) iregex
其中,首字母帶“i”的意思就是不分大小寫,如果需要大小寫敏感就把“i”去掉啦~其他參數大體從字面意思就知道啦,改天補上詳細的例子。
12、Avg、Count、Sum、Max、Min、StdDev、Variance
這些方法就是求數據的相應結果咯,比如avg就是平均值啦,嗯,基本都看得懂,除了后兩個,一個是方差,一個是標准差,具體用法其實前文里有,我還是放一下:
q = Blog.objects.annotate(Count('entry'))
13、強大的Q查詢與F查詢:
嗯,這一部分先留着,總之告訴你很膩害就是了,可以做很復雜的查詢,先放個例子:
q = Blog.objects.filter(Q(title='test')|Q(content='hahaha'))
這是一個或查詢,滿足其中一個條件的數據會被返回。
參考