Django ORM 高级操作
- 补充: Django框架中只要是跟数据库相关的模块基本都在django.db.models 中,如果没有就应该在django.db里面
聚合查询
聚合查询(aggregate)通常情况下都是配合分组一起使用的
使用方式:
1. 导入模块
from django.db.models import Max,Min,Sum,Count,Avg
2. 使用格式
models.表名.objects.aggregate(函数1('字段名'),函数1('字段名')...)
eg:
modles.Book.objects.aggregate(Max('price'),Min('price'),Sum('price'),Count('pk'),Avg('price'))
分组查询
mysql分组查询特点:
- 在5.7及以上版本,分组查询默认只能直接获取分组字段信息,组内其他字段都无法直接获取
- 5.6 版本需要设置严格模式(ONLY_FULL_GROUP_BY)
ROM分组语法:
models.表名.objects.annotate()
注意: models点什么表名就默认按照什么分组
案例:
1.统计每一本书的作者个数
models.Book.objects.annotate(author_num=Count('authors')).values('title','author_num')author_num是我们自己定义的字段 用来存储统计出来的每本书对应的作者个数
可以自定义字段用来配合聚合函数使用 author_num是我们自己定义的字段
按照指定字段分组处理语法:
models.Book.objects.values('字段').annotate()
'annotate 前面加了values 就会按照values内指定字段分组'
F与Q查询
- F查询(进行表内字段的比较)
F查询能够直接获取表中某个字段对应的数据
语法:
from django.db.models import F
1. 查询Book表中kucun大于maichu的数据
models.Book.objects.filter(kucun__gt=F('maichu'))
2. 将所有书籍的价格提升500块
models.Book.objects.update(price=F('price')+500)
'''
F 查询可以操作数值型数据运算,但是在操作字符类型的数据的时候 不能够直接做到字符串拼接
'''
from django.db.models.functions import Concat
from django.db.models import Value
# Concat配合Value 就可以用来进行字符类型数据拼接,执行不会报错但是数据会变成空白
models.Book.objects.update(title=Concat(F('title'),Value('xxx')))
- Q查询
由于filter括号内多个参数是and关系,没有其他关系(or not)
from django.db.models import Q
这个时候就需要Q查询
modles.Book.objects.filter(Q(maichu_gt=100),Q(price_lt=600)) # 默认还是and关系 ,
modles.Book.objects.filter(Q(maichu_gt=100)|Q(price_lt=600)) # or关系 |
modles.Book.objects.filter(~Q(maichu_gt=100)|~Q(price_lt=600)) # not ~
Q 的高阶用法 够将查询条件的左边也变成字符串的形式
q = Q()
q.connector = 'or' # 修改关系
q.children.append(('maichu__gt',100))
q.children.append(('price__lt',600))
models.Book.objects.filter(q) # 默认还是and关系
Django开启事务
事务: ACID 四个特性 原子性,一致性,隔离性,持久性
from django.db import transaction
with transaction.atomic(): # 开启事务
sql1
sql2
...
# 在with代码块内书写的所有orm操作都是属于同一个事务
ORM中常用参数
AutoField
主键字段 primary_key=True
CharField varchar
verbose_name 字段的注释
max_length 长度
IntegerField int
BigIntegerField bigint
DecimalField
max_digits=8
decimal_places=2
EmailFiled varchar(254)
DateField date
DateTimeField datetime
auto_now:每次修改数据的时候都会自动更新当前时间
auto_now_add:只在创建数据的时候记录创建时间后续不会自动修改了
BooleanField(Field) - 布尔值类型
该字段传布尔值(False/True) 数据库里面存0/1
TextField(Field) - 文本类型
该字段可以用来存大段内容(文章、博客...) 没有字数限制
后面的bbs作业 文章字段用的就是TextField
FileField(Field) - 字符类型
upload_to = "/data"
给该字段传一个文件对象,会自动将文件保存到/data目录下然后将文件路径保存到数据库中
# 更多字段
直接参考博客:https://www.cnblogs.com/Dominic-Ji/p/9203990.html
- 自定义字段
# django除了给你提供了很多字段类型之外 还支持你自定义字段
class MyCharField(models.Field):
def __init__(self,max_length,*args,**kwargs):
self.max_length = max_length
# 调用父类的init方法
super().__init__(max_length=max_length,*args,**kwargs) # 一定要是关键字的形式传入
def db_type(self, connection): # 一定要有
"""
返回真正的数据类型及各种约束条件
:param connection:
:return:
"""
return 'char(%s)'%self.max_length
- 外键字段及参数
unique=True
ForeignKey(unique=True) === OneToOneField()
# 你在用前面字段创建一对一 orm会有一个提示信息 orm推荐你使用后者但是前者也能用
db_index
如果db_index=True 则代表着为此字段设置索引
to_field
设置要关联的表的字段 默认不写关联的就是另外一张的主键字段
on_delete
当删除关联表中的数据时,当前表与其关联的行的行为。
"""
django2.X及以上版本 需要你自己指定外键字段的级联更新级联删除
"""
数据库查询优化
ORM语句特点(惰性查询):
- 如果仅仅书写了orm语句,在后面如果没有用到该语句所查询出来的参数,那么orm会自动识别 直接不执行
- 数据库优化两队参数(only与defer)和(select_related与prefetch_related)
only与defer
only作用: 只获取指定字段的数据
modles.表名.objects.only('字段名')
defer作用: 获取指定字段之外的数据
modles.表名.objects.defer('title')
注意:
使用only获取的quertser对象同样可以获取其他字段数据,不过需要重新在数据库中查询
defer括号内放的字段不在查询出来的对象里面 查询该字段需要重新走数据
而如果查询的是非括号内的字段 则不需要走数据库了
select_related与prefetch_related
select_related 可以看做是 INNER JOIN
可以通过外键字段将两张表连起来,然后一次性将大表里面的所有数据全部封装起来给查询出的对象,这个时候查询两张表内的数据都无需再走数据库。
注意: select_related 括号内只能放外键字段 只支持一对多和一对一关系表使用
prefetch_related 可以看成是 子查询
prefetch_related内部就是子查询
会将子查询查询出来的结果(默认是按照外键字段查询)封装到对象中
choice参数
- choice参数是数据库字段设计中十分常见的一个字段
- 该字段可以针对某个可以列举完全的可能性字段的存储方式
- 只要某个字段的可能性是可以列举完全的,那么一般情况下都会采用choices参数
class User(models.Model):
username = models.CharField(max_length=32)
age = models.IntegerField()
# 性别
gender_choices = (
(1,'男'),
(2,'女'),
(3,'其他'),
)
# 保证字段类型和对应choices内元组第一个参数一致
gender = models.IntegerField(choices=gender_choices)
存: # 存的时候 正常存即可,即使不在列举出来的范围内的数据也能存储(前提:在字段类型内)
models.User.objects.create(username='jason',age=18,gender=1)
models.User.objects.create(username='tony',age=45,gender=4)
取:
user_obj = models.User.objects.filter(pk=1).first()
# 只要是choices参数的字段 如果你想获取对应信息 固定写法 get_字段名_display()
print(user_obj.get_gender_display()) # 男
# 取的时候,如果没有对应关系,那么字段值是什么就还是展示什么
user_obj = models.User.objects.filter(pk=2).first()
print(user_obj.get_gender_display()) # 4
MTV与MVC模型
1. MTV : Django专属MTV模型
M:models (模型层:跟数据相关)
T:templates (模板层:html页面)
V:views (逻辑层:代码逻辑相关)
2. MVC : 其他语言,Django本质上也是MVC
M : model (数据处理层)
V : Views (视图页面层)
C : controller (控制器,主要写逻辑)
S : service (服务处,大型项目中抽象出的第四层)
多对多三种创建方式
1. 全自动
class Book(models.Model):
name = models.CharField(max_length=32)
authors = models.ManyToManyField(to='Author')
class Author(models.Model):
name = models.CharField(max_length=32)
'''
优点: 不需要手动创建第三张表,支持orm提供操作第三张关系表的方法
缺点: 第三张表的扩展性极差,没有办法额外添加字段
'''
2. 纯手动
class Book(models.Model):
name = models.CharField(max_length=32)
class Author(models.Model):
name = models.CharField(max_length=32)
class BooktoAuthor(models.Model):
book_id = models.ForeignKey(to='Book')
author_id = models.ForeignKey(to='Author')
'''
优点: 第三张表完全取决于你自己进行的额外的扩展
缺点: 代码加多,不能够使用orm提供的简单方法(不推荐使用)
'''
3. 半自动
class Book(models.Model):
name = models.CharField(max_length=32)
authors = models.ManyToManyField(to='Author',
through='Book2Author',
througn_fields=('book','author')
)
class Author(models.Model):
name = models.CharField(max_length=32)
class Book2Author(models.Model):
book = models.ForeignKey(to='Book')
author = models.ForeignKey(to='Author')
'''
through: 指定第三张表
through_fields字段先后顺序
判断的本质:
通过第三张表查询对应的表,需要那个字段就把那个字段放在前面
简化:
当前表是谁 就把对应的关联字段放在前面
优点: 可以使用orm的正反向查询,第三张表的可扩展性强
缺点: 代码多了 , 没法使用add,set,remove,clear这四个方法
'''
总结:
三种多对多的方法,需要掌握全自动和半自动,为了可扩展性更高 推荐使用半自动