天天看點

Django架構 資料庫模型(Model)3 記錄操作

一.查詢記錄

1.單表查詢:

.get_or_create()參見:https://www.cnblogs.com/zhongbokun/p/9732698.html

如果傳入多個條件",",關系是"且"(AND),即所有條件要同時滿足

models.<table>.objects是1個QuerySet
下述查詢接口的QS參數通常為該QuerySet或其經過下述接口查詢的傳回值
如果沒有特别說明,下述方法都傳回QuerySet對象

#################################查詢相關的API#################################

<QS>.all(**kwargs):查詢所有結果
  #傳回QuerySet對象,形式為[obj,obj...]
  #可以繼續使用切片/索引:
    <QS>.objects.all()[x:s:y]
  #參數說明:同.filter()

<QS>.count():傳回資料庫中比對查詢(QuerySet)的對象數量

<QS>.dates(<field_name>,<kind>[,order="ASC"]):根據日期的某部分進行去重查找并截取該部分
  #參數說明:
    field_name:指定字段
    kind:可為"year"(傳回<year>-01-01)/"month"(傳回<year>-<month>-01)/"day"(傳回<year>-<month>-<day>)
    order:指定結果的排序;可為"ASC"(預設值;升序)/"DESC"(降序)
      #也可為位置參數
  #執行個體:
    models.DatePlus.objects.dates('ctime','day','DESC')

<QS>.datetimes(<field_name>,<kind>[,order="ASC",tzinfo=None]):根據日期和時間的某部分進行去重查找并截取指定内容
  #可将時間轉換為指定時區的時間顯示
  #參數說明:其他參數同<QS>.dates()
    kind:可為"year"/"month"/"day"/"hour"/"minute"/"second"
    tzinfo:為時區對象;預設不轉換
  #執行個體:
    models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.UTC)
      #使用UTC時間
    models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.timezone('Asia/Shanghai'))
      #使用上海時間

<QS>.defer(*<fields>):去除某些字段
  #如果使用了fields中沒有的字段,會造成1次額外的資料庫查詢
  #傳回1個QuerySet,格式:[<obj>,<obj>...]

<QS>.distinct(*fields):從結果中剔除重複紀錄
  #由于存在主鍵,整條記錄不會重複,但隻拿某些字段時可能重複
  #參數說明:
    fields:隻對指定字段去重
      #隻有在PostgreSQL中才能使用該參數

<QS>.exclude(**kwargs):傳回與指定條件不比對的對象

<QS>.exists():如果QuerySet對象包含資料,傳回True;否則傳回False

<QS>.filter(**kwargs):傳回符合條件的對象
  #傳回QuerySet對象,可能包含多條記錄
  #參數說明:
    QS:1個QuerySet
    kwargs:指定條件的字典;也可使用鍵值對的形式

<QS>.first():傳回第一條記錄

<QS>.get():傳回符合條件的對象
  #傳回model對象;且傳回的對象有且隻有1個,而非對象集合
  #如果符合條件的對象超過1個或沒有,均會抛出錯誤

<QS>.get_or_create(**kwargs[,defaults=None]):存在,則擷取;否則,就建立
  #相當于:如果存在符合條件的對象,則執行.get(),否則執行.create()
  傳回(obj,created),obj是查詢到的/被建立的對象構成的QuerySet,created是表示是否建立了新對象的bool
  #參數說明:
    kwargs:指定篩選條件
    defaults:指定建立時的其他參數
      #即:如果符合條件的記錄不存在,使用2個參數指定的值來建立記錄

<dict>=<QS>.in_bulk(<id_list>):根據主鍵ID進行查找
  #???可以用其他類型的主鍵查找嗎???
  注意:傳回的是1個dict,格式:{<ID1>:<記錄1>,<ID2>:<記錄2>...}
  #參數說明:
    id_list:主鍵ID構成的list
      #即:傳回主鍵ID在該清單中的記錄構成的dict
  #執行個體:
    id_list=[11,21,31]
    re_dict=models.DDD.objects.in_bulk(id_list)

<QS>.last():傳回最後一條記錄

<QS>.none():傳回1個空的QuerySet對象

<QS>.only(*<fields>):進擷取指定字段

<QS>.order_by(*<fields>):對查詢結果排序

<QS>.reverse():将查詢結果反向排序
  #對已排序的QuerySet(也就是ordered=True)才有效(如.order_by()的查詢結果)
  #實質是:Asc⇋Des(互相轉換)
  #執行個體:
    models.Person.all()和models.Person.all().reverse()相同
    models.Person.all().order_by("id")和models.Person.all().order_by("id").reverse()相反

