05 Django ORM 常用字段和参数

Django ORM 常用字段和参数

一些说明:

  • 表myapp_person的名称是自动生成的,如果你要自定义表名,需要在model的Meta类中指定 db_table 参数,强烈建议使用小写表名,特别是使用MySQL作为后端数据库时。

  • id字段是自动添加的,如果你想要指定自定义主键,只需在其中一个字段中指定 primary_key=True 即可。如果Django发现你已经明确地设置了Field.primary_key,它将不会添加自动ID列。

  • 本示例中的CREATE TABLE SQL使用PostgreSQL语法进行格式化,但值得注意的是,Django会根据配置文件中指定的数据库后端类型来生成相应的SQL语句。

  • Django支持MySQL5.5及更高版本。

 

常用字段

AutoField

int自增列,必须填入参数 primary_key=True。当model中如果没有自增列,则自动会创建一个列名为id的列。

IntegerField

一个整数类型,范围在 -2147483648 to 2147483647。(一般不用它来存手机号(位数也不够),直接用字符串存,)

CharField

字符类型,必须提供max_length参数, max_length表示字符长度。

这里需要知道的是Django中的CharField对应的MySQL数据库中的varchar类型,没有设置对应char类型的字段,但是Django允许我们自定义新的字段,下面我来自定义对应于数据库的char类型

img

自定义字段在实际项目应用中可能会经常用到,这里需要对他留个印象!

from django.db import models

# Create your models here.
#Django中没有对应的char类型字段,但是我们可以自己创建
class FixCharField(models.Field):
   '''
  自定义的char类型的字段类
  '''
   def __init__(self,max_length,*args,**kwargs):
       self.max_length=max_length
       super().__init__(max_length=max_length,*args,**kwargs)

   def db_type(self, connection):
       '''
      限定生成的数据库表字段类型char,长度为max_length指定的值
      :param connection:
      :return:
      '''
       return 'char(%s)'%self.max_length
#应用上面自定义的char类型
class Class(models.Model):
   id=models.AutoField(primary_key=True)
   title=models.CharField(max_length=32)
   class_name=FixCharField(max_length=16)
   gender_choice=((1,'男'),(2,'女'),(3,'保密'))
   gender=models.SmallIntegerField(choices=gender_choice,default=3)

DateField

日期字段,日期格式 YYYY-MM-DD,相当于Python中的datetime.date()实例。

DateTimeField

日期时间字段,格式 YYYY-MM-DD HH:MM:ss[.uuuuuu],相当于Python中的datetime.datetime()实例。

字段合集(争取记忆)

