django orm(2)
聚合函数
这里的聚合函数和SQL里的聚合函数对应,在使用前需要先进行模块的导入:
from django.db.models import Max,Min,Sum,Count,Avg
常用的聚合函数有求最大值、最小值、和、计数、平均数,具体使用看下面几个例子:
-
筛选出价格最高的书籍
res = models.Book.objects.aggregate(mr = Max('price'))
-
求书籍的总价格
res1 = models.Book.objects.aggregate(mr = Sum('price'))
-
求书籍的平均价格
res1 = models.Book.objects.aggregate(agv1 = Agv('price'))
-
求书籍的最大值、最小值、平均价格、总价格
res1 = models.Book.objects.aggregate(mp = Max('price'), mi = Min('price'),cou = Count('price'),sum1 = Sum('price'),avg1 = Avg('price')) print(res1) #{'mp': Decimal('100.00'), 'mi': Decimal('10.00'), 'cou': 6, 'sum1': Decimal('312.00'), 'avg1': 52.0}
分组查询
分组查询主要应用在比如查询班级中男生、女生的个数等需要先分组再查询的场景,分组操作使用的annotate内部调用的是SQL语句group by,分着查询需要和聚合函数联用。按谁分组,models就是谁,annotaten内部传入筛选的条件。
通过下面几个例子看Django的分组查询:
1.统计每一本书的作者的个数
这里就需要每一本书为一组,然后统计每本书的作者的个数:
res = models.Book.objects.annotate(author_num=Count('author__id')).values('name','author_num')
print(res)
#<QuerySet [{'name': '乖,摸摸头', 'author_num': 2}, {'name': '向着光亮那方', 'author_num': 1}, {'name': '你坏', 'author_num': 1}, {'name': '你的孤独虽败犹荣', 'author_num': 1}, {'name': '三体', 'author_num': 0}, {'name': '乡村教师', 'author_num': 0}]>
2.统计出每个出版社卖的最便宜的书的价格
res = models.Publisher.objects.annotate(min_price=Min('book__price')).values('publisher_name','book__name','book__price')
print(res)
#<QuerySet [{'publisher_name': '湖南文艺出版社', 'book__name': '你坏', 'book__price': Decimal('49.00')}, {'publisher_name': '大象出版社', 'book__name': '乖,摸摸头', 'book__price': Decimal('50.00')}, {'publisher_name': '人民文学出版社', 'book__name': '向着光亮那方', 'book__price': Decimal('51.00')}]>
按照某一个字段进行分组,然后按照另一字段进行查找或排序。
# res = models.Publish.objects.values('想要分组的字段名').annotate(min_price=Min('book__price')).values('name','min_price')
res = models.Book.objects.values('price').annotate(min_price=Min('price')).values('name','min_price')
print(res)
#<QuerySet [{'name': '乖,摸摸头', 'min_price': Decimal('50.00')}, {'name': '向着光亮那方', 'min_price': Decimal('51.00')}, {'name': '你坏', 'min_price': Decimal('49.00')}, {'name': '你的孤独虽败犹荣', 'min_price': Decimal('52.00')}, {'name': '三体', 'min_price': Decimal('100.00')}, {'name': '乡村教师', 'min_price': Decimal('10.00')}]>
3.统计不止一个作者的图书
res = models.Book.objects.annotate(author_num=Count('author__id')).filter(author_num__gt=1).values('name','author_num')
print(res)
#<QuerySet [{'name': '乖,摸摸头', 'author_num': 2}]>
4.查询各个作者出的书的总价格,打印作者名字,总价格
关键字:annotate
借助于聚合函数,Django中models后面点什么就按什么分组
res2 = models.Author.objects.annotate(sum_price=Sum('book__price')).values('auth_name','sum_price')
# print(res2)
#<QuerySet [{'auth_name': '刘同', 'sum_price': Decimal('153.00')}, {'auth_name': '大冰', 'sum_price': Decimal('99.00')}, {'auth_name': '刘慈欣', 'sum_price': None}]>
F与Q查询
使用F和Q查询前需要先导入模块:
from django.db.models import F,Q
F查询
F查询主要解决字段与字段间对比查询的问题,双下划线查询只能实现单个字段的范围查询,引入F查询我们就能够实现不同字段间的对比查询,比如查询出库存数大于卖出数的书籍就是库存和卖出两个字段的对比查询。这里的字段需要在同一张表内方可。
# 1. 查询库存数大于卖出数的书籍
res3 = models.Book.objects.filter(stock__gt=F('sell')).values('name')
print(res3)
#<QuerySet [{'auth_name': '刘同', 'sum_price': Decimal('153.00')}, {'auth_name': '大冰', 'sum_price': Decimal('99.00')}, {'auth_name': '刘慈欣', 'sum_price': None}]><QuerySet []>
# 2. 将所有书的价格上涨100块
book_objs = models.Book.objects.all().update(price=F('price')+100)
print(book_objs.values('price'))
# 3.将所有书的名称后面全部加上 "爆款" 后缀,操作字符串数据需要借助于Concat方法
from django.db.models.functions import Concat
from django.db.models import Value
res4 = models.Book.objects.update(name=Concat(F('name'),Value('新作')))
Q查询
利用Q查询可以实现filter()查询时,查询条件的或非(| 、~)的关系。
说明:如果Q对象和关键字参数一起使用,Q对象必须放在前面。
# 1.查询一下书籍名称是乡村教师 或者 库存数是500的书籍
res4 = models.Book.objects.filter(Q(name='乡村教师新作')|Q(stock=500)).values('name')
print(res4)#或
#<QuerySet [{'name': '三体新作'}, {'name': '乡村教师新作'}]>
#查询出除乡村教师新版和库存数等于500的书籍名称
res5 = models.Book.objects.filter(~Q(name='乡村教师新作')|~Q(stock=500)).values('name')
print(res5)#非
Q对象的高级用法
我们可以根据要求不断给q对象添加筛选条件,然后进行查询,如:
# 1.查询一下书籍名称是乡村教师 或者 库存数是500的书籍
q = Q()#实例化一个Q的对象,之后我们可以按照我们的要求给q添加条件
q.connector = 'or'#默认是and,这里设置的就是各筛选条件之间的关系
q.children.append(('name','乡村教师新作'))#添加筛选条件name='乡村教师'
q.children.append(('stock',500))#添加筛选条件stock=500
res6 = models.Book.objects.filter(q).values('name')#按照我们的要求进行筛选
print(res6)
事务
事务的操作相当于起了一个单独的进程,当操作执行成功之前,数据库中的数据不会有任何的变化,只有当操作结束且执行成功了数据库中的数据才会发生变化,而且这个变化是不可逆的,在操作未执行结束时(没有提交数据时)可以如果我们的操作有错误,可以执行回滚指令,撤销前面的操作(这里的撤销是撤销前面的所有),事务的原子性其实就是我们起一个事务对多张表进行数据操作时,要么这多张表同时被修改成功,要么都不被修改。银行转账就是利用了事务的这一特点。
Django中的事务
还未进行详细讲解
orm字段及参数
常用字段:
字段 | 描述 |
---|---|
AutoField | int自增列,必须填入参数 primary_key=True。当model中如果没有自增列,则自动会创建一个列名为id的列。 |
IntegerField | 一个整数类型,范围在 -2147483648 to 2147483647。(一般不用它来存手机号(位数也不够),直接用字符串存,) |
CharField | varchar类型,必须提供max_length参数, max_length表示字符长度。 |
DateField | 日期字段,日期格式 YYYY-MM-DD,相当于Python中的datetime.date()实例。 |
DateTimeField | 日期时间字段,格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ],相当于Python中的datetime.datetime()实例。 |
字段参数:
参数 | 描述 |
---|---|
null | 用于表示某个字段可以为空。 |
unique | 如果设置为unique=True 则该字段在此表中必须是唯一的 。 |
db_index | 如果db_index=True 则代表着为此字段设置索引。 |
default | 为该字段设置默认值。 |
auto_now_add | (日期与时间字段参数)配置auto_now_add=True,创建数据记录的时候会把当前时间添加到数据库。 |
auto_now | (日期与时间字段参数)配置上auto_now=True,每次更新数据记录的时候会更新该字段。 |
Django字段集合
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)
- 二进制类型
字段合集
Django的字段与数据库字段的对应关系:
对应关系:
'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)',
ORM字段与MySQL字段对应关系
自定义char字段
自定义字段可以使用下面的方法,其实我们也只是更改一下字段的数据长度、字段的名字等,Django中的字段已经很丰富了。
# 如何自定义字段类型
class MyCharField(models.Field):
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):
return 'char(%s)'%self.max_length