<QS>.using(<alias>):到指定的資料庫進行查詢
  #參數說明:
    alias:資料庫的别名(即settings.py中DATABASES中的key)

<QS>.values(*<fields>):傳回結果中指定字段的值
  #傳回1個ValueQuerySet,格式:[<dict>,<dict>...]
  #也可以是<table>.values(),得到所有記錄中指定字段的值
  #ValueQuerySet是特殊的QuerySet,運作後得到的不是一系列model對象,而是1個可疊代的dict構成的序列,dict中包含字段及其值
  #參數說明:
    fields:指定字段;即"<field1>",...

<QS>.values_list(*<fields>):與values()相似,但傳回1個元組構成的序列
  #傳回1個ValueQuerySet,格式:[<tuple>,<tuple>...]

###################有時查詢API不能滿足需要,故提供擴充查詢方法####################

<QS>.extra(select=None,where=None,params=None,tables=None,order_by=None,select_params=None)

#執行個體:
models.Entry.objects.extra(select={'is_recent': "pub_date > '2006-01-01'"})
models.Blog.objects.extra(
    select=SortedDict([('a', '%s'), ('b', '%s')]),
    select_params=('one', 'two'))
q=models.Entry.objects.extra(select={'is_recent':"pub_date > '2006-01-01'"})
q=q.extra(order_by=['-is_recent'])
p=models.Entry.objects.extra(where=['headline=%s'],params=['Lennon'])
#如果仍不能滿足要求,還可以直接使用原生SQL語句

###############################條件查詢(萬能的__)##############################
判斷條件隻能使用 = 的形式:
<field>__<xx>用于實作一些特殊效果
 #示例:
	__contains:包含指定值
	__data:比對日期
	__day:比對[日]
	__endswith:以指定值結尾
	__exact:進行精确比對(相當于不加__<xx>的查找)
	__gt:大于指定值
	__gte:大于等于指定值
	__hour:比對[時]
	__icontains:包含不區分大小寫的指定值
	__iendswith:以不區分大小寫的指定值結尾
	__iexact:進行不區分大小寫的精确比對
	__iregex:進行不區分大小寫的正則比對
	__isnull:為True表示該字段為空的記錄;為False表示該字段不為空的記錄
	__istartswith:以不區分大小寫的指定值開始
	__in=[x1,x2,...]:在指定值中
	__lt:小于指定值
	__lte:小于等于指定值
	__minute:比對[分]
	__month:比對[月]
	__range=[x,y]:在[x,y]範圍内
	__regex:進行正則比對
	__second:比對[秒]
	__startswith:以指定值開始
	__time:比對時間
	__week:比對[周]
	__week_day:比對[星期幾]
	__year:比對[年]
<for_key>__<field>:關聯表的某字段
  #通常用于多表的正向關聯查詢
  #參數說明:
    for_key:外鍵字段名
    field:關聯表中的字段
<tab>__<field>:關聯表中的指定字段
  #通常用于多表的反向關聯查詢
  #參數說明:
    tab:關聯表
           

2.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是惰性的:
.all()/.filter()等都隻是傳回了1個QuerySet(查詢結果集合對象)
它不會馬上執行SQL,而是在調用QuerySet時才執行

QuerySet對應于資料庫的若幹記錄(row),通過可選的查詢過濾
如下面的代碼會得到資料庫中所有名字為'Dave'的人:
person_set=models.Person.objects.filter(first_name="Dave")
上面的代碼并沒有運作任何的資料庫查詢
可以使用person_set,加上一些過濾條件,或将其傳給某個函數,這些操作都不會發送給資料庫
因為資料庫查詢是顯著影響web應用性能的因素之一

2.用到資料時才會真正執行SQL,如周遊QuerySet或者使用if QuerySet
為了驗證這些,在settings.py中加入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")
for i in obj:#得到的的仍是原本緩存的資料,而不是新的資料
    print(i)#LOGGING隻會列印一次,即隻執行1次
models.Book.objects.filter(id=3).update(title="NET")
obj=models.Book.objects.filter(id=3)
for i in obj:
    print(i)#重新讀取後會得到新的資料,并再次執行

4.簡單的使用if語句進行判斷也會完全執行整個queryset
并把資料放入cache,雖然你不需要這些資料
為避免這個,可以用<result>.exists()來檢查是否有資料
obj=models.Book.objects.filter(id=4)
#exists()的檢查可以避免資料放入QuerySet的cache
if obj.exists():
    print("hello world!")