AutoField(Field)
       - int自增列,必须填入参数 primary_key=True

   BigAutoField(AutoField)
       - bigint自增列,必须填入参数 primary_key=True

       注:当model中如果没有自增列,则自动会创建一个列名为id的列
       from django.db import models

       class UserInfo(models.Model):
           # 自动创建一个列名为id的且为自增的整数列
           username = models.CharField(max_length=32)

       class Group(models.Model):
           # 自定义自增列
           nid = models.AutoField(primary_key=True)
           name = models.CharField(max_length=32)

   SmallIntegerField(IntegerField):
       - 小整数 -32768 32767

   PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
       - 正小整数 0 32767
   IntegerField(Field)
       - 整数列(有符号的) -2147483648 2147483647

   PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
       - 正整数 0 2147483647

   BigIntegerField(IntegerField):
       - 长整型(有符号的) -9223372036854775808 9223372036854775807

   BooleanField(Field)
       - 布尔值类型

   NullBooleanField(Field):
       - 可以为空的布尔值

   CharField(Field)
       - 字符类型
       - 必须提供max_length参数, max_length表示字符长度

   TextField(Field)
       - 文本类型

   EmailField(CharField)
       - 字符串类型,Django Admin以及ModelForm中提供验证机制

   IPAddressField(Field)
       - 字符串类型,Django Admin以及ModelForm中提供验证 IPV4 机制

   GenericIPAddressField(Field)
       - 字符串类型,Django Admin以及ModelForm中提供验证 Ipv4和Ipv6
       - 参数:
           protocol,用于指定Ipv4或Ipv6, 'both',"ipv4","ipv6"
           unpack_ipv4, 如果指定为True,则输入::ffff:192.0.2.1时候,可解析为192.0.2.1,开启此功能,需要protocol="both"

   URLField(CharField)
       - 字符串类型,Django Admin以及ModelForm中提供验证 URL

   SlugField(CharField)
       - 字符串类型,Django Admin以及ModelForm中提供验证支持 字母、数字、下划线、连接符(减号)

   CommaSeparatedIntegerField(CharField)
       - 字符串类型,格式必须为逗号分割的数字

   UUIDField(Field)
       - 字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证

   FilePathField(Field)
       - 字符串,Django Admin以及ModelForm中提供读取文件夹下文件的功能
       - 参数:
               path,                      文件夹路径
               match=None,                正则匹配
               recursive=False,           递归下面的文件夹
               allow_files=True,          允许文件
               allow_folders=False,       允许文件夹

   FileField(Field)
       - 字符串,路径保存在数据库,文件上传到指定目录
       - 参数:
           upload_to = ""      上传文件的保存路径
           storage = None      存储组件,默认django.core.files.storage.FileSystemStorage

   ImageField(FileField)
       - 字符串,路径保存在数据库,文件上传到指定目录
       - 参数:
           upload_to = ""      上传文件的保存路径
           storage = None      存储组件,默认django.core.files.storage.FileSystemStorage
           width_field=None,   上传图片的高度保存的数据库字段名(字符串)
           height_field=None   上传图片的宽度保存的数据库字段名(字符串)

   DateTimeField(DateField)
       - 日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]

   DateField(DateTimeCheckMixin, Field)
       - 日期格式      YYYY-MM-DD

   TimeField(DateTimeCheckMixin, Field)
       - 时间格式      HH:MM[:ss[.uuuuuu]]

   DurationField(Field)
       - 长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型

   FloatField(Field)
       - 浮点型

   DecimalField(Field)
       - 10进制小数
       - 参数:
           max_digits,小数总长度
           decimal_places,小数位长度

   BinaryField(Field)
       - 二进制类型
对应关系:
   'AutoField': 'integer AUTO_INCREMENT',
   'BigAutoField': 'bigint AUTO_INCREMENT',
   'BinaryField': 'longblob',
   'BooleanField': 'bool',
   'CharField': 'varchar(%(max_length)s)',
   'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
   'DateField': 'date',
   'DateTimeField': 'datetime',
   'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
   'DurationField': 'bigint',
   'FileField': 'varchar(%(max_length)s)',
   'FilePathField': 'varchar(%(max_length)s)',
   'FloatField': 'double precision',
   'IntegerField': 'integer',
   'BigIntegerField': 'bigint',
   'IPAddressField': 'char(15)',
   'GenericIPAddressField': 'char(39)',
   'NullBooleanField': 'bool',
   'OneToOneField': 'integer',
   'PositiveIntegerField': 'integer UNSIGNED',
   'PositiveSmallIntegerField': 'smallint UNSIGNED',
   'SlugField': 'varchar(%(max_length)s)',
   'SmallIntegerField': 'smallint',
   'TextField': 'longtext',
   'TimeField': 'time',
   'UUIDField': 'char(32)',

 

字段参数

null

用于表示某个字段可以为空。

unique

如果设置为unique=True 则该字段在此表中必须是唯一的 。

db_index

如果db_index=True 则代表着为此字段设置索引。

default

为该字段设置默认值。

DateField和DateTimeField

auto_now_add

配置auto_now_add=True,创建数据记录的时候会把当前时间添加到数据库。

auto_now

配置上auto_now=True,每次更新数据记录的时候会更新该字段。

关系字段

ForeignKey

外键类型在ORM中用来表示外键关联关系,一般把ForeignKey字段设置在 '一对多'中'多'的一方。

ForeignKey可以和其他表做关联关系同时也可以和自身做关联关系。

字段参数

to

设置要关联的表

to_field

设置要关联的表的字段

on_delete

当删除关联表中的数据时,当前表与其关联的行的行为。

models.CASCADE

删除关联数据,与之关联也删除

db_constraint

是否在数据库中创建外键约束,默认为True。

models.DO_NOTHING
删除关联数据,引发错误IntegrityError


