模型层
目录
前期数据准备
"""
django自带的sqlite3数据库 功能很少 并且针对日期类型不精确
1.数据库正向迁移命令(将类操作映射到表中)
python3 manage.py makemigrations
python3 manage.py migrate
2.数据库反向迁移命令(将表映射成类)
python3 manage.py inspectdb
"""
需求
我们只想操作orm 不想使用网络请求
需要有专门的测试环境
1.自己搭建
tests.py文件
import os
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day54.settings")
import django
django.setup()
2.pycharm提供
python console
ORM操作关键字
all() # 查询所有数据 queryset
filter() # 根据条件筛选数据 多个条件默认是and关系 queryset
first() # 获取结果集中的第一个元素 数据对象
last() # 获取结果集中的最后一个元素 数据对象
values() # 可以看成是列表套字典 queryset
values_list() # 可以看成是列表套元组 queryset
exclude() # 取反操作 queryset
exists() # 判断结果集是否有值 布尔值
distinct() # 去重 queryset
order_by() # 排序 queryset
get() # 直接获取数据对象 数据对象
count() # 计数 int
create() # 创建数据
update() # 批量修改数据
delete() # 批量删除数据
"""
参数pk的含义:自动定位到当前表的主键字段
参数-的含义:用于排序中的升序与降序
"""
双下划线查询
算术运算
__gt 大于
__lt 小于
__gte 大于等于
__lte 小于等于
# 实例 查询年龄大于20的用户
res = models.User.objects.filter(age__gt=20)
print(res)
成员运算
__in
# 实例 查询年龄是18、22、25的用户
res = models.User.objects.filter(age__in=[18, 22, 25])
print(res)
范围查询
__range
# 实例 查询年龄在18到26之间的用户
res = models.User.objects.filter(age__range=[18, 26]) # 包含18和26
print(res)
字符串查询
__contains 区分大小写
__icontains 忽略大小写
# 实例 查询姓名中包含字母j的用户
res = models.User.objects.filter(name__contains='j')
res = models.User.objects.filter(name__icontains='j')
print(res)
时间查询
__year 按照年份筛选数据
__month 按照月份筛选数据
...
# 实例
# 查询月份是5月的数据
res = models.User.objects.filter(op_time__month=5)
print(res)
# 查询年份是22年的数据
res = models.User.objects.filter(op_time__year=2022)
print(res)
其他方法
__startswith # 以...开头
__endswith # 以...结尾
__regex # 正则
外键字段的创建
关系的种类
一对多关系
多对多关系
一对一关系
关系的判断
换位思考
字段的位置
一对多关系 外键字段建在多的一方
多对多关系 外键字段建在第三张关系表中
一对一关系 外键字段建在任意一方都可以 但是推荐建在查询频率较高的表中
一对多关系
# ForeignKey
'''django orm外键字段针对一对多关系也是建在多的一方 '''
res = models.ForeignKey(to='关联表名')
多对多关系
# ManyToManyField
'''django orm外键字段针对多对多关系 可以不用自己创建第三张表'''
res = models.ManyToManyField(to='关联表名') # res为虚拟字段
一对一关系
# OneToOneField
'''django orm外键字段针对一对一关系 建在查询频率较高的表中'''
res = models.OneToOneField(to='关联表名')
补充
1.ManyToManyField不会在表中创建实际的字段,而是告诉django orm自动创建第三张关系表
2.ForeignKey、OneToOneField会在字段的后面自动添加_id后缀,如果你在定义模型类的时候自己添加了该后缀,那么迁移的时候还会再次添加,_id_id,所以不要自己加_id后缀
3.to用于指定跟哪张表有关系 自动关联主键
to_field\to_fields 也可以自己指定关联字段
外键字段操作
一对多、一对一外键字段操作
增
1.
models.Book.objects.create(title='聊斋志异', price=16987.22, publish_id=1) # 直接填写关联数据的主键值
2.
publish_obj = models.Publish.objects.filter(pk=2).first() # 先获取一个被关联表的主键对象
models.Book.objects.create(title='资本论', price=56777.98, publish=publish_obj) # 然后在创建外键所在表的记录时,用对象代替关联数据的主键值
改
1.
models.Book.objects.filter(pk=1).update(publish_id=3) # 直接修改关联数据的主键值
2.
publish_obj = models.Publish.objects.filter(pk=2).first() # 先获取一个被关联表的主键对象
models.Book.objects.filter(pk=1).update(publish=publish_obj) # 用该对象代替关联数据的主键值
多对多字段操作
增
1.第三张关系表创建数据
book_obj = models.Book.objects.filter(pk=1).first() # 获得一个对象
book_obj.虚拟字段.add() # 虚拟字段 = models.ManyToManyField(to='关联表名')
'括号内可以放主键值也可以放数据对象,并且都支持多个'
改
2.第三张关系表修改数据
book_obj = models.Book.objects.filter(pk=1).first() # 获得一个对象
book_obj.虚拟字段.set()
'括号内必须是一个可迭代对象,元素同样支持主键值或者数据对象'
删
3.第三张关系表删除数据
book_obj = models.Book.objects.filter(pk=1).first() # 获得一个对象
book_obj.虚拟字段.remove()
'括号内可以放主键值也可以放数据对象 并且都支持多个'
清空
4.第三张关系表清空指定数据
book_obj = models.Book.objects.filter(pk=1).first() # 获得一个对象
book_obj.虚拟字段.clear() # 该对象在第三张关系表中的绑定记录全部清空
'括号内无需传值 直接清空当前表在第三张关系表中的绑定记录'
多表查询
MySQL多表查询思路
:
子查询:将SQL语句用括号括起来当做条件使用
连表操作:inner join\left join\right join\union
django orm多表查询思路
(本质还是使用的上述两种方法):
子查询:基于对象的跨表查询
连表操作:基于双下划线的跨表查询
正反向的概念
核心在于当前数据对象是否含有外键字段 有则是正向 没有则是反向
正向:
由书籍查询出版社 外键字段在书籍表中 那么书查出版社就是'正向'
由书籍查询作者 外键字段在书籍表中 那么书查作者就是'正向'
由作者查询作者详情 外键字段在作者表中 那么也是'正向'
反向:
由出版社查询书籍 外键字段不在出版社表 那么出版社查书就是'反向'
"""
查询口诀
正向查询按外键字段名
反向查询按表名小写
"""
基于对象的跨表查询
1.获取已知对象
2.使用跨表查询
3.得到查询对象
# 实例 查询数据分析书籍对应的出版社
# 先获取书籍对象
book_obj = models.Book.objects.filter(title='数据分析').first()
# 再使用跨表查询
res = book_obj.publish # 正向查询按外键字段名
print(res) # 得到一个出版社对象
# 实例 查询东方出版社出版的书籍
publish_obj = models.Publish.objects.filter(name='东方出版社').first()
res = publish_obj.book_set # app01.Book.None
res = publish_obj.book_set.all() # 反向查询按表名小写
print(res)
基于双下划线的跨表查询
基于双下划线的跨表查询本质就是连表操作
1.手上有什么条件就先拿models点该条件对应的表名
2.values('关联表名__字段名' ,'关联表名__关联表名__字段名',),可以多个参数 并且关联表可以不断延续
# 实例 查询数据分析书籍对应的价格和出版日期
models.Book.objects.filter(title='数据分析').values('price','publish_time')
# 实例 查询作者jason的手机号和地址
res = models.Author.objects.filter(name='jason').values('author_detail__phone','author_detail__addr')
双下划线查询扩展
基于双下划线的跨表查询的结果也可以是完整的数据对象
1.手上有条件所在的表可以不被models点 直接点最终的目标数据对应的表
2.filter(条件所在表名__字段名='XXX') 关联表可以不断延续
3.获取到一个对象
# 实例 查询数据分析书籍对应的出版社名称
1.获取对象
res = models.Publish.objects.filter(book__title='数据分析')
print(res) # <QuerySet [<Publish: 出版社对象:北方出版社>]>
2.获取属性
res = models.Publish.objects.filter(book__title='数据分析').values('name')
print(res) # <QuerySet [{'name': '北方出版社'}]>
'''连续跨表操作'''
# 实例 查询python全栈开发对应的作者的手机号
res1 = models.AuthorDetail.objects.filter(author__book__title='python全栈开发').values('phone')
print(res1)
如何查看SQL语句
方式1:如果结果集对象是queryset 那么可以直接点query查看
res = models.Publish.objects.filter(book__title='数据分析')
print(res.query)
方式2:配置文件固定配置
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console':{
'level':'DEBUG',
'class':'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'propagate': True,
'level':'DEBUG',
},
}
}
适用面更广 只要执行了orm操作 都会打印内部SQL语句
聚合查询
aggregate()
是QuerySet
的一个终止子句,意思是说,它返回一个包含一些键值对的字典。
from django.db.models import Max, Min, Sum, Avg, Count
# 实例
res = models.Book.objects.aggregate(Max('price'),
Min('price'),
Sum('price'),
Avg('price'),
Count('pk')
)
print(res)
{'price__max': Decimal('56777.98'), 'price__min': Decimal('16987.22'), 'price__sum': Decimal('179408.51'), 'price__avg': 29901.418333, 'pk__count': 6}
分组查询
ORM使用annotate()
执行分组操作,如果报错,可能需要去修改sql_mode,移除only_full_group_by
# 实例
# 统计每本书的作者个数
from django.db.models import Max, Min, Sum, Avg, Count
res = models.Book.objects.annotate(author_num=Count('authors__pk')).values('title', 'author_num')
# 统计每个出版社卖的最便宜的书的价格
res = models.Publish.objects.annotate(min_price=Min('book__price')).values('name', 'min_price')
"""上述操作都是以表为单位做分组 如果想要以表中的某个字段分组如何操作"""
# 统计每个出版社主键值对应的书籍个数
res = models.Book.objects.values('publish_id').annotate(book_num=Count('pk')).values('publish_id','book_num')
print(res)