5.當queryset非常巨大時,cache會成為問題
處理成千上萬的記錄時,将它們一次裝入記憶體是很浪費的
更糟的是,巨大的QuerySet可能會鎖住系統程序,讓程式瀕臨崩潰
要避免在周遊資料時産生QuerySet cache
可以使用.iterator()獲擷取資料,1次隻取1條記錄,處理完就丢棄資料
objs=models.Book.objects.all().iterator()
#iterator()可以1次隻從資料庫擷取少量資料,這樣可以節省記憶體
for obj in objs:
    print(obj.name)
#但是再次周遊沒有列印,因為疊代器已經在上次周遊完了
for obj in objs:
    print(obj.name)
#不過使用iterator()來防止生成Cache,意味着周遊同1個QuerySet時會重複執行查詢
#是以使用iterator()時要當心,確定代碼在操作1個大的QuerySet時沒有重複執行查詢

#總結:
QuerySet的cache是用于減少程式對資料庫的查詢,在通常的使用下會保證隻有在需要的時候才會查詢資料庫
使用exists()和iterator()方法可以優化程式對記憶體的使用
不過,由于它們不生成QuerySet cache,可能造成額外的資料庫查詢
           

3.多表查詢(對象查詢/條件查詢):

#################################對象形式的查找################################
#正向查找:從"多"查找"一"中的字段
  <table>.objects.filter(<key>=<value>)
  #得到1條關聯表中的記錄,可通過該對象得到關聯表中字段的值
  #參數說明:
    for_key:外鍵字段
  #執行個體:
    ret1=models.Book.objects.first()
    print(ret1.title)
    print(ret1.publisher)#得到的是publisher的記錄對象
    #因為多個Book對1個Publisher的關系,ret1.publisher是對象,而非QuerySet集合
    print(ret1.publisher.name)

#反向查找:從"一"查找"多"中的字段
  <table>.objects.filter(<for_key>__<field>=<value>)
    #參數說明:
      for_key__field=value:關聯表中的指定字段等于指定值
        #也可為<for_key>=<obj>,即通過外鍵得到的關聯表記錄為指定記錄
  #執行個體:
    ret2=models.Publisher.objects.last()
    print(ret2.name)
    print(ret2.book_set.all())
    #ret2.book_set是所有與ret2綁定的Book對象的queryset集合
    #如果在Book中設定了related_name,此處可為print(ret2.<related_name>.all())
    pub_o=models.Publisher.objects.last()
    ret2_2=models.Book.objects.filter(publish=pub_o).values("title")
    #Publisher中最後1條記錄對應的出版社出版的全部書籍

#關于book_set:
If a model has a ForeignKey, instances of the foreign-key model will have access to
a Manager that returns all instances of the first model. By default, this Manager is
named FOO_set, where FOO is the source model name, lowercased. This Manager returns
QuerySets, which can be filtered and manipulated as described in the "Retrieving
objects” section above.
#############################多表條件關聯查詢(__)##############################

#正向條件查找:利用<for_key>__<field>查詢
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)
#注意:
  1.正向查找的publisher__city或author__name中的publisher,author是Book表中的外鍵字段
  2.一對多和多對多在這裡的用法無差別

#反向條件查找:利用<tab>__<field>查詢
#反向查找之一對多:
ret8=models.Publisher.objects.filter(book__title='Python').values('name')
print(ret8)#[{'name':'人大出版社'}]
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'}]
#注意:
  1.反向查找的book__title中的book就是Publisher的關聯表名,因為Publisher中沒有外鍵
  2.一對多和多對多在這裡用法無差別
           

4.聚合查詢和分組查詢:

<QS>.aggregate(*args,**kwargs):對QuerySet進行計算,傳回聚合值字典
  #每個參數都指定1個傳回的字典中的鍵值對,即:在查詢集上生成聚合
  #即對整個表進行分組,隻傳回1列
  #參數說明:
    result:要進行計算的QuerySet;通常是上1步查詢的結果
    args:<func>("<field>")
      #在傳回的字典中自動命名
    kwargs:"<name>"=<func>("<field>")

#aggregate()是QuerySet的一個終止子句,意思是說:
傳回1個字典,鍵的名稱是聚合值的辨別符(名稱),值是計算出的聚合值
如果通過*args指定,聚合值的辨別符是按字段和聚合函數名自動生成的
如果要為聚合值指定辨別符,需要通過**args(鍵值對)提供聚合值
**args中的鍵是聚合值的辨別符,值是要計算的聚合值