models.PROTECT
删除关联数据,引发错误ProtectedError


models.SET_NULL
删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空)


models.SET_DEFAULT
删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值)


models.SET

删除关联数据,
a. 与之关联的值设置为指定值,设置:models.SET(值)
b. 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象)
def func():
   return 10

class MyModel(models.Model):
   user = models.ForeignKey(
       to="User",
       to_field="id"
       on_delete=models.SET(func)
  )

OneToOneField

一对一字段。

通常一对一字段用来扩展已有字段。(通俗的说就是一个人的所有信息不是放在一张表里面的,简单的信息一张表,隐私的信息另一张表,之间通过一对一外键关联)

字段参数

to

设置要关联的表。

to_field

设置要关联的字段。

on_delete

当删除关联表中的数据时,当前表与其关联的行的行为。(参考上面的例子)

一般操作

在进行一般操作时先配置一下参数,使得我们可以直接在Django页面中运行我们的测试脚本

在Python脚本中调用Django环境

img

 

这样就可以直接运行你的test.py文件来运行测试

必知必会13条

操作下面的操作之前,我们实现创建好了数据表,这里主要演示下面的操作,不再细讲创建准备过程

<1> all(): 查询所有结果

img

<2> filter(**kwargs): 它包含了与所给筛选条件相匹配的对象

img

<3> get(**kwargs): 返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。

img

img

<4> exclude(**kwargs): 它包含了与所给筛选条件不匹配的对象 img

<5> values(*field): 返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列model的实例化对象,而是一个可迭代的字典序列 img

<6> values_list(*field): 它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列 img

<7> order_by(*field): 对查询结果排序

<8> reverse(): 对查询结果反向排序,请注意reverse()通常只能在具有已定义顺序的QuerySet上调用(在model类的Meta中指定ordering或调用order_by()方法)。

<9> distinct(): 从返回结果中剔除重复纪录(如果你查询跨越多个表,可能在计算QuerySet时得到重复的结果。此时可以使用distinct(),注意只有在PostgreSQL中支持按字段去重。)

<10> count(): 返回数据库中匹配查询(QuerySet)的对象数量。 img

<11> first(): 返回第一条记录 img

<12> last(): 返回最后一条记录

<13> exists(): 如果QuerySet包含数据,就返回True,否则返回False

13个必会操作总结

返回QuerySet对象的方法有

all()

filter()

exclude()

order_by()

reverse()

distinct()

特殊的QuerySet

values() 返回一个可迭代的字典序列

values_list() 返回一个可迭代的元祖序列

返回具体对象的

get()

first()

last()

返回布尔值的方法有:

exists()

返回数字的方法有

count()

Django终端打印SQL语句

如果你想知道你对数据库进行操作时,Django内部到底是怎么执行它的sql语句时可以加下面的配置来查看

在Django项目的settings.py文件中,在最后复制粘贴如下代码:

LOGGING = {
   'version': 1,
   'disable_existing_loggers': False,
   'handlers': {
       'console':{
           'level':'DEBUG',
           'class':'logging.StreamHandler',
      },
  },
   'loggers': {
       'django.db.backends': {
           'handlers': ['console'],
           'propagate': True,
           'level':'DEBUG',
      },
  }
}

 

配置好之后,再执行任何对数据库进行操作的语句时,会自动将Django执行的sql语句打印到pycharm终端上

补充:

除了配置外,还可以通过一点.query即可查看查询语句,具体操作如下:

img

单表查询之神奇的双下划线

models.Tb1.objects.filter(id__lt=10, id__gt=1)   # 获取id大于1 且 小于10的值

models.Tb1.objects.filter(id__in=[11, 22, 33])   # 获取id等于11、22、33的数据
models.Tb1.objects.exclude(id__in=[11, 22, 33])  # not in

models.Tb1.objects.filter(name__contains="ven")  # 获取name字段包含"ven"的
models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感

models.Tb1.objects.filter(id__range=[1, 3])      # id范围是1到3的,等价于SQL的bettwen and

类似的还有:startswith,istartswith, endswith, iendswith 

