主頁 > 後端開發 > Django model 層之Making Query總結

Django model 層之Making Query總結

2020-09-18 00:20:56 後端開發

Django model 層之Making Query總結

by:授客 QQ1033553122

 

實踐環境

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

上一篇:Python 基于Python生成短8位唯一id解決方案

下一篇:Django 不通過外鍵實作多表關聯查詢

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more