#執行個體:
from django.db.models import Avg,Min,Sum,Max,Count#首先需要導入聚合函數

#從整個查詢集生成統計值,比如計算所有在售書的平均價錢:
models.Book.objects.all().aggregate(Avg('price'))
#結果:{'price__avg': 34.35}

#aggregate()的參數指定要計算的聚合值,在下例中,是Book模型中price字段的平均值:
models.Book.objects.aggregate(average_price=Avg('price'))
#結果:{'average_price': 34.35}

#如果同時也要獲得所有圖書價格的最大值和最小值:
models.Book.objects.aggregate(Avg('price'),Max('price'),Min('price'))
#結果:{'price__avg': 34.35, 'price__max': Decimal('81.20'), 'price__min': Decimal('12.99')}

#計算總共有多少種價格和多少本書:
models.Book.objects.aggregate(p=Count('price',distinct=True),t=Count('title'))
  #distinct=True表示進行去重
#結果:{'p':3,'t':4}

##############################################################################

<QS>.annotate(*args,**kwargs):對QuerySet進行分組,并對每組進行計算,傳回聚合值清單
  #計算查詢結果中每個對象所關聯的對象集合,進而得到聚合值,即:為查詢集的每1項生成聚合
  #也就是對各個字段進行分組,傳回多個列
  #參數說明:同aggregate()

分組依據是result中的字段,常由.values()得到;傳回的清單由多個字典構成,每個都是某組的聚合值字典
每個字典包括2個鍵值對,1個表示分組依據(鍵)及具體的組(值),另1個表示聚合值(值)及其辨別符(鍵)

#執行個體:
models.Book.objects.values("authors_name").annotate(Sum("price"))
#結果:[{"authors_name":"alex","price_sum":Decimal("140.00")},{"authors_name":"alvin","price_sum":Decimal("160.00")},{"authors_name":"wu","price_sum":Decimal("90.00")},{"authors_name":"yu","price_sum":Decimal("105.00")},{"authors_name":None,"price_sum":Decimal("44.00")}]

models.Book.objects.values("publisher_name").annotate(Min("price"))
#結果:[{"publisher_name":"中國郵電大學出版社","price_min":Decimal("10.00")},{"publisher_name":"人民出版社","price_min":Decimal("85.00")},{"publisher_name":"機械出版社","price_min":Decimal("44.00")}]
           

5.F查詢和Q查詢:

#有時單一關鍵字參數查詢無法滿足需求,為此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

1.Q對象(django.db.models.Q)可以對關鍵字參數進行封裝,進而更好地應用多個查詢:
  q1=models.Book.objects.filter(Q(title__startswith='P')).all()
  print(q1)#[<Book: Python>, <Book: Perl>]

2.可以組合使用&和|操作符,當1個操作符用于2個Q對象,将産生1個新的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))
)
#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

5.Q對象可以與關鍵字參數查詢一起使用,不過Q對象一定要在關鍵字參數查詢前面:
  #正确:
    models.Book.objects.get(
        Q(pub_date=date(2005,5,2))|Q(pub_date=date(2005,5,6)),
        title__startswith='P'
    )
  #錯誤:
    models.Book.objects.get(
        question__startswith='P',
        Q(pub_date=date(2005,5,2))|Q(pub_date=date(2005,5,6))
    )
           

6.其他查詢:

##################################執行原生SQL##################################

ORM無法生成所有SQL語句,有些隻能使用RAW SQL

#方法1:
from django.db import connection, connections
cursor=connection.cursor()
#也可為:cursor=connections['default'].cursor()#default為資料庫的别名(見settings.py中DATABASE)
cursor.execute("""SELECT * from auth_user where id = %s""",[1])
row=cursor.fetchone()

#方法2:models.<table_name>.objects.raw()
models.UserInfo.objects.raw('select * from userinfo')
  #使用這種方法則必須取出主鍵,否則會報錯
models.UserInfo.objects.raw('select id as nid from UserType')
  #如果要通過本表查詢其他表,必須将字段名設為目前表的主鍵字段名
  #id為UserType中的字段,nid為UserInfo的主鍵字段
models.UserInfo.objects.raw('select id as nid from userinfo where nid>%s', params=[12,])
  #為原生SQL設定參數
name_map={'first':'first_name','last':'last_name','bd':'birth_date','pk':'id'}
models.Person.objects.raw('SELECT * FROM some_other_table',translations=name_map)
  #将擷取的到列名轉換為指定列名