date字段还可以:
models.Class.objects.filter(first_day__year=2017)
date字段可以通过在其后加__year,__month,__day等来获取date的特点部分数据
# date
       #
       # Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1))
       # Entry.objects.filter(pub_date__date__gt=datetime.date(2005, 1, 1))

       # year
       #
       # Entry.objects.filter(pub_date__year=2005)
       # Entry.objects.filter(pub_date__year__gte=2005)

       # month
       #
       # Entry.objects.filter(pub_date__month=12)
       # Entry.objects.filter(pub_date__month__gte=6)

       # day
       #
       # Entry.objects.filter(pub_date__day=3)
       # Entry.objects.filter(pub_date__day__gte=3)

       # week_day
       #
       # Entry.objects.filter(pub_date__week_day=2)
       # Entry.objects.filter(pub_date__week_day__gte=2)
需要注意的是在表示一年的时间的时候,我们通常用52周来表示,因为天数是不确定的,老外就是按周来计算薪资的哦~
# 1.查询主键为2的书籍对应的出版社(不能点书籍)
   # res = models.Publish.objects.filter(book__pk=2)
   # print(res) # <QuerySet [<Publish: 对象:北方出版社>]>
   # 2.查询主键为3的书籍对应的作者
   # res = models.Author.objects.filter(book__pk=3)
   # print(res) # <QuerySet [<Author: 对象:jason>, <Author: 对象:tony>]>
   # 3.查询jason的详情数据
   # res = models.AuthorDetail.objects.filter(author__name='jason')
   # print(res)
   # 4.查询北方出版社出版的书籍
   # res = models.Book.objects.filter(publish__name='北方出版社')
   # print(res)
   # 5.查询jason写过的书籍
   # res = models.Book.objects.filter(authors__name='jason')
   # print(res)
   # 6.查询电话是110的作者姓名
   res = models.Author.objects.filter(author_detail__phone=110)
   print(res)

ForeignKey操作

正向查找(两种方式)

1.对象查找(跨表)

语法:

对象.关联字段.字段

要点:先拿到对象,再通过对象去查对应的外键字段,分两步

示例:

book_obj = models.Book.objects.first()  # 第一本书对象(第一步)
print(book_obj.publisher) # 得到这本书关联的出版社对象
print(book_obj.publisher.name) # 得到出版社对象的名称

2.字段查找(跨表)

语法:

关联字段__字段

要点:利用Django给我们提供的神奇的双下划线查找方式

示例:

models.Book.objects.all().values("publisher__name")
#拿到所有数据对应的出版社的名字,神奇的下划线帮我们夸表查询

反向操作(两种方式)

1.对象查找

语法:

obj.表名_set

要点:先拿到外键关联多对一,一中的某个对象,由于外键字段设置在多的一方,所以这里还是借用Django提供的双下划线来查找

示例:

publisher_obj = models.Publisher.objects.first()  # 找到第一个出版社对象
books = publisher_obj.book_set.all() # 找到第一个出版社出版的所有书
titles = books.values_list("title") # 找到第一个出版社出版的所有书的书名

结论:如果想通过一的那一方去查找多的一方,由于外键字段不在一这一方,所以用__set来查找即可

2.字段查找

语法:

表名__字段

要点:直接利用双下滑线完成夸表操作

titles = models.Publisher.objects.values("book__title")

ManyToManyField

class RelatedManager

"关联管理器"是在一对多或者多对多的关联上下文中使用的管理器。

它存在于下面两种情况:

  1. 外键关系的反向查询

  2. 多对多关联关系

简单来说就是在多对多表关系并且这一张多对多的关系表是有Django自动帮你建的情况下,下面的方法才可使用。

方法

create()

创建一个关联对象,并自动写入数据库,且在第三张双方的关联表中自动新建上双方对应关系。

models.Author.objects.first().book_set.create(title="偷塔秘籍")
上面这一句干了哪些事儿:
1.由作者表中的一个对象跨到书籍比表
2.新增名为偷塔秘籍的书籍并保存
3.到作者与书籍的第三张表中新增两者之间的多对多关系并保存

add()

把指定的model对象添加到第三张关联表中。

添加对象

>>> author_objs = models.Author.objects.filter(id__lt=3)
>>> models.Book.objects.first().authors.add(*author_objs)

添加id

