天天看点

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)