多则价谦,万物皆然,唯独知识例外。知识越丰富,则价值就越昂贵。——马戈
模型层
文章目录
- 模型层
- 一、Django测试环境搭建
- 二、单表查询关键字操作之增删改查
- 三、QuerySet对象查询方法介绍
- 四、查看内部封装的sql语句的2种形式
- 五、单表查询之神奇的双下划线(范围查询)
- 五、ORM常用字段介绍
- 六、常用于非常用字段类型合集(联想记忆:与MySQL字段对应关系)
- 七、字段参数
- 八、自定义字段及使用
- 九、手动创建第三张表
- 十、准备表和基础数据
- 十一、一对多表结构的增删改
- 十二、多对多表结构的增删改(API)本质就是操作虚拟中间表(第三张表)
- 十三、查询储备知识
- 十四、子查询:基于对象的跨表查询
- 十五、基于双下划线的跨表查询 : 连表查询
- 十六、聚合查询 aggregate( *args,**kwargs)
- 十七、分组查询 annotate
- 十八、F 查询
- 十九、Q查询
- 二十.Meta 元信息
- 二十一.原生 SQL
- 二十二.重点大补充
一、Django测试环境搭建
当你只是想测试django中的某一个py文件内容 那么你可以不用书写前后端交互的形式, 而是直接写一个测试脚本即可:
1.方式一
- 使用脚本形式:任意创建一个 py 文件, 在该文件内书写固定的配置(可以去manage.py文件中去复制前四行)
# 测试环境的准备 去manage.py中拷贝前四行代码 然后自己写两行
import os
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day64.settings")
import django
django.setup()
# 在这个代码块的下面就可以测试django里面的单个py文件了(注意: 导模块也要写在这下面)
2.方式二
- 直接使用pycharm提供的Python console窗口
补充:
- 使用
pycharm
链接数据库都需要提前下载对应的驱动- 自带的
sqlite3
对日期格式数据不敏感
- 如果后续业务需要使用日期辅助筛选数据那么不推荐使用
sqlite3
二、单表查询关键字操作之增删改查
准备表
# 在models.py文件准备表
class Books(models.Model):
title = models.CharField(verbose_name='书名', max_length=32)
price = models.DecimalField(verbose_name='价格', max_digits=8, decimal_places=2)
publish_time = models.DateField(auto_now_add=True)
'''
auto_now:每次修改数据的时候都会自动更新当前时间
auto_now_add:只在数据创建的时候记录一次创建时间,后续不会自动更改
'''
# 然后使用数据库迁移同步命令
python3 manage.py makemigrations
manage.py migrate
- 增
#方式一:create方法
res = models.Books.objects.create(title='十年', price=9.9)
#方式二:创建对象再()save保存
ccd = models.Books(title='庆余年',price=18.1)
ccd.save()
- 删
bool_obj = models.Books.objects.filter(title='十年', price=9.9).delete()方式一:delete方法
row = models.Books.objects.all().delete()
print(row) # row 为影响的条数
rows = models.Books.objects.filter(pk=2).delete() #删除主键字段为2的
方式二:获取对象再进行删除delete()
bool_obj = models.Books.objects.filter(title='十年', price=9.9).delete()
- 改
方式一:update()
models.Books.objects.filter(title='创业邦').update(price=99)
方式二:get()获取对象再进行赋值再save()保存
book_obj = models.Books.objects.get(pk=9)
book_obj.price = 999.99
book_obj.save() # 效率低(所有字段重新写一遍)
补充:
1.pk能够自动查找到当前表的主键字段 我们不需要查看当前表主键字段名
2.get括号内可以放多个参数 默认是and关系 不推荐使用 条件不符合直接报错
- 查
- QuerySet对象的查询方法
三、QuerySet对象查询方法介绍
1.QuerySet介绍
-
数据库接口相关的接口(QuerySet API)
-
QuerySet对象它是一个生成器,也可以将其看成一个列表
-
ORM提供了13种查询API
2.ORM13种查询API介绍
1.all():查询所有结果
- 返回QuerySet对象
- 用
.all( )
取值的时候,不会一下子全部取出来, 而是每次只取21条 (LIMIT21)
res = models.Books.objects.all()
print(res) # <QuerySet [<Books: Books object (9)>, <Books: Books object (10)>, <Books: Books object (12)>, <Books: Books object (13)>, <Books: Books object (15)>, <Books: Books object (16)>, <Books: Books object (18)>, <Books: Books object (19)>]>
2.filter(**kwargs):过滤
- 返回QuerySet对象. 筛选出与所给筛选条件相匹配的数据对象
- 不指定筛选条件则默认查询所有
- 当结果不存在返回空的QuerySet对象, 布尔值为False
res = models.Books.objects.filter(title='创业邦')
print(res) #<QuerySet [<Books: Books object (9)>, <Books: Books object (12)>, <Books: Books object (15)>, <Books: Books object (18)>]>
bok_obj = models.Books.objects.filter() # 筛选所有,等同于帅选所有
print(bok_obj) #<QuerySet [<Books: Books object (9)>, <Books: Books object (10)>, <Books: Books object (12)>, <Books: Books object (13)>, <Books: Books object (15)>, <Books: Books object (16)>, <Books: Books object (18)>, <Books: Books object (19)>]>
book_obj = models.Books.objects.filter(pk=8888)
print(book_obj, bool(book_obj)) #<QuerySet []> False
3.get( **kwargs ) : 筛选表中的数据对象
- 直接获取数据对象
- 只能指定一个筛选条件,如果指定的筛选条件返回的结果不唯一 或者不存在抛出异常
book_get_queryset = models.Books.objects.get(pk=10)
print(book_get_queryset) # Books object (10)
条件不存在抛出异常
book_obj = models.Books.objects.get(pk=8888) #app01.models.DoesNotExist: Books matching query does not exist.
book_get_queryset = models.Books.objects.get(title='十年') #get() returned more than one Books -- it returned 3!
4.last():最后一个对象
- 获取QuerySet列表中最后一个数据对象.
- 如果QuerySet对象为空, 再使用它返回None
book_last_queryset = models.Books.objects.filter().last()
print(book_last_queryset) #Books object (22)
book_last_queryset = models.Books.objects.filter(pk=999).last()
print(book_last_queryset) # None
5.first( ) : 第一个对象
- 直接获取QuerySet列表中第一个数据对象.
- 如果QuerySet对象为空, 再使用它返回None
book_first_last = models.Books.objects.filter(title='庆余年').first()
print(book_first_last) #Books object (10)
book_first_queryset = models.Books.objects.filter(pk=8888).first()
print(book_first_queryset) #None
6.values(*field) :返回指定字段对象集
- 返回QuerySet对象,内部是一种列表套字典的格式
- 字典的key就是指定的字段名,指定字段若不存在抛出异常
book_values_queryset=models.Books.objects.values('title','price')
print(book_values_queryset) #<QuerySet [{'title': '创业邦', 'price': Decimal('999.99')},,... {'title': '十年', 'price': Decimal('9.90')}]>
#注意:指定字段不存在抛出异常
book_values_queryset = models.Books.objects.values('xxxx')
print(book_values_queryset) #django.core.exceptions.FieldError: Cannot resolve keyword 'xxxx' into field. Choices are: id, price, publish_time, title
7.values_list(*field):返回指定字段对象集
- 返回QuerySet对象, 内部是一种列表套元组的格式
- 字典的key就是指定的字段名,指定字段不存在就抛出异常
book_values_queryset = models.Books.objects.values_list('title', 'price')
print(book_values_queryset) #<QuerySet [('创业邦', Decimal('999.99')), ... ('十年', Decimal('9.90'))]>
#指定字段不存在就抛出异常
book_values_queryset = models.Books.objects.values_list('xxx')
print(book_values_queryset) #django.core.exceptions.FieldError: Cannot resolve keyword 'xxx' into field. Choices are: id, price, publish_time, title
8.distinct():去重
- 注意: 必须排除主键字段 或 唯一字段才会有意义
- 要排除使用filter无法晒选,一般用在
.values()
或.value_list()
后面
book_values_queryset = models.Books.objects.values_list('title')
dis_book_queryset = book_values_queryset.distinct()
print(dis_book_queryset) #<QuerySet [('创业邦',), ('庆余年',), ('十年',)]>
9.order_by(*field):排序
- 对查询结果进行指定字段的排序
- 默认升序. 如果想要降序在对应要查询的字段前指定
-
号
book_order_by=models.Books.objects.values('title').order_by('title') #默认升序
book_order_by=models.Books.objects.values('title').order_by('-title') # - 号降序
book_order_by =models.Books.objects.all().order_by('title')
book_order_by = models.Books.objects.all().order_by('-age')
10.reverse( ) : 排序
- 对查询结果进行反向排序
- 反转的前提是数据已经排过序. 没排过序reverse将不起作用.
book_reverse = models.Books.objects.all().order_by('title').reverse() #先进行排序,在反转
book_reverse = models.Books.objects.all().order_by('-title').reverse()
11.count():统计个数
- 查询QuerySet内部所包含的数据对象的个数
book_count =models.Books.objects.filter().count()
print(book_count) # 11
bok_count = models.Books.objects.all().count()
print(bok_count) # 11
12.exclude( **kwargs ) : 筛选出不匹配的对象
- 查询与所给筛选条件不匹配的对象, 类似于取反
book_exclude=models.Books.objects.exclude(title='庆余年')
print(book_exclude)
13…exists( ) : 判断数据是否存在
- 如果QuerySet包含数据, 就返回True, 否则返回False
book_exists = models.Books.objects.filter(title='创业邦', price=99).exists()
print(book_exists) # True
box_exists = models.Books.objects.filter(title='青年杂志').exists()
print(box_exists) # False
3.总结
# 返回QuerySet对象的方法
.all() 获取所有.
.filter() 过滤. 不指定参数获取所有. 查询结果不存在返回空的QuerySet对象, 布尔值False
.distinct() 去重. 必须排除主键字段 或 唯一字段才会有意义. filter无法筛选, 一般用在.values() 或 .value_list()后面
.order_by() 排序. 默认升序. 指定降序在字段前加`-`
.reverse() 反转. 只能对排序过后进行反转. 必须在.order_by()之后.
.exclude() 排除. 排除指定的, 展示所有.
# 返回特殊QuerySet对象的方法
.values() 返回QuerySet之内部列表套字典格式. 字典key就是指定的字段.
.values_list() 返回QuerySet之内部列表套元组格式. 元组中值就是指定字段对应的值, 按照指定字段顺序显示.
注意!!!: 指定的字段不存在, 将会抛出异常
# 返回数据对象的方法
.get() 直接获取数据对象. 只能指定一个筛选条件. 如果指定的筛选条件返回的结果不唯一 或者 不存在 抛出异常
.first() 获取QuerySet对象列表内第一个值. 用在QuerySet对象之后, 如果QuerySet对象为空, 再使用它返回None
.last() 获取QuerySet对象列表内最后一个值. 用在QuerySet对象之后, 如果QuerySet对象为空, 再使用它返回None
# 返回数字的方法
.count() 统计QuerySet对象列表内数据对象的个数
# 返回布尔值的方法
.exist() 没软用
4.拓展
QuerySet 是可迭代的,比如:
es = models.Books.objects.all()
for e in es:
print({'书名': e.title, '价格': e.price})
QuerySet 支持切片 Entry.objects.all()[:10] 取出10条,可以节省内存
es = models.Books.objects.all()[:2]
print(es) # <QuerySet [<Books: Books object (9)>, <Books: Books object (10)>]>
用 len(es) 可以得到Entry的数量,但是推荐用 Entry.objects.count()来查询数量
es = models.Books.objects.all()
print(len(es))
list(es) 可以强行将 QuerySet 变成 列表
es = models.Books.objects.all()[:2] #前两条
print(list(es))
QuerySet 不支持负索引
book_index = models.Books.objects.all().reverse()[:2] # 最后两条
print(book_index)
box_index = models.Books.objects.filter().reverse()[0] # 最后一条
print(box_index)
bok_index =models.Books.objects.order_by('-price')[:1]
print(bok_index)
QuerySet 是可以用pickle序列化到硬盘再读取出来的
>>> import pickle
>>> query = pickle.loads(s) # Assuming 's' is the pickled string.
>>> qs = MyModel.objects.all()
>>> qs.query = query # Restore the original 'query'.
四、查看内部封装的sql语句的2种形式
# 第一种: QuerySet.query
user_queryset = models.User.objects.values_list('name', 'age')
print(user_queryset.query) # SELECT `app01_user`.`name`, `app01_user`.`age` FROM `app01_user`
# 第二种: 执行脚本时打印日志显示到终端. 复制以下日志内容到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',
},
}
}
五、单表查询之神奇的双下划线(范围查询)
1.参数介绍
参数 | 释义 |
---|---|
__in | 是否在给定的数据集中 |
__gt | 大于 |
__lt | 小于 |
__gte | 大于等于 |
__lte | 小于等于 |
__range | 在…之间, 闭区间 |
__contains | 包含 |
__icontains | 包含(忽略大小写) |
__startswith | 以…开头 |
__endswith | 以…结尾 |
__year | 取出年份(仅限于时间类型) |
__month | 取出月份(仅限于时间类型) |
__day | 取出日期(仅限于时间类型) |
__hour | 取出小时(仅限于时间类型) |
__minute | 取出分钟(仅限于时间类型) |
__second | 取出秒(仅限于时间类型) |
__week_day | 一周的第几天(仅限于时间类型) |
2.示例
# 1.查询价格大于100的书籍
res = models.Books.objects.filter(price__gt=100)
print(res)
print(res.query)
# 2.查询价格小于100的书籍
res = models.Books.objects.filter(price__lt=100)
print(res)
print(res.query)
# 价格大于等于99 小于等于99
res1 = models.Books.objects.filter(price__gte=99)
res2 = models.Books.objects.filter(price__lte=99)
print(res1.query, res2.query)
# 3.查询价格要么是9.9要么是999.66 要么是1999
res = models.Books.objects.filter(price__in=[9.9, 99.99, 1999])
print(res)
"""python对数字不是很敏感 精确度不高 很多时候我们会采取字符串存储数字类型"""
# 4.查询价格在200到1000之间的
res = models.Books.objects.filter(price__range=(200,1000))
print(res.query)
# 5.查询书名中包含字母s的书
res = models.Books.objects.filter(title__contains='s') # 区分大小写
print(res.query)
res = models.Books.objects.filter(title__icontains='s') # 区分大小写
print(res)
# 6.查询出版日期是2021的书
res = models.Books.objects.filter(publish_time__year=2021)
print(res.query)
# 7.查询出版日期是3月的书
res = models.Books.objects.filter(publish_time__month=3)
print(res)
3.总结
# 注意: 争对字段使用. 如: field__gt
__gt __lt __gte __glt
__in=[] __range=[start, stop]
__contains __icontains i全称忽略ignore
__startswith __istartswith
__endswith __iendswith
__year='2020' __year=2020
__month='1' __month=1
__day='20' __day=20
五、ORM常用字段介绍
常用字段类型表
常用字段 | 描述 | 与MySQL字段对应关系 |
---|---|---|
AutoField | 必须指定参数primary_key=True指定主键. 如果没有设置主键, 默认创建并以id名作为主键 | integer auto_increment |
IntegerField | 整型字段. 存储宽度4Bytes. 无符号: 0~2^32 有符号: -232/2~232-1 | int 或 integer |
BigIntegerField | 整型字段. 存储宽度8Bytes. 无符号: 0~2^64 有符号: -264/2~264-1 | bigint |
DeciamlField | 浮点字段. 必须指定参数max_digits设置总长度. decimal_places设置小数位长度 | numeric(%(max_digits)s, %(decimal_places)s) |
EmailField | 字符字段. Django Admin以及ModelForm中提供验证机制 | |
CharField | 字符字段. 必须指定参数max_length参数设置字符存储个数. Django中的CharField对应的MySQL数据库中的varchar类型,没有设置对应char类型的字段,但是Django允许我们自定义新的字段. | varchar(%(max_length)s) |
DateField | 日期字段. 格式: 年-月-日. 一般指定参数auto_now=Ture更新记录的时间, 或者auto_now_add=True插入记录的时间 | date |
DateTimeField | 日期字段. 格式: 年-月-日 时:分:秒 一般指定参数auto_now=Ture更新记录的时间, 或者auto_now_add=True插入记录的时间 | datetime |
六、常用于非常用字段类型合集(联想记忆:与MySQL字段对应关系)
1.自增长字段
- 注:当model如果没有自增列,则自动会创建一个列名为id的列(主键)
models.AutoField(primary_key=True) # 必须填入参数 primary_key=True
models.BigAutoField(primary_key=True) # 必须填入参数 primary_key=True
# 与MySQL字段对应关系
'AutoField': 'integer AUTO_INCREMENT',
'BigAutoField': 'bigint AUTO_INCREMENT',
2.整形字段
- 常用
IntegerField() # 整数列(有符号) -2147483648 ~ 2147483647
BigIntegerField() # 长整型(有符号) -9223372036854775808 ~ 9223372036854775807
# 对应mysql字段对应关系
IntegerField----->int
BigIntegerField----->bigint
- 不常用
PositiveSmallIntegerField() # 正小整数 0 ~ 32767
PositiveIntegerField() # 正整数 0 ~ 2147483647
SmallIntegerField() # 小整数 -32768 ~ 32767
# 对应mysql字段对应关系
PositiveSmallIntegerField----->samllint unsigned
PositiveIntegerField----->int unsigned
SmallIntegerField----->smallint
3.布尔类型
BooleanField() # 布尔值类型
NullBooleanField() # 可以为空的布尔值
# 对应mysql字段对应关系
BooleanField----->bool # mysql 中只提供了一种bool类型
4.字符串类型
- 常用
CharField() # 必须提供max_length参数, max_length表示字符长度
TextField() # 文本类型, 该字段可以用来存大段内容(文章、评论等), 没有字数限制
# 对应mysql字段对应关系
CharField----->varchar(length)
TextField----->longtext
- 邮箱类型
EmailField() # Django Admin以及ModelForm中提供验证机制
# mysql以varchar(254)形式存储
- IP地址
IPAddressField() # Django Admin以及ModelForm中提供验证 IPV4 机制
GenericIPAddressField() # 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"
# mysql中分别用char(15)与char(39)形式存储
- URL
URLField() # Django Admin以及ModelForm中提供验证 URL
- 文件 类型
FileField() # 传入一个文件对象,保存在指定路径下,然后将路径保存在数据库中
# 参数
upload_to = "" # 上传文件的保存路径
storage = None # 存储组件,默认django.core.files.storage.FileSystemStorage
FilePathField() # Django Admin以及ModelForm中提供读取文件夹下文件的功能
# 参数
path # 文件夹路径
match=None # 正则匹配
recursive=False # 递归下面的文件夹
allow_files=True # 允许文件
allow_folders=False # 允许文件夹
# mysql中都使用varchar(length)
- 图片类型
ImageField() # 路径保存在数据库,文件上传到指定目录
# 参数
upload_to = "" # 上传文件的保存路径
storage = None # 存储组件,默认django.core.files.storage.FileSystemStorage
width_field=None # 上传图片的高度保存的数据库字段名(字符串)
height_field=None # 上传图片的宽度保存的数据库字段名(字符串)
- 其他类型
SlugField() # Django Admin以及ModelForm中提供验证支持 字母、数字、下划线、连接符(减号)
CommaSeparatedIntegerField() # 格式必须为逗号分割的数字
UUIDField() # Django Admin以及ModelForm中提供对UUID格式的验证
# mysql中前两者都使用varchar(length), UUIDField对应char(32)
5.时间类型
DateField() # 日期格式, YYYY-MM-DD
TimeField() # 时间格式, HH:MM[:ss[.uuuuuu]]
DateTimeField() # 日期+时间格式, YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]
DurationField() # 长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型
# 对应mysql字段类型
DateField----->date
DateTimeField----->datetime
DurationField----->bigint
6.浮点型
FloatField() # 浮点数
DecimalField() # 10进制小数
#参数
max_digits # 小数总长度
decimal_places # 小数位长度
# 对应mysql字段类型
FloatField----->double precision
DecimalField----->numeric(max_digits,decimal_places)
7.二进制类型
BinaryField() # 二进制类型
# 对应mysql字段类型
BinaryField----->longlob
8.关系型字段
- ForeignKey : 一对多
# 一对多,外键字段推荐建在一对多中多的一方
publish = models.ForeignKey(to='Publish)
- ManyToManyField : 多对多
# 建议外键字段推荐建在查询频率较高的一方,且无需手动创建中间表,models会自动帮你创建一张虚拟表
authors = models.ManToManyField(to='authors')
- OneToOneField : 一对一
# 建议外键字段建立在查询频率较高的一方
author_detail = models.OneToOneField(to='AuthorDetail')
ps : 建立一对多、一对一关系的外键关联表, 关联表中默认会在建立的外键字段之后拼接**"_id"**, 也就是 我们无需自己手动写个后缀
七、字段参数
1.通用字段参数
- 所有字段类型都具备的参数
# 更改字段名
db_colum=''
# 设置主键
primary_key=True,默认为False
# 给字段设置别名(备注)
verbose_name=''
# 为字段设置默认值
default
# 字段的唯一键属性
unique=True,设置之后,这个字段的没一条记录的每个值是唯一的
# 允许字段为空
null=True(数据库中字段可以为空),blank=True(网页表单提交内容可以为空),切记不可以将null设置为Fasle的同时还把blank设置为True。会报错的。
# 给字段建立索引
db_index=True
# 在表单中显示说明
help_text=''
# 字段值不允许更改
editable=False,默认是True,可以更改。
2.个别字段才有的参数
CharField(max_length=100)
字段长度为utf8编码的100个字符串
DateField(unique_for_date=True)
这个字段的时间必须唯一
DecimalField(max_digits=4, decimal_places=2)
前者表示整数和小数总共多少数,后者表示小数点的位数
3.auto_now和auto_now_add(面试)
- 提示: 一般作为
DateField
和DateTimeField
参数
auto_now=True
对这条记录内容更新的时间
auto_now_add=True
插入这条记录的时间
4.关系型字段的参数
- ForeignKey : 一对多
# to
设置要关联的表
unique=True
ForeignKey(unique=True) === OneToOneField()
# 你在用前面字段创建一对一 orm会有一个提示信息 orm推荐你使用后者但是前者也能用
# to_field
置要关联的表的字段 默认不写关联的就是另外一张的主键字段.
# on_delete=models.CASECADE 和 on_update=models.CASECADE
设置级联更新级联删除. 同等与SQL语言中的ON DELETE CASCADE等约束 (提示: 该操作为Django1.X版本的默认操作, 2.X和3.X需要手动指定)
# db_index
如果db_index=True 则代表着为此字段设置索引
# db_constraint:
注意:db_constraint参数只适用于一对一, 或者一对多的关系. 至于多对多也是由双向的一对多关系组合而成, 是在一对多的关系上使用
是否在数据库中创建外键约束,默认为True,可以设置为False
好处是不会出现脏数据
坏处是插入的时候,效率低
企业通常不建立,由程序员来控制
只提供约束,基于对象和连表查询还是照样查
关联字段与外键约束没有必然的联系(建管理字段是为了进行查询,建约束是为了不出现脏数据)
其余字段的参数
models.DO_NOTHING
删除关联数据,引发错误IntegrityError
models.PROTECT
删除关联数据,引发错误ProtectedError
models.SET_NULL
删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空)
models.SET_DEFAULT
删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值)
models.SET
删除关联数据,
a. 与之关联的值设置为指定值,设置:models.SET(值)
b. 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象)
# on_dalete实际应用
on_delete参数:
1、表之间没有外键关联,但是有外键逻辑关联(有充当外键的字段)
2、断关联后不会影响数据库查询效率,但是会极大提高数据库增删改效率(不影响增删改查操作)
3、断关联一定要通过逻辑保证表之间数据的安全,不要出现脏数据,代码控制
4、断关联
5、级联关系
作者没了,详情也没:on_delete=models.CASCADE
出版社没了,书还是那个出版社出版:on_delete=models.DO_NOTHING
部门没了,员工没有部门(空不能):null=True, on_delete=models.SET_NULL
部门没了,员工进入默认部门(默认值):default=0, on_delete=models.SET_DEFAULT
- OneToOneField : 一对一
to
# 设置要关联的表
to_field
# 设置要关联的表的字段
on_delete
# 同ForeignKey字段
- ManyToManyField : 多对多
to
# 设置要关联的表
to_field
# 设置要关联的表的字段
related_query_name
# 反向查询操作时,使用的连接前缀,用于替换表名
symmetrical
# 仅用于多对多自关联时,指定内部是否创建反向操作的字段, 默认为True
through
# 手动创建第三张表,指定通过哪个表
through_fields
# 设置关联的字段
db_table
# 默认创建第三张表时,数据库中表的名称
5.自关联字段参数
需要在第一个参数中添加‘self’字符串,或写上它自己的表名(模型类名)
parent = ForeignKey(to='self')
6. related_name 和 related_query_name
related_name
# related_name 子查询反向操作时,使用的字段名,用于代替原反向查询时的'表名_set'。
# 例如:
class Classes(models.Model):
name = models.CharField(max_length=32)
class Student(models.Model):
name = models.CharField(max_length=32)
theclass = models.ForeignKey(to="Classes")
# 当我们要查询某个班级关联的所有学生(反向查询)时,我们会这么写:
models.Classes.objects.first().student_set.all()
# 当我们在ForeignKey字段中添加了参数 related_name 后
class Student(models.Model):
name = models.CharField(max_length=32)
theclass = models.ForeignKey(to="Classes", related_name="students")
# 当我们要查询某个班级关联的所有学生(反向查询)时,我们会这么写:
models.Classes.objects.first().students.all()
related_query_name
# related_query_name连表查询时, 反向查询操作时,使用的连接前缀,用于替换表
八、自定义字段及使用
1.自定义字段
-
在
Django
中CharField字段类型对应的mysql中默认就是varchar类型,如果想让Django支持Char类型,那么我们可以重写一个类来支持Char字段 -
在pycharm中先Ctrl+点击 CharField 字段查看其源码,按照它的模板改写
class MyCharField(models.Field):
# 1. 自定义独有参数
def __init__(self, max_length, *args, **kwargs):
self.max_length = max_length
super().__init__(max_length=max_length, *args, **kwargs) # max_length一定要是关键字的形式传入
# 2. 定义存储的类型及约束条件
def db_type(self, connection):
return 'Char(%s)' % self.max_length
2.自定义字段的使用
class Text(models.Model):
myfield = MyCharField(max_length=32, null=True)
九、手动创建第三张表
1.自动创建(常用)
- 优点 : 第三张表以及对应的外键关联字段不需要书写
- 缺点 : 可扩展性差, 无法对ORM自动生成的中间表进行增加字段的操作
- 注意 : 可以使用ORM提供给多对多关系表操作API以及正方向和双下划线查询
- 第三张表中没有其他字段
class Author(models.Model):
name = models.CharField(max_length=32,verbose_name='作者名')
# 通过ORM自带的ManyToManyField自动创建第三张表
class Book(models.Model):
title = models.CharField(max_length=32,verbose_name='书名')
authors = models.ManyToManyField(to='Author',related_name='authors')
2.全手动创建(基本不用)
- 优点:可扩展性强, 第三张表的内容完全取决于自己
- 缺点:ORM提供给多对多关系表之间的API以及正反向和双下划线查询都不支持, 并且代码也会多一些
class Author(models.Model):
name = CharField(max_legth=32,verbose_name='作者名')
class Book(models.Model):
title = CharField(max_length=32,verbose_name='书名')
class BookAuthor(models.Model):
book = models.ForeignKey(to='Book')
author = models.ForeignKey(to='Author')
3.半手动创建(比较常用)
- 优点:既可以对第三张表进行字段的添加, 也能使用ORM提供的正反向和双下划线查询
- 缺点 : 无法使用ORM提供的多对多表关系之间的API (add, set, remove, clear)
class Author(models.Model):
name = models.CharField(max_length=32,verbose_name='作者名')
class Book(models.Model):
title = models.CharField(max_length=32,verbose_name='书名')
authors = models.ManyToManyField(
to='Author', # 建立多对多关系的表,to_field参数可以不指定默认是主键
through='BookAuthor', # 第三张表名
through_fields=('book','author'))
# 第三张表中对应需要关联两张表的字段,以元组形式传
# 顺序:外键字段在哪张表,该表就排在前面
class BookAuthor(models.Model):
book = models.ForeignKey(to='Book')
author = models.ForeignKey(to='Author')
十、准备表和基础数据
1.准备表
1.表设计
from django.db import models
# Create your models here.
class Books(models.Model):
title = models.CharField(verbose_name='书名', max_length=32)
price = models.DecimalField(verbose_name='价格', max_digits=8, decimal_places=2)
publish_time = models.DateField(auto_now_add=True)
"""
提示: django自带的sqlite3数据库对日期格式不是很敏感 处理的时候容易出错
DateField
DateTimeField
两个重要参数
auto_now:每次修改数据的时候都会自动更新当前时间
auto_now_add:只在数据创建的时候记录一次创建时间 后续不会自动更改
"""
def __str__(self):
return self.title
class Book(models.Model):
title = models.CharField(verbose_name='书名', max_length=32)
price = models.DecimalField(verbose_name='价格', max_digits=8, decimal_places=2)
publish_time = models.DateField(verbose_name='出版日期', auto_now_add=True)
# 一对多 外键字段建在多的一方
publish = models.ForeignKey(to='Publish',on_delete=models.DO_NOTHING)
# 多对多 外键字段推荐建在查询频率较高的表中
authors = models.ManyToManyField(to='Author')
def __str__(self):
return self.title
class Publish(models.Model):
title = models.CharField(verbose_name='名称', max_length=32)
addr = models.CharField(verbose_name='地址', max_length=128)
email = models.EmailField(verbose_name='邮箱')
def __str__(self):
return self.title
class Author(models.Model):
name = models.CharField(verbose_name='姓名', max_length=32)
age = models.IntegerField(verbose_name='年龄')
# 一对一 外键字段推荐建在查询频率较高的表中
author_detail = models.OneToOneField(to='AuthorDetail',on_delete=models.DO_NOTHING)
def __str__(self):
return self.name
class AuthorDetail(models.Model):
phone = models.BigIntegerField(verbose_name='电话')
addr = models.CharField(verbose_name='地址', max_length=32)
def __str__(self):
return self.addr
'''
执行python3 manage.py makemigrations 报错TypeError: __init__() missing 1 required positional argument: 'on_delete'
缺少一个必需的位置参数:“on_delete”,解决方法:https://blog.csdn.net/m0_38109046/article/details/82660038
'''
2.准备数据
先往出版社、作者表、作者详情表准备一些数据。
因为一对一关系外键和一对多差不多,我们一对多来操作,把一对一的表先建立好,不做操作。
出版社表是一个被图书表关联的表我们先建立这个被关联表。
书籍表和关系表通过orm实现。
publish | |||
---|---|---|---|
id | title | addr | |
1 | 东方红出版社 | 中国 | 275242996@qq.com |
2 | 安慕希出版社 | 月球 | 202135897@qq.com |
3 | 太空出版社 | 太空 | 999888666@111.com |
author | |||
---|---|---|---|
id | name | age | author_detail_id |
1 | zhaozong | 8 | 3 |
2 | Shawn | 28 | 1 |
3 | kangkang | 3 | 2 |
authordetail | ||
---|---|---|
id | phone | addr |
2 | 112 | 天水 |
1 | 114 | 山东 |
3 | 119 | 重庆 |
十一、一对多表结构的增删改
1.两种设置外键字段的方式
- 实际字段指定
id publish_id=id
- 虚拟字段指定对象
publish=publish_obj
2.增create
#方式一:直接传指定字段
models.Book.objects.create(title='论语', price=666.98, publish_id=1)
models.Book.objects.create(title='孟子', price=444.44, publish_id=2)
models.Book.objects.create(title='老子', price=555.55, publish_id=3)
#方式二:传数据对象
publish_obj = models.Publish.objects.filter(pk=1).first()
models.Book.objects.create(title='三字经',price=666.66,publish=publish_obj)
3.删delete
models.Book.objects.filter(pk=1).delete() # 级联删除
4.改update
#方式一:实际字段
models.Book.objects.filter(pk=2).update(publish_id=1)
#方式二:虚拟字段
publish_obj = models.Publish.objects.filter(pk=2).first()
models.Book.objects.filter(pk=2).update(publish=publish_obj)
十二、多对多表结构的增删改(API)本质就是操作虚拟中间表(第三张表)
1.增add
- add:括号内既可以传数字也可以传对象 并且都支持多个
# 给书籍增加作者
book_obj = models.Book.objects.filter(pk=3).first()
print(book_obj.authors) # app01.Author.None(就类似于你已经到了第三张关系表了)
book_obj.authors.add(1) # 添加一个book_id是3对应的author_id是1的记录
book_obj.authors.add(2, 3)
book_obj = models.Book.objects.filter(pk=3).first() # id为3的书籍
authors1 = models.Author.objects.filter(pk=1).first() # id为1的作者
authors2 = models.Author.objects.filter(pk=2).first() # id为2的作者
authors3 = models.Author.objects.filter(pk=3).first() # id为3的作者
book_obj.authors.add(authors1) # id为3的书籍添加一个id为1的作者
book_obj.authors.add(authors2, authors3) # id为3的书籍添加id为2和3的作者
2.删remove
- 括号内既可以传数字也可以传对象并且都支持多个
book_obj = models.Book.objects.filter(pk=3).first()
book_obj.authors.remove(2) # 删除中间表中book_id是3的对应的author_id是2的记录
book_obj.authors.remove(1, 3)
author_obj = models.Author.objects.filter(pk=2).first() # id为2的作者
author_obj1 = models.Author.objects.filter(pk=3).first() # id为3的作者
book_obj.authors.remove(author_obj, author_obj1) # 从id为3的书籍中删除这两个作者
3.改set
- 括号内必须传一个可迭代对象,该对象内既可以是数字也可以是对象 并且都支持多个
- set操作是一种新增操作. 执行set一上来先将括号内没有指定的全部清除, 如果有则保留, 没有则新增. 如果想修改某一个数据, 必须对源数据进行指定, 不然源数据将会被清除.
book_obj = models.Book.objects.filter(pk=3).first()
book_obj.authors.set([1, 2]) # 先删除author_id不是等于1,2的, 如果没有1, 2则添加
book_obj = models.Book.objects.filter(pk=3).first()
authors1 = models.Author.objects.filter(pk=1).first()
authors2 = models.Author.objects.filter(pk=2).first()
book_obj.authors.set([authors1, authors2])
4.清空clear
- 括号内不要加任何参数
book_obj = models.Book.objects.filter(pk=3).first()
book_obj.authors.clear() # 清空book_id为3所对应的所有author_id
十三、查询储备知识
1.正向查询
- 正向查询按照字段
- 正向:外键字段在的一方查不在的一方,外键字段在谁那儿,谁查另外的人就是正向
- 如果返回结果多个还需要
.all()
2.反向查询
- 反向查询按照表名小写
- 反向:没有外键字段,就是外键字段不在的一方查在的一方
- 如果返回结果多个还需要连接
_set.all()
# 提示:
书籍与作者, 外键字段在书籍.
作者与作者详情, 外键字段在作者.
书籍与出版社外键字段在书籍.
总结:正向反向查询就是判断你是否有关联的外键字段
十四、子查询:基于对象的跨表查询
1.正向查询
- 表内有关联字段, 可以直接使用
[对象].[字段名]
的方式查询到结果 - 当结果有多个情况下,需要加上
.all()
, 如果只是一个则直接拿到对象
示例:
'''
正向查询按照字段
当你的结果可能有多个的情况下就需要加.all()
book_obj.authors.all()
如果是一个则直接拿到数据对象
book_obj.publish
author_obj.author_detail
'''
# 1. 查询书籍主键为1的出版社
book_obj = models.Book.objects.filter(pk=1).first() # 先拿到对象
print(book_obj.publish.title) # 出版社字段在book中,直接点取值
# 2. 查询书籍主键为2的作者: 一本书可能被多个作者出版使用.all()
book_obj = models.Book.objects.filter(pk=2).first() # 先拿到书籍对象
print(book_obj.authors) # app01.Author.None (作者外键在book表中,直接点取值)
print(book_obj.authors.all()) # <QuerySet [<Author: kangkang>, <Author: zhaozong>]>
# 3. 查询作者kangkang的电话号码:
author_obj = models.Author.objects.filter(name='kangkang').first() # 先拿到作者对象
print(author_obj.author_detail) # AuthorDetail object(详情字段在作者表中,直接点)
print(author_obj.author_detail.phone) # 112
2.反向查询
- 表内没有关联的字段, 查询的结果有多个就必须加
_set.all()
- 如果查询结果就一个则不需要加
_set.all()
'''
反向查询按表名小写
如果查询结果可以有多个就必须需要加_set.all()
publish_obj.book_set.all()
author_obj.book_set.all()
如果查询结果只有一个的时候就不需要加_set.all()
author_detail_obj.author
'''
# 4. 查询出版社是东方出版社出版的书: 一个出版社可以出版多本书使用.all()
publish_obj = models.Publish.objects.filter(title='东方出版社').first()
print(publish_obj.title) # app01.Book.None
print(publish_obj.title.all()) # <QuerySet [<Book: 论语>, <Book: 三字经>]>
# 5. 查询作者是jason写过的书: 一个作者jason可以写过多本书
author_obj = models.Author.objects.filter(title='jason').first()
print(author_obj.book_set) # app01.Book.None
print(author_obj.book_set.all()) # <QuerySet [<Book: 夕阳西下>]>
# 6. 查询手机号是114的作者姓名
author_detail_obj = models.AuthorDetail.objects.filter(phone=114).first()
print(author_detail_obj.author.name) # jason
3.总结
'''子查询: 基于对象的跨表查询'''
# 正向: 查询书籍主键为1的出版社
models.Book.objects.filter(pk=1).first().publish.name
# 正向: 查询书籍主键为2的作者
models.Book.objects.filter(pk=2).first().authors.all()
# 正向: 查询作者jason的电话号码
models.Author.objects.filter(name='jason').first().author_detail.phone
# 反向: 查询出版社是东方出版社出版的书
models.Publish.objects.filter(name='东方出版社').first().publish_set.all()
# 反向: 查询作者是jason写过的书
models.Author.objects.filter(name='jason').first().book_set.all()
# 反向: 查询手机号是110的作者姓名
models.AuthorDetail.objects.filter(phone=110).first().author.name
十五、基于双下划线的跨表查询 : 连表查询
正向查询使用字段名, 反向查询表名小写
1.示例:查找书名是“红楼梦”的书的作者的手机号码
# 正向查询
res = models.Book.objects.filter(title='红楼梦').values('authors__author_detail__phone')
print(res)
# 反向查询
res = models.Author.objects.filter(book__title='红楼梦').values('author_detail__phone')
print(res)
res = models.AuthorDetail.objects.filter(author__book__title='红楼梦').values('phone')
print(res)
2.示例 : 查询城市是北京的出版社出版过的所有书籍
# 正向查询
res = models.Publish.objects.filter(addr='北京').values('book__title')
print(res) # <QuerySet [{'book__title': '红楼梦'}, {'book__title': '包子秘籍'}]>
# 反向查询
res = models.Book.objects.filter(publish__addr='北京').values('title')
print(res) # <QuerySet [{'title': '红楼梦'}, {'title': '包子秘籍'}]>
3.示例:查询手机号以133开头的作者出版过的书籍名称以及书籍出版社名称
# 正向查询
res = models.AuthorDetail.objects.filter(phone__startswith=133).values('author__book__title','author__book__publish__name')
print(res)
# 反向查询
res = models.Author.objects.filter(author_detail__phone__startswith=133).values('book__title','book__publish__name')
print(res)
res = models.Book.objects.filter(authors__author_detail__phone__startswith=133).values('title','publish__name')
print(res)
res = models.Publish.objects.filter(book__authors__author_detail__phone__startswith=133).values('book__title','name')
print(res)
4.总结
'''连表查询: 基于双下划线的连表查询'''
# 查询jason的手机号和作者姓名
# 正向
models.Author.objects.filter(name='jason').values('author_detail__phone', 'name')
# 反向
models.AuthorDetail.objects.filter(author__name='jason').values('phone', 'author__name')
# 查询书籍主键为1的出版社名称和书的名称
# 正向
models.Book.objects.filter(pk=1).values('publish__name', 'title')
# 反向
models.Publish.objects.filter(book__pk=1).values('name' 'book__title')
# 查询书籍主键为1的作者姓名
# 正向
models.Book.objects.filter(pk=1).values('authors__name')
# 反向
models.Author.objects.filter(book_pk=1).values('name')
# 查询书籍主键是1的作者的手机号
# 正向
models.Book.objects.filter(pk=1).values('authors__author_detail__phone')
# 反向
models.AuthorDetail.objects.filter(author__book__pk=1).values('phone')
十六、聚合查询 aggregate( *args,**kwargs)
1.基本介绍
介绍:聚合查询通常情况下都是配合分组一起使用的. 如果你只想使用聚合函数, 但是不想分组, 那么就应该使用aggregate.
使用:直接在objects后面链接.
返回:返回字典格式的数据. 如果是对price字段求平均值, 那么返回格式是: {'price__avg': 值}
和数据库相关模块的方法:
基本是都在django.db.models里面
如果上述没有那么应该在django.db里面
2.五种聚合函数
- Avg (Average) : 平均值
- Max (Maximum) : 最大值
- Min (Minimum) : 最小值
- Sum (Summary) : 求和
- Count : 个数
3.aggregate()
aggregate()
是QuerySet
的一个终止子句,意思是说,它返回一个包含一些键值对的字典- 键的名称是聚合值的标识符,值是计算出来的聚合值
- 键的名称是按照字段和聚合函数的名称自动生成出来的
- 如果你想要为聚合值指定一个名称,可以向聚合子句提供它
from django.db.models import Avg,Max,Min,Sum,Count # 导入聚合函数
res = models.Book.objects.aggregate(Avg('price'))
print(res) # {'price__avg': Decimal('34.204000')}
res = models.Book.objects.aggregate(avg_price=Avg('price')) # 指定名称
print(res) # {'avg_price': Decimal('34.204000')}
4.示例
- 求所有数据价钱最高的, 最低的, 总和, 平均价钱
from django.db.models import Max,Min,Sum,Avg
res = models.Book.objects.aggregate(Max('price'),Min('price'),Sum('price'),Avg('price'))
print(res) # {'price__max': Decimal('88.88'), 'price__min': Decimal('6.66'), 'price__sum': Decimal('171.02'), 'price__avg': Decimal('34.204000')}
十七、分组查询 annotate
1.介绍
分组注意事项
-
分组只能拿到分组得依据. 按照什么分组就只能拿到什么组. 其他字段不能直接获取, 需要借助一些方法(聚合函数)
-
提示: 可以指定多个分组, 指定多个分组, 当然就可以获取多个分组之间的分组依据.
MySQl中设置全局生效得分组严格模式:
set global sql_mode='only_full_group_by';
使用步骤:
第一步: 指定分组的依据
第一种情况: 默认分组. annotate直接在objects后面链接时, models后面点什么就按照什么分组.
例子: 按照书分组
models.Book.objects.annotate(sum_price=Sum)
第二种情况: 指定分组. annotate跟在values后面, values中指定什么字段就按照什么分组
例子: 按照书中的价格分组.
models.Book.objects.values('price').annotate()
第二步: 为分组的字段取别名
第三步: 在annotate后面使用values可以获取分组的依据 和 分组举和的结果
返回:返回QuerySet对象
- 提示: 只要你的orm语句得出的结果还是一个queryset对象, 那么它就可以继续无限制的点queryset对象封装的方法.
特殊情况使用
- 使用Count进行获取分组举和的结果应该是要对主键 或者 唯一字段进行的统计. 所以使用Count进行统计时比如: authors__id 就可以写成 authors
2.分组依据
- values( ) 在 annotate( ) 之前则表示 group by 字段
# 默认分组依据
如果 annotate() 直接跟在 objects 后面,则表示直接以当前的基表为分组依据
例 : 按书来分组 : odels.Book.objects.annotate(sum_price=Sum)
# 指定分组依据
如果 annotate() 跟在 values() 后面,则表示按照values中指定的字段来进行分组
例 : 按照书中的price进行分组 : models.Book.objects.values('price').annotate()
- values( ) 在 annotate( ) 之后则表示取字段
- filter( ) 在 annotate( ) 之前则表示 where 条件
- filter( ) 在 annotate( ) 之后则表示 having 条件
3.示例
# from django.db.models import Max,Min,Sum,Avg
# 查询出版社id大于1的出版社id,以及出书平均价格
res = models.Book.objects.values('publish_id').filter(publish_id__gt=1).annotate(avg_price=Avg('price')).values('publish_id','avg_price')
print(res) # <QuerySet [{'publish_id': 2, 'avg_price': Decimal('31.635000')}, {'publish_id': 3, 'avg_price': Decimal('9.435000')}]>
# 查询出版社id大于1的出版社id,以及出书平均价格大于25的
res = models.Book.objects.values('publish_id').filter(publish_id__gt=1).annotate(avg_price=Avg('price')).filter(avg_price__gt=25).values('publish_id','avg_price')
print(res) # <QuerySet [{'publish_id': 2, 'avg_price': Decimal('31.635000')}]>
# 查询每一个出版社出版的名称和书籍个数(连表)(连表最好以group by的表作为基表)
res = models.Publish.objects.annotate(count_book=Count('book')).values('name','count_book')
print(res) # <QuerySet [{'name': '沙河尚出版社', 'count_book': 1}, {'name': '北京出版社', 'count_book': 2}, {'name': '狗不理出版社', 'count_book': 2}]>
# 上面以Book作为基表
res = models.Book.objects.values('publish_id').annotate(book_count=Count('nid')).values('publish__name','book_count')
print(res) # <QuerySet [{'publish__name': '沙河尚出版社', 'book_count': 1}, {'publish__name': '北京出版社', 'book_count': 2}, {'publish__name': '狗不理出版社', 'book_count': 2}]>
# 查询每个作者的名字,以及出版过书籍的最高价格(建议使用分组的表作为基表)
# 多对多如果不以分组的表作为基表, 可能会出现数据问题
res = models.Author.objects.annotate(max_price=Max('book__price')).values('name','max_price')
print(res) # <QuerySet [{'name': 'shawn', 'max_price': Decimal('88.88')}, {'name': 'chris', 'max_price': Decimal('88.88')}, {'name': '小rub', 'max_price': Decimal('55.50')}, {'name': 'summer', 'max_price': Decimal('12.21')}]>
res = models.Book.objects.values('authors__nid').annotate(max_price=Max('price')).values('authors__name','max_price')
print(res) # <QuerySet [{'authors__name': 'shawn', 'max_price': Decimal('88.88')}, {'authors__name': 'chris', 'max_price': Decimal('88.88')}, {'authors__name': '小rub', 'max_price': Decimal('55.50')}, {'authors__name': 'summer', 'max_price': Decimal('12.21')}]>
# 查询每一个书籍的名称, 以及对应的作者个数
res = models.Book.objects.annotate(auth_count=Count('authors__nid')).values('title','auth_count')
print(res) # <QuerySet [{'title': '北京爱情故事', 'auth_count': 2}, {'title': '水浒传', 'auth_count': 2}, {'title': '三国演义', 'auth_count': 1}, {'title': '红楼梦', 'auth_count': 3}, {'title': '包子秘籍', 'auth_count': 2}]>
# 统计价格25元, 作者个数大于1的图书
res = models.Book.objects.filter(price__gt=25).annotate(auth_count=Count('authors__nid')).values('title','auth_count','price')
print(res) # <QuerySet [{'title': '北京爱情故事', 'price': Decimal('88.88'), 'auth_count': 2}, {'title': '红楼梦', 'price': Decimal('55.50'), 'auth_count': 3}]>
十八、F 查询
1.F查询介绍
- 我们之前的例子中对一些字段的过滤和操作都是与一个常量进行比较大小, 如果我们想让字段与字段的值进行比较就无法简单实现, 于是就可以使用 F 查询了
- 作用 : 取出某个字段对应的值
2.使用示例
"""
# 作用: 能够帮助你直接获取到表中某个字段对应的数据
# 使用:
from django.db.models import F
# 获取到某个字段对应的数据
F("字段__条件")
# 查询字段对应的数据是数字类型可以直接加减运算:
F('字段__条件') + 500
# 字符类型需要借助Concat和Value方法
from django.db.models.functions import Concat
from django.db.models import Value
Concat(F('字段__条件'), Value("str"))
注意: 查询字段对应的数据是字符类型不能直接进行拼接. 否则操作的字段对应的所有数据将变成空白.
"""
from django.db.models import F
# 1. 查询卖出数大于库存数的书籍
res = models.Book.objects.filter(sale__gt=F('stock'))
print(res) # <QuerySet [<Book: 论语>, <Book: 老子>]>
# 2. 将所有书籍的价格提升500块
res = models.Book.objects.update(price=F('price') + 500)
print(res) # .update方法返回受影响的行数 7
# 3. 将所有书的名称后面加上爆款两个字
from django.db.models.functions import Concat
from django.db.models import Value
models.Book.objects.update(title=Concat(F('title'), Value('爆款')))
# models.Book.objects.update(title=F('title') + '爆款') # 这样指定的字段title所有对应的数据全部变成空白.
十九、Q查询
1.Q查询介绍
- filter 的字段筛选条件如果指定多个, 默认是and连接多个条件, 如果想要使用or或者not,则需要Q查询
- 作用 : 构造
或 or
、与 &
、非 ~
条件
2.示例
"""
# 作用: filter的字段筛选条件指定多个, 默认是and连接. 要实现or或者not需要借助Q查询
# 使用:
from django.db.models import Q
Q(字段__条件=值)
# 连接条件and的3种情况
1. filter中指定多个参数逗号隔开: filter(参数1, 参数2)
2. 查询指定多个逗号隔开: filter(Q(), Q())
3. 使用&连接符: filter(Q() & Q())
# 连接条件or
filter(Q() | Q())
# 连接条件not
filter(~Q() | Q())
# Q查询的高阶用法: 让左边指定的变量形式的查询条件可以是字符串
q = Q()
q.connecter = 'or' # 指定连接符. 不指定默认and
q.children.append(Q('字段__条件', 值))
res = models.XXX.objects.filter(q)
"""
from django.db.models import Q
# 1. 查询卖出数大于100 和 价格小于900的书籍 --> 连接条件 and
# res = models.Book.objects.filter(sale__gt=100, price__lt=900)
# res = models.Book.objects.filter(Q(sale__gt=100), Q(price__lt=900))
res = models.Book.objects.filter(Q(sale__gt=100) & Q(price__lt=900))
print(res) # <QuerySet [<Book: 论语爆款>]>
# 2. 查询卖出数大于100或者价格小于600的书籍 --> 连接条件 or
res = models.Book.objects.filter(Q(sale__gt=100) | Q(price__lt=600))
print(res) # <QuerySet [<Book: 红龙盟爆款>, <Book: 老子爆款>, <Book: 论语爆款>, <Book: 孟子爆款>, <Book: 老子爆款>, <Book: 三字经爆款>]>
# 3. 查询卖出数不大于100或者价格小于600的书籍 --> 连接条件 not
res = models.Book.objects.filter(~Q(sale__gt=100) | Q(price__lt=600))
print(res) # <QuerySet [<Book: 夕阳西下爆款>]>
# 4. Q的高阶用法: 能够将查询条件的左边变量的形式变成字符串的形式
q = Q() # 第一步: 实例化一个q对象
q.connector = 'or' # 第二步: 定义连接条件
q.children.append(('sale__gt', 100)) # 第三步: 指定字符串形式的查询字段条件, 以及范围100
q.children.append(('price__lt', 600))
res = models.Book.objects.filter(q) # 第四步: 将q对象传入filter
print(res)
二十.Meta 元信息
1.Meta 类
- ORM对应的每一个模型类中都可以包含一个Meta类, 而Meta类中封装了一些数据库的信息
2.主要字段
db_table
: ORM在数据库中的表名默认是[app_类名]
,可以通过 db_table 可以重写表名index_together
: 多个字段联合索引unique_together
: 联合唯一索引ordering
: 指定默认按什么字段排序, 只有设置了该属性, 我们查询到的结果才可以被reverse( )
3.示例
class User(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32,verbose_name='用户名')
pwd = models.CharField(max_length=64,verbose_name='密码')
class Meta:
db_table='S_User' #表名
index_together=('name','pwd') # 多个字段联合索引
unique_together=('name','pwd') # 联合唯一
ordering=('nid', ) # 默认以哪个字段排序
二十一.原生 SQL
在查询表内容时, 如果遇到使用ORM无法满足需求的情况下, 我们就需要使用原生sql语句进行查询, 而ORM给我们提供了这个功能 .raw( )
- 执行原生SQL, 与使用的模型类就无关了, SQL里面写的查什么得到的就是什么, 看下面使用两个不同模型类测试
# 使用原生SQL进行查询
from app01 import models
res = models.Book.objects.raw('select * from app01_author where age>23')
for any_obj in res:
print(f'使用的对象:{any_obj} 查询的结果:(名字:{any_obj.name} 年龄:{any_obj.age})')
'''
使用的对象:水浒传 查询的结果:(名字:chris 年龄:43)
使用的对象:三国演义 查询的结果:(名字:小rub 年龄:45)
'''
res = models.Author.objects.raw('select * from app01_author where age>23')
for any_obj in res:
print(f'使用的对象:{any_obj} 查询的结果:(名字:{any_obj.name} 年龄:{any_obj.age})')
'''
使用的对象:chris 查询的结果:(名字:chris 年龄:43)
使用的对象:小rub 查询的结果:(名字:小rub 年龄:45)
'''
ps : 使用原生 sql 语句 select 后面必须存在主键或者直接写
*
, 否则报错
res = models.Author.objects.raw('select name,age from app01_author where age>23')
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ht8fkCB5-1617120950852)(https://i.loli.net/2021/03/31/fwVLsZbJAr19oMh.png)]
二十二.重点大补充
1.普通函数以 __
开头
- 这个是前面的知识点, 表示只在当前模块(py文件)下使用, 尽量不在外部调用
2.mysql 字符集
- utf8 字符集 : 两个字节表示一个字符 (生僻字,颜文字存不了)
- utf8mb4 字符集 : 等同于真正意义上的
utf-8
- utf-8 字符编码 : 1~4 个字节表示一个字符
3.django admin 的使用
- 作用 : 后台管理, 方便我们快速录入数据
- 使用 :
# 第一步 : 在 admin.py 中把要使用的表注册
from app01 import models
admin.site.register(models.Book)
admin.site.register(models.Author)
admin.site.register(models.AuthorDetail)
admin.site.register(models.Publish)
# 第二步 : 创建一个超级管理员
python3 manage.py createsuperuser
输入用户名及密码、确认密码
# 第三步 : 登入, 进行操作
127.0.0.1:8000/admin/
上面页面显示全是英文, 可以通过更改
settings.py
配置文件进行更改语言设置
# setting.py 配置文件进行修改参数
# LANGUAGE_CODE = 'en-us' # 英语
LANGUAGE_CODE = 'zh-hans' # 汉语
# TIME_ZONE = 'UTC' # 格林威治时间
TIME_ZONE = 'Asia/Shanghai' # 亚洲上海时区
USE_I18N = True
USE_L10N = True
# USE_TZ = True
USE_TZ = False # 不使用UTC时区
刷新后展示界面 :
5.WSGI协议简介
- Web服务器网关接口(Python Web Server Gateway Interface,缩写为WSGI)
是为Python语言定义的Web服务器和Web应用程序或框架之间的一种简单而通用的接口, 自从WSGI被开发出来以后,许多其它语言中也出现了类似接口
WSGI协议规定了如何拆http请求 :
- 将HTTP请求信息拆到一个python字典中—>
environ
- 响应对象----->
start_response
wsgiref : 它是符合WSGI协议的web服务器
- python中有 : wsgiref (测试环境), uWSGI (线上环境,并发强), gunicorn
- Java中有 : tomcat, jboss
- php : php服务器
6.Django模板语法
- dtl (django template language) : django 模板语言, 在模板中写Python代码
- php : 例 : http://www.aa7a.cn/user.php 一般以 .php 为后缀
- Java : 例 : https://www.pearvideo.com/category_loading.jsp 一般以 .jsp 为后缀
- go : 例 : …/xx.html 一般以 .html 为后缀