>>> models.Book.objects.first().authors.add(*[1, 2])

set()

更新某个对象在第三张表中的关联对象。不同于上面的add是添加,set相当于重置

>>> book_obj = models.Book.objects.first()
>>> book_obj.authors.set([2, 3])

remove()

从关联对象集中移除执行的model对象(移除对象在第三张表中与某个关联对象的关系)

>>> book_obj = models.Book.objects.first()
>>> book_obj.authors.remove(3)

clear()

从关联对象集中移除一切对象。(移除所有与对象相关的关系信息)

>>> book_obj = models.Book.objects.first()
>>> book_obj.authors.clear()

注意:

对于ForeignKey对象,clear()和remove()方法仅在null=True时存在。

举个例子:

ForeignKey字段没设置null=True时,

class Book(models.Model):
  title = models.CharField(max_length=32)
  publisher = models.ForeignKey(to=Publisher)

没有clear()和remove()方法:

>>> models.Publisher.objects.first().book_set.clear()
Traceback (most recent call last):
File "<input>", line 1, in <module>
AttributeError: 'RelatedManager' object has no attribute 'clear'

当ForeignKey字段设置null=True时,

class Book(models.Model):
  name = models.CharField(max_length=32)
  publisher = models.ForeignKey(to=Class, null=True)

此时就有clear()和remove()方法:

>>> models.Publisher.objects.first().book_set.clear()

再次强调:

  1. 对于所有类型的关联字段,add()、create()、remove()和clear(),set()都会马上更新数据库。换句话说,在关联的任何一端,都不需要再调用save()方法。

案例

from django.db import models

class Goods(models.Model):#商品
   g_name = models.CharField(max_length=20)
   g_price = models.DecimalField(max_digits=5, decimal_places=2)
   gc = models.ForeignKey("Category", null=True, on_delete=models.SET_NULL)#gc为外键,类别表为母表

class Category(models.Model):#类别
   c_name = models.CharField(max_length=20)


class Store(models.Model):#商家
   s_name = models.CharField(max_length=30)
   s_detail = models.TextField(blank=True, null=True)
   sc = models.ManyToManyField("Category")#与类别表进行多对多关联

   
   
# 多对多增删改查同时也适用一对多,一对一,修改少量参数即可
# 1.增
添加商家
Store.objects.create(s_name="商家A", s_detail="物美价廉,抄底折扣。。。。")
Out[2]: <Store: Store object>

Store(s_name="商家B", s_detail="大促销").save()

添加类别
Category.objects.create(c_name="电脑整机")
<Category: Category object>

Category(c_name="文具耗材").save()

增与改(增添子表或母表数据参照一对一的增,多对多重点在于关系表的对应关系变更)

创建商家C添加全部分类
Store.objects.create(s_name="商家C").sc.add(*(Category.objects.all()))#如果商户已存在则把create改成get

store = Store.objects.get(s_name="商家C")
store.sc=(Category.objects.all())
store.save()


创建商家D添加指定分类
store = Store.objects.create(s_name="商家D")
category = Category.objects.filter(c_name__in=["电脑整机","文具耗材"])#单个改成get,全部改成all
store.sc.add(*category)#add是追加模式
store.sc.clear()#清空此商家的商品

#让指定商品分类添加指定的商家,反向查询
store = Store.objects.create(s_name="商家E")
category = Category.objects.get(c_name="电脑整机")
category.store_set.add(store)
效果与上面一样

让所有商家都添加这个分类
stores = Store.objects.all()
category = Category.objects.get(c_name="电脑整机")
category.store_set.add(*stores)

category.store_set.clear()#让所有商家去除这个分类
category.store_set.all().delete()#是删除store_set的所有商家
#只有子表才有"子表名小写_set"的写法,得到的是一个QuerySet集合,后边可以接.add(),.remove(),.update(),.delete(),.clear()



# 2.查
(Store.objects.get(s_name="商家C")).sc.all()
<QuerySet [<Category: Category object>, <Category: Category object>]>

Category.objects.filter(store__s_name="商家C")
<QuerySet [<Category: Category object>, <Category: Category object>]>