models.UserInfo.objects.raw('select * from userinfo',using="default")
  #指定資料庫

##############################下面是性能相關的查詢##############################

均用于減少查詢資料庫的次數,以提高性能;可能出現跨表操作時才使用

<QS>.select_related(["<for_key>__<field>"]):進行join連表操作,擷取關聯資料
  #QS應當是前述查詢方法傳回的QuerySet
  #參數說明:
    for_key:外鍵字段;預設擷取指定關聯表中的關聯資料
      如果加上__field,則隻擷取關聯表中指定字段的關聯資料
      如果不加參數,擷取所有關聯表中的關聯資料
      #如果要同時獲得多個關聯表中的資料,用逗号隔開各個for_key

#執行個體:
model.tb.objects.all().select_related()
model.tb.objects.all().select_related('外鍵字段')
model.tb.objects.all().select_related('外鍵字段__關聯表的字段')

<QS>.prefetch_related(["<for_key>__<field>"]):執行多次SQL查詢以擷取關聯資訊
  #多表連表會導緻性能損耗,該指令對每個相關聯的表執行一次查詢,以降低性能損耗
  #參數說明:同<QS>.select_related()

#執行個體:
models.UserInfo.objects.prefetch_related("ut")
#執行SELECT * FROM UserInfo
#計算得到所有使用者類型的ID:[1,2,3]
#再執行SELECT * FROM UserType WHERE id in [1,2,3]
           

二.增加/删除/修改記錄

1.增:就是執行個體化表對象的過程

(1)處理一對一關系:

<record>=<table>(<field1>=...,<field2>=...,...)
<record>.save()
  #參數說明:
    table:表對象
    field1,field2,...:字段名
      #也可以寫為**dict,傳入1個字典而不是許多個鍵值對
    record:執行個體化出的對象,即記錄

#執行個體:
from app01.models import Author
author=Author(name="alvin")
author.save()
author=Author()
author.name="alvin"#執行個體化出對象後再設定鍵值對也可以
author.save()

##############################################################################

<table>.objects.create(field1=...,...)
  #無需.save();内部就是上1種方法
  #參數說明:同上

#執行個體:
from app01.models import Author #導入所有表對象
Author.objects.create(name='Alvin')
Author.objects.create(**{"name":"alex"})

##############################################################################

<table>.bulk_create(objs,batch_size=1):批量增加記錄
  #參數說明:
    obj:要插入表中的記錄對象構成的list
    batch_size:1次最多插入多少條記錄
      #如果要插入的記錄對象超過了batch_size,會自動分幾次插入
    table:表對象

#執行個體:
objs=[
    models.DDD(name='r11'),
    models.DDD(name='r22')
]
models.DDD.objects.bulk_create(objs,10)
           

(2)處理一對多/多對多關系(外鍵):

#一對多:建立ForeignKey
  方式1:通過<for_key>=<value>指定外鍵字段的值,進而建立關聯
    #參數說明:
      for_key:本表中的外鍵字段
    #執行個體:
      models.Book.objects.create(
          title='php',
          publisher__id=2,#建立ForeignKey,一定在Book中
          #2指為該book對象綁定Publisher表中id=2的行對象
          publication__date='2017-7-7',
          price=99
      )
  方式2:通過<field>=<obj>綁定關聯表中的記錄
    #參數說明:
      field:表中外鍵字段名去除關聯表中主鍵名和"_"的部分
        #即顯式建立的,未被Django補全的外鍵字段名
        #如:外鍵字段為publisher_id,關聯表中主鍵為id,則此處為publisher
      obj:關聯表中的記錄對象
    #執行個體:
      <1>先擷取要綁定的Publisher對象:
        pub_obj=Publisher(
            name='河大出版社',
            address='保定',
            city='保定',
            state_province='河北',
            country='China',
            website='http://www.hbu.com'
        )
        #或:pub_obj=Publisher.objects.get(id=1)
      <2>将方法1中的publisher_id=2改為publisher=pub_obj

##########################################################

#多對多(ManyToManyField()):
<QS>.<for_key>.add(<aobjs>):為result中的記錄和aobjs中的記錄建立多對多關聯
  #參數說明:
    result:查詢本表得到的結果;為QuerySet集合
    for_key:本表的外鍵字段
    aobjs:要關聯的記錄;為QuerySet集合(加*表示這一點)/在關聯表中的id
      #也可以是aobj1,aobj2...的形式
  #執行個體:
	author1=models.Author.objects.get(id=1)
	author2=models.Author.objects.filter(name='alvin')[0]
	book=models.Book.objects.get(id=1)
	book.authors.add(author1,author2)
	#等同于:
	book.authors.add(*[author1,author2])
	##############################################
	book=models.Book.objects.filter(id__gt=1)
	authors=models.Author.objects.filter(id=1)[0]
	authors.book_set.add(*book)
	##############################################
	models.Book.author.add(1)
	models.Author.book_set.add(1)

