Django model 層之Making Query總結
by:授客 QQ:1033553122
實踐環境
Python版本:python-3.4.0.amd64
下載地址:https://www.python.org/downloads/release/python-340/
Win7 64位
Django 1.11.4
下載地址:https://www.djangoproject.com/download/
API互動
MySQL資料庫為例,假設專案目錄結構如下:
mysite/
myapp/
__init__.py
admin.py
apps.py
migrations/
__init__.py
models.py
tests.py
views.py
manage.py
mysite/
__init__.py
settings.py
urls.py
wsgi.py
models.py內容如下:
from django.db import models
# Create your models
here.
class Person(models.Model):
first_name =
models.CharField(max_length=30)
last_name =
models.CharField(max_length=30)
class Book(models.Model):
book_name =
models.CharField(max_length=30)
borrower = models.ForeignKey(Person,
to_field='id', on_delete=models.CASCADE)
class Blog(Book):
author = models.CharField(max_length=50)
class Store(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=50)
last_update = models.DateField(auto_now=True)
class Fruit(models.Model):
store = models.ManyToManyField(Store)
name = models.CharField(max_length=100)
onsale_date = models.DateField()
class News(models.Model):
title = models.CharField(max_length=20)
n_comments = models.IntegerField()
n_pingbacks = models.IntegerField()
進入到專案根目錄下,運行python
cd /d F:\project\Django\MyProjects\mysite\
python
>>> import os
>>> os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")
'mysite.settings'
>>>
說明:
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project_name.settings")
添加以上代碼避免出現以下問題:
django.core.exceptions.ImproperlyConfigured: Requested setting DEFAULT_INDEX_TABLESPACE, but settings are not configured. You must either define the environment variable DJANGO_SETTINGS_MODULE or call settings.configure() before accessing
>>> import django
>>> django.setup()
說明:
添加以上代碼,避免出現以下問題
……
raise AppRegistryNotReady("Apps aren't loaded yet.")
django.core.exceptions.AppRegistryNotReady: Apps aren't loaded yet.
創建物件
>>> from myapp.models import Person
說明:
from app_name.models import module
python當前目錄為專案根目錄下,models位于app目錄下,所以如上 app_name.models
>>> person = Person(first_name="ke", last_name="shou")
>>> person.save()
執行save()方法時,等同于執行以下sql INSERT陳述句,
INSERT INTO `myapp_person` (`id`, `first_name`, `last_name`) values('1','ke','shou');
說明:創建物件時也可以使用字典解引的方式,如下:
>>> fields_dict = {"first_name":"ke", "last_name":"shou"}
>>> person = Person(**fields_dict)
創建包含外鍵field的物件
>>> from myapp.models import Person,Book
方式1:創建物件時,外鍵Field名稱要寫為“外鍵Field名稱_id”的形式
>>> book1 = Book(borrower_id='1', book_name='mybook')
>>> book1.save()
注意:如果,外鍵Field名稱直接使用所在模塊類中的定義的類屬性名稱,直接設定值為外鍵Field的值,則會提示外鍵Field的值必須為外鍵Field所關聯類的實體,如下:
>>> book1 = Book(borrower='1', book_name='mybook')
……
self.field.remote_field.model._meta.object_name,
ValueError: Cannot assign "'1'": "Book.borrower" must be a "Person" instance.
方式2:解決以上依賴問題
>>> person2 = Person(first_name="ku", last_name="shou")
>>> person2.save()
>>> book2 = Book(borrower=person2, book_name='mybook2')
>>> book2.save()
創建包含多對多Field的物件
針對多對多Field,除了建立model對應的表外,django還會新增一張關聯表:app名稱_包含ManyToManyField型別Field的model名稱_ManyToManyField型別Field指定的model名稱(例中myapp_fruit_store),存放兩個model對應的表對應記錄的id
>>> from myapp.models import Store,Fruit
>>> amy_store = Store.objects.create(name='aimi')
SELECT * FROM `myapp_store` WHERE `name` = 'aimi';
顯示:
id name last_update
7 aimi 2018-03-25
說明:以create方式建立物件,無需執行obj.save()函式就會在表新增對應的記錄,如上,如果添加,amy_store.save()代碼,則會在此基礎上,再新增一條記錄,內容和amy_store物件的一樣,
注意:針對多對多,不能按以下方式創建物件,會報錯:
>>> apple = Fruit(name='apple',onsale_date='2018-03-24')
>>> apple.save()
SELECT * FROM `myapp_fruit` WHERE `name` = 'apple';
顯示:
id name onsale_date
3 apple 2018-03-24
注意:呼叫add之前,必須保證物件自身已存在,即已創建,否則會報類似如下的錯誤:
ValueError: "<Fruit: Fruit object>" needs to have a value for field "id" before this many-to-many relationship can be used.
由此可見,我們不能通過類似以下陳述句,一次性創建包含ManyToManyField型別Field的物件
banana = Fruit(store=aimi_store,name='banana',
onsale_date='2018-03-24')
banana.save()
到目前為止,關聯表myapp_fruit_store里還是沒有記錄
>>> apple.add(aimi_store)
SELECT * FROM `myapp_fruit_store`
顯示:
id fruit_id store_id
1 3 7
修改物件
person.last_name="yu"
person.save()
執行save()方法時,等同于執行以下sql UPDATE陳述句,
UPDATE `myapp_person` SET last_name ='yu' where id =1;
修改包含外鍵的物件
清空Person,Book moduel關聯的表資料后,執行以下操作
例:
>>> person = Person(first_name='ke', last_name="shou")
>>> person.save()
>>> book = Book(book_name="yueding", borrower=person)
>>> book.save()
SELECT * FROM myapp_person
顯示:
id first_name last_name
1 ke shou
SELECT * FROM myapp_book
顯示:
id book_name borrower_id
1 yueding 1
>>> person = Person(first_name='yu', last_name='lai')
>>> person.save()
>>> book.borrower = person
>>> book.save()
SELECT * FROM myapp_person
顯示:
id first_name last_name
1 ke shou
2 yu lai
SELECT * FROM myapp_book
顯示:
id book_name borrower_id
1 yueding 2
修改包含多對多Field的物件
add
增加model物件到關聯物件,即在model對應表中增加關聯記錄
>>> ximi_store = Store.objects.create(name='ximi')
>>> apple = Fruit.objects.get(id=3)
>>> apple.store.add(ximi_store)
SELECT * FROM `myapp_fruit_store`
顯示:
id fruit_id store_id
1 3 7
2 3 8
結果如上,新增一條關聯記錄,關聯新增的ximi_store
當然,我們也可以一次性關聯多個物件
>>> xima_store = Store.objects.create(name='xima')
>>> masu_store = Store.objects.create(name='masu')
>>> apple.store.add(xima_store, masu_store)
SELECT * FROM `myapp_fruit_store`
顯示:
id fruit_id store_id
1 3 7
2 3 8
3 3 9
4 3 10
remove
從已關聯物件中,洗掉指定關聯物件
承接add
>>> apple.store.remove(masu_store)
SELECT * FROM `myapp_fruit_store`
顯示:
id fruit_id store_id
1 3 7
2 3 8
3 3 9
注意:上面是基于“add”中操作的,如果還沒獲取物件,則需要通過類似 model_name.objects.get(id=1)的方式,先獲取物件,然后操作物件,
set
注:以下基于remove之后的操作
>>> gami_store = Store.objects.create(name='gami')
>>> gama_store = Store.objects.create(name='gama')
>>> new_list = [gami_store, gama_store]
>>> apple.store.set(new_list)
SELECT * FROM `myapp_store`
顯示:
id name last_update
7 aimi 2018-03-25
8 ximi 2018-03-25
9 xima 2018-03-25
10 masu 2018-03-25
11 gami 2018-03-25
12 gama 2018-03-25
SELECT * FROM `myapp_fruit_store`
顯示:
id fruit_id store_id
6 3 11
5 3 12
如上,關聯的物件,全部被替換為最新的了
clear
清空所有已關聯物件
>>> apple.store.clear()
結果myapp_fruit_store表中的記錄全部被清空了,
參考鏈接:
https://docs.djangoproject.com/en/1.11/ref/models/relations/#django.db.models.fields.related.RelatedManager.add
檢索物件
檢索所有物件
例:
>>> person = Person(first_name="ke", last_name="shou")
>>> person.save()
>>> all_entries = Person.objects.all()
>>> all_entries
<QuerySet [<Person: Person object>]>
>>> print(all_entries.query) # 查看獲取結果集執行對sql陳述句
說明:
all()方法會回傳資料庫表中所有記錄的結果集,等同于以下sql SELECT 陳述句
SELECT * FROM `myapp_person`
另外,我們可以通過list(QueryResultSet)把QuerySet型別的查詢結果轉為串列型別
通過過濾器檢索特定物件
例:
>>> entries = Person.objects.filter(first_name='ke')
說明:
等同于
>>> entries = Person.objects.all().filter(first_name='ke')
執行以上filter方法,等同于執行以下SQL方法
SELECT * FROM `myapp_person` WHERE first_name = 'ke'
>>> entries = Person.objects.exclude(last_name='yu')
執行以上exclude方法,等同于執行以下SQL方法
SELECT * FROM `myapp_person` WHERE first_name != 'yu'
鏈式過濾器
例:
>>> Person.objects.filter(first_name='ke').filter(last_name='shou')
說明:先過濾出first_name值為ke的,然后再從這里面篩選出last_name為 shou的,
等同于
>>> Person.objects.filter(first_name='ke', last_name='shou')
>>> Person.objects.filter(first_name='ke').exclude(last_name='shou')
說明:先過濾出first_name值為ke的,然后再從這里面篩選出last_name不為 shou的
當然,也可以拆分成多條陳述句
>>> q1 = Person.objects.filter(first_name='ke')
>>> q2 = q1.exclude(last_name='shou')
>>> print(q2)
注意:只有真正用到查詢結果時,才會執行資料庫操作,比如上述代碼,只有執行最后一行,print(q2)時才會真正去資料庫查詢,
注意:exclude并不是filter的完全實作,如下:
SELECT * FROM `myapp_person`
顯示:
id first_name last_name
1 ke shou
2 yu lai
3 ke lai
4 yu laiyu
SELECT * FROM `myapp_book`
顯示:
id book_name borrower_id
1 yueding 2
2 duzhe 1
3 zhazhi 2
4 shenghuo 3
5 qinglv 4
獲取myapp_person表記錄,排除在myapp_book表中存在外鍵參考,且對應記錄book_name為yueding, id=1的,
>>> persons = Person.objects.exclude(book__book_name='yueding', book__id=1)
>>> for person in persons:
... print(person.first_name, person.last_name, person.id)
...
ke shou 1
ke lai 3
yu laiyu 4
注意:
1、“反向查找”,條件左側運算式填寫格式:model名稱__field名稱
2、“正向查找”,不能按以上方式查找,比如以下,會報錯:
>>> books = Book.objects.exclude(Person__first_name='ke', Person__last_name='shou')
反向查找和正向查找是我個人定義,定義不太好表達,具體參考以上例子,
如果結合filter方法來實作,則可采用“嵌套”的方式
>>> persons = Person.objects.exclude(book__in=Book.objects.filter(book_name='yueding', id=1))
>>> for person in persons:
... print(person.first_name, person.last_name, person.id)
...
ke shou 1
ke lai 3
yu laiyu 4
>>>
檢索單個物件
例:
>>> only_one_entry = Person.objects.get(id=1)
注意:
如果沒有匹配的查詢結果,則拋出DoesNotExist例外,如果查詢結果多余一個物件,則拋出 MultipleObjectsReturned,對比之下,如果查詢不到結果,filter則回傳空結果集,不會報錯,
更多檢索方法參考API:
https://docs.djangoproject.com/en/2.0/ref/models/querysets/#django.db.models.query.QuerySet
主鍵快捷查找
Django提供pk(主鍵)查找快捷方式,
SELECT * FROM `myapp_store`
id name last_update
7 aimi 2018-03-25
8 ximi 2018-03-25
9 xima 2018-03-25
10 masu 2018-03-25
11 gami 2018-03-25
12 gama 2018-03-25
其中id為表的主鍵
檢索主鍵id為7的物件
>>> Store.objects.get(id__exact=7)
>>> Store.objects.get(id=7)
>>> Store.objects.get(pk=7)
檢索主鍵id為7,8,9的物件
>>> Store.objects.filter(pk__in=[7, 8, 9])
<QuerySet [<Store: Store object>, <Store: Store object>, <Store: Store object>]>
同樣的,pk查找也支持聯合查詢,
>>> from myapp.models import Fruit
>>> f = Fruit.objects.get(id=3)
>>> f.store.add(Store.objects.get(id=7))
檢索myapp_fruit表中,同myapp_store表記錄存在多對多關聯關系,且myapp_store.id主鍵值為7的記錄,
>>> Fruit.objects.filter(store__id=7)
<QuerySet [<Fruit: Fruit object>]>
>>> Fruit.objects.filter(store__pk=7)
<QuerySet [<Fruit: Fruit object>]>
>>> Fruit.objects.filter(store__id__exact=7)
<QuerySet [<Fruit: Fruit object>]>
注意雙下線的書寫,
限制查詢結果集
例:回傳檢索結果的前兩個物件
>>> Person.objects.all()[:2]
等同于SELECT * FROM `myapp_person` LIMIT 2
例:回傳從第2條開始的物件,一共回傳1個物件,
>>> Person.objects.all()[1:2]
等同于SELECT * FROM `myapp_person` LIMIT 1, 1
注意:
1、不支持復數索引,比如 Person.objects.all()[-1]
2、對查詢結果“切片”,會回傳一個新的結果集,并不會重新計算查詢,但是如果切片使用了步長值,則每次取值都會重新執行查詢,為了按步長值回傳串列,例:第一個物件開始,每隔一個物件取一次,直到索引為10停止,
>>> Person.objects.all()[:10:2]
對于接收單個物件,我們也可以這么做,先排序,然后取第一個物件,
>>> Person.objects.order_by('id')[0]
等同于
>>> Person.objects.order_by('id')[0:1].get()
注意:使用get()如果沒有匹配記錄,則會拋出DoesNotExist 例外,
欄位查詢
基本的查詢關鍵詞,格式形如:field__
lookuptype=value ,可用于filter,exclude,get等函式,注意,雙下劃線,
常用欄位查詢
lte 小于等于
gte 大于等于
gt 大于
lt 小于
exact 精確查詢
iexact 類似exact, 不同之處在于大小寫不敏感
contains 包含,模糊查詢,大小寫敏感,
startswith 僅匹配開頭
endswith 僅匹配結尾
istartswith 僅匹配開頭,類似startswith,不同之處在于大小寫不敏感
iendswith 僅匹配結尾,類似endswith,不同之處在于大小寫不敏感
例子:查詢id小于等于 1的物件,
>>> Person.objects.filter(id__lte = 1)
等同于
SELECT * FROM `myapp_person` WHERE id <=1;
>>> Person(first_name='yu', last_name='lai').save()
>>> Person(first_name='yu', last_name='laiyu').save()
例:查找last_name值為laiyu的物件
>>> Person.objects.get(last_name__exact='laiyu')
等效做法:
>>> Person.objects.get(last_name='laiyu')
等同于SELECT * FROM `myapp_person` WHERE last_name='laiyu';
例:檢索last_name包含lai的物件
>>> Person.objects.filter(last_name__contains='lai')
等同于
SELECT * FROM `myapp_person` WHERE last_name LIKE '%lai%'
例:查詢last_name以lai開頭的物件
>>> Person.objects.filter(last_name__startswith='lai')
等同于
SELECT * FROM `myapp_person` WHERE last_name LIKE '%lai'
例:查詢last_name以yu結尾的物件
>>> Person.objects.filter(last_name__endswith='lai')
等同于
SELECT * FROM `myapp_person` WHERE last_name LIKE 'lai%'
更多欄位查詢參考API
https://docs.djangoproject.com/en/2.0/ref/models/querysets/#field-lookups
F運算式和Filter的配合使用
可結合Filter,用F運算式,對同一model的不同Field做檢索
SELECT * FROM `myapp_news`
id title n_comments n_pingbacks rank
1 news1 20 40 34
2 news2 35 30 25
3 news3 14 20 34
檢索myapp_news表,查找n_comments值大于n_pingbacks值的記錄
>>> from myapp.models import News
>>> from django.db.models import F
>>> mynews = News.objects.filter(n_comments__gt=F('n_pingbacks'))
>>> for news in mynews:
... print(news.title)
...
news2
檢索myapp_news表,查找n_pingbacks值大于等于n_comments值2倍的記錄
>>> mynews = News.objects.filter(n_pingbacks__gte=F('n_comments') * 2)
>>> for news in mynews:
... print(news.title)
...
news1
檢索myapp_news表,查找rank值等于n_comments+n_pingbacks的記錄
>>> mynews = News.objects.filter(rank=F('n_comments') + F('n_pingbacks'))
>>> for news in mynews:
... print(news.title)
...
news3
對于date/time field,還可以加減timedlta物件
>>> from datetime import timedelta
>>> Entry.objects.filter(mod_date__gt=F('pub_date') + timedelta(days=3))
更多參考鏈接:
https://docs.djangoproject.com/en/1.11/topics/db/queries/#filters-can-reference-fields-on-the-model
轉義字符%和_
iexact, contains, icontains, startswith, istartswith, endswith 和iendswith這些類似SQL LIKE陳述句的欄位查找,會自動化轉義 % 和 _ 這兩種字符,
例:
>>> Book.objects.filter(book_name__contains = '%')
等同SELECT * FROM myapp_book WHERE book_name LIKE '%\%%';
快取和查詢
Django會快取查詢結果集,可適當利用這個特征來加速查詢,減輕系統壓力,
例子:
>>> from myapp.models import Book
>>> print([b.book_name for b in Book.objects.all()])
>>> print([b.borrower for b in Book.objects.all()])
如果按以上方式書寫,會去資料庫查詢兩次,而且兩次的查詢還可能不一樣,因為有可能新增了資料、洗掉了資料,為了避免這個問題,簡單的做法是保存查詢結果集,并重用它,優化方案如下:
# 計算查詢結果集
>>> print([b.book_name for b in queryset])
# 重用快取的結果集
>>> print([b.borrower for b in queryset])
不使用快取的查詢
當僅查詢部分查詢結果集時,會檢查快取,但是如果結果集還沒被填充(個人理解,還沒執行資料庫查詢,并沒有真正回傳整個查詢結果集存放至定義的查詢結果集變數),后續子查詢回傳的結果項將不會被快取,這也就意味著如果使用陣列切片,或者索引的方式限制回傳結果集,將不使用快取,(Querysets do not always cache their results. When evaluating only part of the queryset, the cache is checked, but if it is not populated then the items returned by the subsequent query are not cached. Specifically, this means that limiting the queryset using an array slice or an index will not populate the cache.)
例如以下,重復獲取查詢結果集中索引值為4的物件
>>> queryset = Book.objects.all()
>>> print(queryset[4]) # 查詢資料庫
>>> print(queryset[4]) # 不使用快取,再次查詢資料庫,
>>> queryset = Book.objects.all()
>>> [book for book in queryset] # 查詢資料庫
>>> print(queryset[4]) # 使用快取
>>> print(queryset[4]) # 使用快取
其它一些會導致查詢整個結果集并快取的行為舉例
>>> [entry for entry in queryset]
>>> bool(queryset)
>>> entry in queryset
>>> list(queryset)
注意:單純的print結果集,并不會填充快取,因為呼叫__repr__()僅回傳整個查詢結果集的切片(Simply printing the queryset will not populate the cache. This is because the call to __repr__() only returns a slice of the entire queryset)
使用Q物件執行更復雜的查找
可以使用Q實作OR的關系
例子:查找myapp_book表中,book_name為yueding或者duzhe的記錄
>>> from django.db.models import Q
>>> Book.objects.filter(Q(book_name='yueding') | Q(book_name='duzhe'))
<QuerySet [<Book: Book object>, <Book: Book object>]>
等價于:
SELECT * FROM myapp_book WHERE boo_name = 'yueding' or book_name='duzhe'
使用~Q實作NOT查詢
例:查詢myapp_book表中,book_name為yueding,或者 book_name不為duzhe的記錄
>>> Book.objects.filter(Q(book_name='yueding') | ~Q(book_name='duzhe'))
每個接收關鍵詞引數的查詢函式(filter,exclude,get等)都可以傳遞1個或多個Q物件,作為位置引數,如果提供了多個Q物件引數給查詢函式,這些引數之間將是 AND 關系,
>>> Book.objects.filter(Q(borrower_id=2), Q(book_name='duzhe') | Q(book_name='yueding'))
等同于執行SQL
SELECT * FROM myapp_book WHERE borrower_id = 2 AND (book_name = 'duzhe' OR book_name = 'yueding')
可以混用關鍵詞引數,和Q物件,他們之間為 AND 關系,但是關鍵詞引數必須位于位置引數后面,
>>> Book.objects.get(Q(book_name='duzhe') | Q(book_name='yueding'), borrower_id=2)
<Book: Book object>
比較物件
可使用標準python比較符 == 比較同一個model的兩個實體,實質是比較實體的主鍵值,
例:
>>> one_book = Book.objects.get(book_name='yueding')
>>> anther_book = Book.objects.get(id=1)
>>> one_book == anther_book
True
等價比較法:
>>> one_book.id == anther_book.id
True
假設主鍵不是id,比如說是name呢,如下
>>> one_book.name == anther_book.name
洗掉物件
delete(),執行該方法,洗掉物件并回傳被洗掉物件的數量及每種物件型別的被洗掉的數量(使用字典方式表示)
例:洗掉myapp_book表中,book_name為qinglv的記錄
>>> one_book = Book.objects.get(book_name='qinglv')
>>> one_book.delete()
(1, {'myapp.Book': 1})
批量洗掉
每個查詢結果集都有一個delete()方法,洗掉QuerySet的所有成員
例:洗掉myapp_book表中,所有borrower_id為2的記錄
>>> Book.objects.filter(borrower_id=2).delete()
(2, {'myapp.Book': 2})
假如想洗掉所有物件,可參照如下陳述句
>>> Book.objects.all().delete()
備注:即便通過filter檢索出來的結果為空,呼叫delete函式也不會報錯,假如borrower_id=2的記錄不存在,執行delete函式也不會報錯,
復制model實體
例:
>>> book = Book(book_name="test1", borrower_id = 2)
>>> book.save() # 新增第一條記錄
>>> book.pk = None # 設定主鍵值為None
>>> book.save() #執行save方法時,會新增第二條記錄,主建值自動加1,其它內容和第一條保持一樣
如果使用了繼承,則如果想想復制繼承類的實體,則復制之前需要設定主鍵列及pk為None
>>> from myapp.models import Book,Blog
>>> borrower = Person.objects.get(id=1)
>>> dj_blog = Blog(book_name='mybook', borrower=borrower, author='laiyu')
>>> dj_blog.save()
>>> dj_blog.id = None
>>> dj_blog.pk = None
>>> dj_blog.save()
SELECT * FROM myapp_book WHERE book_name = 'mybook'
顯示:
id book_name borrower_id
8 mybook 1
9 mybook 1
SELECT * FROM myapp_blog
顯示:
book_ptr_id author
8 laiyu
9 laiyu
注意,繼承類實體的資料存盤,繼承欄位的值是存在基類對應的資料表中的,
這種處理方式并不會復制不屬是model資料庫表部分的關系,比如Entry model有一個指向Author的ManyToManyField,在復制entry后,需要為新的entry設定多對多的關系,
entry = Entry.objects.all()[0] # 之前創建的某個entry
old_authors = entry.authors.all()
entry.pk = None
entry.save()
entry.authors.set(old_authors)
對于一對一關系,必須復制關聯物件,并賦值給對新物件的field,以面違反一對一唯一性約束,
例,假設entry已經被復制了,
detail = EntryDetail.objects.all()[0]
detail.pk = None
detail.entry = entry
detail.save()
批量更新多個物件
使用update()方法,一次性批量更新查詢結果集中物件的某個field值,
例:查找myapp_news表中,id大于1的記錄,并把這些記錄的rank欄位值,統一+1
>>> from django.db.models import F
>>> News.objects.filter(id__gt=1).update(rank=F('rank') + 1)
當然,我們也可以一次性更新多個欄位,欄位之間用逗號分隔,如下,同時更新title和rank
>>> News.objects.filter(id__gt=1).update(title='newTitle', rank=F('rank') + 1)
update引數也可以寫成字典的形式
>>> data = https://www.cnblogs.com/shouke/p/{'title': 'newTitle',' rank:'0'}
>>> News.objects.filter(id__gt=1).update(**data)
2
注意:僅可設定和其它表不存在關聯關系的field,如果是更新無關聯Field,只需要提供新值作為常量值即可,但是如果要更新外鍵Field,則需要提供model實體作為新值,(You can only set non-relation fields and ForeignKey fields using this method. To update a non-relation field, provide the new value as a constant. To update ForeignKey fields, set the new value to be the new model instance you want to point to.)
例:
設定myapp_book表,所有記錄的外鍵borrower為shouke
>>> borrower=Person.objects.filter(last_name='shou', first_name='ke')[0]
>>> Book.objects.all().update(borrower=borrower)
6
注意,update操作后立即生效,并回傳匹配查詢的行記錄(不一定是更新的行記錄數,如果某些行已經是最新值的情況下),另外,update只能更新一張表:model主表,可以基于關聯field做過濾,但是只能更新主表中的列,
更多資料參考鏈接:
https://docs.djangoproject.com/en/1.11/topics/db/queries/#updating-multiple-objects-at-once
關聯物件
一對多關系
正向查找
例子:通過屬性Field訪問被關聯物件(外鍵物件)
>>> book = Book.objects.get(id=2)
>>> book.borrower
<Person: Person object>
>>>
修改外鍵物件
>>> person = Person.objects.get(id=2)
>>> book.borrower=person
>>> book.save()
如果定義ForeignKey時,指定了null=True,可指定None來移除關聯,
>>> book = Book.objects.get(id=2)
>>> book.person = None
>>> book .save() # "UPDATE myapp_book SET person_id = NULL ...;"
一對多關系的訪問,會快取第一次獲取的被關聯物件,
>>> book = Book.objects.get(id=2)
>>> print(book.borrower) # 訪問資料庫
>>> print(book.borrower) # 使用快取
使用select_related方法,會遞回快取一對多關系,
比如:
>>> book = Book.objects.select_related().get(id=2)
>>> print(book.borrower) # 使用快取
>>> print(book.borrower) # 使用快取
“反向”追蹤關系
例:查找myapp_book表中,同myapp_person表存在外鍵參考,且被參考記錄id為1的表記錄
>>> person=Person.objects.get(id=1)
>>> person.book_set.all()
<QuerySet [<Book: Book object>, <Book: Book object>, <Book: Book object>, <Book: Book object>, <Book: Book object>]>
>>>
# person.book_set是一個方便查詢結果集的管理器. 注意書寫格式:ForeignKey指向的model的實體.包含ForeignKey Field的小寫model名稱_set,
>>> person.book_set.filter(book_name='mybook')
<QuerySet [<Book: Book object>, <Book: Book object>]>
>>> person.book_set.count()
5
可以在定義外鍵時,通過設定related_name來重寫FOO_set名稱,比如
更改Book model
borrower = ForeignKey(Person, to_field='id', on_delete=models.CASCADE, related_name='books'),
則可這么寫
>>> person=Person.objects.get(id=1)
>>> person.books.all()
>>> person.books.filter(book_name='mybook')
>>> person.books.count()
使用自定義反向管理器
略
參考鏈接:
https://docs.djangoproject.com/en/2.0/topics/db/queries/#using-a-custom-reverse-manager
處理被關聯物件的其它方法
add(obj1, obj2, ...)
添加指定model物件到被關聯物件集
create(**kwargs)
創建一個新物件,保存并放入被關聯物件集,回傳新建物件,
remove(obj1, obj2, ...)
從被關聯物件集中移除model物件,
clear()
移除所有關聯物件集
set(objs)
替換被關聯的結果物件集,
本節可參考 “修改物件 -修改包含多對多Field的物件”
>>> book1 = Book.objects.filter(book_name='duzhe')[0]
>>> book2 = Book.objects.filter(book_name='shenghuo')[0]
>>> person=Person.objects.get(id=2)
>>> person.book_set.set([book1, book2])
結果,更新了myapp_book中的兩條記錄,更新結果如下(borrower_id變成了2)
id book_name borrower_id
2 duzhe 2
4 shenghuo 2
>>> book2 = Book.objects.filter(book_name='test1')[0]
>>> person.book_set.add(book2)
結果,更新了myapp_book中的1條記錄,更新結果如下(borrower_id變成了2)
id book_name borrower_id
6 test1 2
>>> person.book_set.create(book_name='mybook3')
<Book: Book object>
結果,往myapp_book表新增一條記錄,且borrower_id為2
id book_name borrower_id
10 mybook3 2
注意:不能像上面一樣,按下面的方法使用clear,remove,會報錯(似乎只能用于多對多)
>>> person.book_set.clear()
>>> person.book_set.remove()
參考鏈接:
https://docs.djangoproject.com/en/2.0/topics/db/queries/#additional-methods-to-handle-related-objects
多對多關系
類似“一對多”關系
>>> from myapp.models import Fruit
>>> f = Fruit.objects.get(pk=3)
>>> f.store.all()
<QuerySet [<Store: Store object>]>
>>> f.store.count()
1
>>> f.store.filter(id=7)
<QuerySet [<Store: Store object>]>
反向檢索
>>> from myapp.models import Store
>>> s = Store.objects.get(id=7)
>>> s.fruit_set.all()
<QuerySet [<Fruit: Fruit object>]>
參考鏈接:
https://docs.djangoproject.com/en/2.0/topics/db/queries/#many-to-many-relationships
一對一關系
略
參考鏈接:
https://docs.djangoproject.com/en/2.0/topics/db/queries/#one-to-one-relationships
入口參考鏈接:
https://docs.djangoproject.com/en/2.0/topics/db/queries/
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/69439.html
標籤:Python