#查找指定商家下面的所有分类
Store.objects.get(s_name="商家C").sc.all()#写法:子表对象.子表多对多字段.过滤条件(all()/filter())
<QuerySet [<Category: Category object>, <Category: Category object>]>

反向查询
Category.objects.filter(store__s_name="商家C")
Out[25]: <QuerySet [<Category: Category object>, <Category: Category object>]>#母表对象.filter(子表表名小写__子表字段名="过滤条件")

从分类查询,查看那些商家里面发布指定的某一个分类
(Category.objects.get(c_name="电脑整机")).store_set.all()
<QuerySet [<Store: Store object>, <Store: Store object>]>

Store.objects.filter(sc=Category.objects.get(c_name="电脑整机"))
<QuerySet [<Store: Store object>, <Store: Store object>]>

Store.objects.filter(sc__c_name="电脑整机")#filter(子表外键字段__母表字段='过滤条件')
<QuerySet [<Store: Store object>, <Store: Store object>]>

Store.objects.filter(sc=category)##filter得到QuerySet,写法:filter(子表外键字段=母表主键对象),此处和一对多略有不同,是子表外键字段而不是外键字段_母表主键
<QuerySet [<Store: Store object>, <Store: Store object>]>



# 3.删
让指定商家清空分类
s = Store.objects.get(s_name="商家C")
c = Category.objects.all()
s.sc = ""
s.save()

s=Store.objects.get(s_name="商家C")
c = Category.objects.all()
s.sc.remove(*c)

s = Store.objects.get(s_name="商家C")
s.sc.clear()

删除母表与子表关联关系
让所有商家去掉指定分类

s = Store.objects.all()
c = Category.objects.get(c_name="电脑整机")
c.store_set.remove(*s)

c = Category.objects.get(c_name="电脑整机")
c.store_set.clear()

#删除商家子表数据
删除所有指定分类的全部商家

c=Category.objects.get(c_name="电脑整机")
c.store_set.all().delete()

删除所有商家
Store.objects.all().delete()

正反向查询总结

书-作者:多对多    书-出版社:多对一
作者-出版社:多对多 作者-作者详情:一对一

基于对象的正向查询:
对象.关联字段.字段 对象.关联字段.all() for循环.字段
加不加all()取决于获取的数据是不是多个,多在多对多的关系中
# 查询书籍pk=1的作者信息(书籍表——>作者表:一对多)
obj = models.Book.objects.filter(pk=1).first()
res = obj.authors.all() 一本书可能有多个作者
for author_obj in res:
print(author_obj.name)
print(obj.publish.name) 一本书只能在一个出版社出版

基于对象的反向查询:
对象.小写表名.字段 对象.小写表名_set.all() for循环.字段
加不加_set.all()取决于获取的数据是不是多个,多在一对多,多对多关系中
# 查询出版社pk=1的书籍的信息(出版社表——>书籍表:一对多)
obj = models.Pulish.objects.filter(pk=1).first()
res = obj.book_set.all() 一个作者下面可能有多本书
for book_obj in res:
print(book_obj.name)
# 查询作者信息pk=1的作者的信息(作者信息表——>作者表:一对一)
obj = models.Detail.objects.filter(pk=1).first()
res = obj.authors.name 一个作者详情对应一个作者


(基于双下划线的跨表查询,对应sql里面的连表操作)
基于字段的正向查询:
关联字段__字段
# 查询书籍pk=1的出版社的信息(书籍表——>出版社表:多对一)
res = models.Book.objects.filter(pk=1).values('publish__name', 'publish__addr')

# 查询书籍pk=2的作者的电话(跨了三张表)(书籍表——>作者表——>作者详情表)【author_detail为作者表中的外键字段】
res = models.Book.objects.filter(pk=2).values('authors__author_detail__phone')

基于字段的反向查询:
小写表名__字段
# 查询北方出版社出版的书籍(出版社表——>书籍表:一对多)
res = models.Pulish.objects.filter(name='北方出版社').values('book__name', 'book__price')


#### 双下划线进阶操作:在filter()中添加关联字段或者小写表名
# 查询主键为2的书籍对应的出版社(不能点书籍表),反向查询
res = models.Publish.objects.filter(book__pk=2).values('book__name', 'book_price')
# 查询主键为2的作者对应的书籍(不能点作者表),正向查询
res = models.Book.objects.filter(authors__pk=2).values('name', 'price')
 
 