#注意:如果第三張表是通過models.ManyToManyField()自動建立的,那麼綁定關系隻有上面1種方式
#如果第三張表是自己建立的:
class Book2Author(models.Model):
    author=models.ForeignKey("Author")
    Book=models.ForeignKey("Book")
#那麼還有1種添加綁定關系的方式:
author_obj=models.Author.objects.filter(id=2)[0]
book_obj =models.Book.objects.filter(id=3)[0]
s=models.Book2Author.objects.create(author_id=1,Book_id=2)
s.save()
s=models.Book2Author(author=author_obj,Book_id=1)
s.save()
           

2.删

(1)處理一對一關系:

<QS>.delete():删除指定記錄
  #預設為級聯删除
  #參數說明:
    obj:通過查詢得到的記錄

models.Book.objects.filter(id=1).delete()
#結果:(3, {'app01.Book_authors': 2, 'app01.Book': 1})
           

(2)處理多對多關系:

<QS>.<for_key>.remove(<aobjs>):删除result中的記錄和aobjs中的記錄的關聯
  #參數說明:
    result:查詢本表得到的結果;為QuerySet集合
    for_key:本表的外鍵字段
    aobjs:關聯的記錄;為QuerySet集合(加*)/在關聯表中的id(要求有id)

<QS>.<for_key>.clear():删除result的所有關聯
  #for_key在反向删除時為<for_tab>_set,即關聯表的全部記錄的1個QuerySet集合

#正向:從foreign key所在的表删除關聯
book=models.Book.objects.filter(id=1)
#删除第三張表中和女孩1關聯的所有關聯資訊:
book.author.clear()#清空與Book中id=1關聯的所有資料
book.author.remove(2)#可以為id,但不常用
book.author.remove(*[1,2,3,4])#可以為清單,前面加*

#反向:從沒有foreign key的表删除關聯
author=models.Author.objects.filter(id=1)
author.book_set.clear() #清空與Author中id=1關聯的所有資料
           

3.改

(1)處理一對一關系:

<QS>.update(<field>=<value>):将指定記錄的指定字段改為指定值
  #可以同時更新多條記錄
  #傳回1個Int,表示受影響的記錄條數

#.save()方法:
<obj>.<field>=<value>
<obj>.save()
  #參數說明:
    obj:通過查詢得到的記錄
    field:要修改的字段
    value:修改後的值

#執行個體:
models.Book.objects.filter(id=5).update(name="Python基礎")
b=models.Book.objects.get(id=3)
b.name="Django基礎"
b.save()

##############################################################################

[<obj>,<created>]=<QS>.update_or_create(**kwargs[,defaults=None]):如果存在,則更新;否則,就建立
  #參數說明:
    kwargs:指定篩選條件
    defaults:指定建立/更新後的值
      #即:僅根據defaults指定的值來建立/更新記錄
    obj:傳回更新/建立後的結果
    created:傳回1個表示是否建立了記錄的bool

#執行個體:
obj,created=models.UserInfo.objects.update_or_create(username='root1',defaults={'email':'1111111','u_id': 2,'t_id':1})
           
#注意:
1.update()方法不能通過get()獲得記錄;save()方法不能通過filter獲得記錄
因為update()是QuerySet對象的方法;而get()傳回model對象,沒有update()
filter傳回QuerySet對象,沒有save();并且是1個對象集合,可能包含多條記錄,無法調用字段
即使對QuerySet對象進行切片獲得記錄,也無法調用save()

2.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)

#save()方法會将所有屬性重新設定一遍,效率低:
obj=models.Book.objects.filter(id=3)[0]
obj.title="Python"
obj.save()
#SQL:
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;

3.save()方法還可能引起競态條件

4.update()對于任何結果集(QuerySet)均有效,這意味着可以同時更新多條記錄
update()會傳回1個整數,表示受影響的記錄條數
           

(2)處理多對多關系:

#實際就是先删除再增加:
obj=Book.objects.filter(id=1)[0]
author=Author.objects.filter(id__gt=2)
obj.author.clear()
obj.author.add(*author)