聚合查询和分组查询

聚合(利用聚合函数)

aggregate()QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。

键的名称是聚合值的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。

用到的内置函数:

from django.db.models import Avg, Sum, Max, Min, Count

示例:

from django.db.models import Max,Min,Sum,Count,Avg
    # 1.统计所有书籍的总价格
    res1 = models.Book.objects.aggregate(Sum('price'))
    print(res1)
    # 2.统计所有书籍的总数
    res2 = models.Book.objects.aggregate(Count('pk'))
    print(res2)
    # 3.统计价格最高的
    res3 = models.Book.objects.aggregate(Max('price'))
    print(res3)
    res4 = models.Book.objects.aggregate(Max('price'),Min('price'),Sum('price'),Count('price'),Avg('price'))
    print(res4)

如果你想要为聚合值指定一个名称,可以向聚合子句提供它。

models.Book.objects.aggregate(average_price=Avg('price'))
{'average_price': 13.233333}

如果你希望生成不止一个聚合,你可以向aggregate()子句中添加另一个参数。所以,如果你也想知道所有图书价格的最大值和最小值,可以这样查询:

models.Book.objects.all().aggregate(Avg("price"), Max("price"), Min("price"))
{'price__avg': 13.233333, 'price__max': Decimal('19.90'), 'price__min': Decimal('9.90')}

分组

我们在这里先复习一下SQL语句的分组。

假设现在有一张公司职员表:

img

我们使用原生SQL语句,按照部分分组求平均工资:

select dept,AVG(salary) from employee group by dept;

ORM查询:

from django.db.models import Avg
Employee.objects.values("dept").annotate(avg=Avg("salary").values(dept, "avg")
这里需要注意的是annotate分组依据就是他前面的值,
如果前面没有特点的字段,则默认按照ID分组,
这里有dept字段,所以按照dept字段分组

连表查询的分组:

img

SQL查询:

select dept.name,AVG(salary) from employee inner join dept on (employee.dept_id=dept.id) group by dept_id;

ORM查询:

from django.db.models import Avg
models.Dept.objects.annotate(avg=Avg("employee__salary")).values("name", "avg")

更多示例:

示例1:统计每一本书的作者个数

book_list = models.Book.objects.all().annotate(author_num=Count("author"))
for obj in book_list:
...     print(obj.author_num)
...
1

示例2:统计出每个出版社买的最便宜的书的价格

publisher_list = models.Publisher.objects.annotate(min_price=Min("book__price"))
for obj in publisher_list:
...     print(obj.min_price)
...     
9.90
19.90

方法二:

models.Book.objects.values("publisher__name").annotate(min_price=Min("price"))
<QuerySet [{'publisher__name': '沙河出版社', 'min_price': Decimal('9.90')}, {'publisher__name': '人民出版社', 'min_price': Decimal('19.90')}]>

示例3:统计不止一个作者的图书

models.Book.objects.annotate(author_num=Count("author")).filter(author_num__gt=1)
<QuerySet [<Book: 番茄物语>]>

示例4:根据一本图书作者数量的多少对查询集 QuerySet进行排序

models.Book.objects.annotate(author_num=Count("author")).order_by("author_num")
<QuerySet [<Book: 香蕉物语>, <Book: 橘子物语>, <Book: 番茄物语>]>

示例5:查询各个作者出的书的总价格

models.Author.objects.annotate(sum_price=Sum("book__price")).values("name", "sum_price")
<QuerySet [{'name': '小精灵', 'sum_price': Decimal('9.90')}, {'name': '小仙女', 'sum_price': Decimal('29.80')}, {'name': '小魔女', 'sum_price': Decimal('9.90')}]>

总结

value里面的参数对应的是sql语句中的select要查找显示的字段,

filter里面的参数相当于where或者having里面的筛选条件

annotate本身表示group by的作用,前面找寻分组依据,内部放置显示可能用到的聚合运算式,后面跟filter来增加限制条件,最后的value来表示分组后想要查找的字段值

 

posted @ 2022-04-15 16:26  vonmo  阅读(270)  评论(0编辑  收藏  举报