查询
正反向查询进阶操作
'反向查找小写表名在双下划线在写字段名'
1.查询主键为1的书籍对应的出版社名称及书名
res = models.Publish.objects.filter(book__pk=1).values('name', 'book__title')
print(res)
# <QuerySet [{'name': '北方出版社', 'book__title': 'django基础'}]>
2.查询主键为3的书籍对应的作者姓名及书名
res = models.Author.objects.filter(book__pk=3).values('name', 'book__title')
print(res)
# <QuerySet [{'name': 'tom', 'book__title': 'python基础'}]>
3.查询barry的作者的电话号码和地址
res = models.AuthorDetail.objects.filter(author__name='barry').values('phone', 'addr')
print(res)
# <QuerySet [{'phone': 110, 'addr': '上海'}]>
4.查询南方出版社出版的书籍名称和价格
res = models.Book.objects.filter(publish__name='南方出版社').values('title', 'price')
print(res)
# <QuerySet [{'title': 'Golang高并发', 'price': Decimal('19800.98')}, {'title': 'python数据分析', 'price': Decimal('29800.98')}]>
5.查询barry写过的书的名称和日期
res = models.Book.objects.filter(authors__name='barry').values('title', 'publish_time')
print(res)
# <QuerySet [{'title': 'django基础', 'publish_time': datetime.datetime(2022, 9, 5, 14, 18, 52, 900173, tzinfo=<UTC>)}, {'title': 'Golang高并发', 'publish_time': datetime.datetime(2022, 9, 5, 14, 19, 50, 130331, tzinfo=<UTC>)}]>
6.查询电话是110的作者姓名和年龄
res = models.Author.objects.filter(author_detail__phone=110).values('name', 'age')
print(res)
# <QuerySet [{'name': 'barry', 'age': '18'}]>
7.查询主键为1的书籍对应的作者电话号码
res = models.AuthorDetail.objects.filter(author__book__pk=1).values('phone')
print(res)
# <QuerySet [{'phone': 110}]>
'可以直接在作者信息中获取,也可以从作者中获取'
res = models.Author.objects.filter(book__pk=1).values('author_detail__phone')
print(res)
# <QuerySet [{'author_detail__phone': 110}]>
聚合查询
1.max>>>取最大值
2.min>>>取最小值
3.sum>>>求和
4.avg>>>平均值
5.count>>>计数
from django.db.models import Max, Min, Sum, Avg, Count
'''
聚合函数需先导入才能使用
若还没有分组时使用聚合函数,需要使用aggregate关键字
可以用"名字="来起别名
'''
res = models.Book.objects.aggregate(Max('price'), Min('price'), Sum('price'), Avg('price'), Count('pk'))
print(res)
# {'price__max': Decimal('29800.98'), 'price__min': Decimal('19000.98'), 'price__sum': Decimal('98403.92'), 'price__avg': Decimal('24600.980000'), 'pk__count': 4}
分组查询
1.统计每一本书的作者个数
res = models.Book.objects.annotate(author_num=Count('authors__pk')).values('title', 'author_num')
print(res)
# <QuerySet [{'title': 'django基础', 'author_num': 1}, {'title': 'Golang高并发', 'author_num': 1}, {'title': 'python基础', 'author_num': 1}, {'title': 'python数据分析', 'author_num': 1}]>
'''
1.按照整条数据分组,按照一条条书籍记录分组
2.按照表中某个字段分组,按照annotate之前的values括号中指定的字段分组
'''
res = models.Book.objects.values('title').annotate(book_num=Count('authors__pk')).values('title', 'book_num')
print(res)
# <QuerySet [{'title': 'django基础', 'book_num': 1}, {'title': 'Golang高并发', 'book_num': 1}, {'title': 'python基础', 'book_num': 1}, {'title': 'python数据分析', 'book_num': 1}]>
2.统计出每个出版社卖的最便宜的书的价格
res = models.Publish.objects.annotate(min_price=Min('book__price')).values('name', 'min_price')
print(res)
# <QuerySet [{'name': '北方出版社', 'min_price': Decimal('19000.98')}, {'name': '南方出版社', 'min_price': Decimal('19800.98')}]>
3.统计不止一个作者的图书
'filter在annotate前面则类似于where,在后面则类似于having'
res = models.Book.objects.annotate(author_num=Count('authors__pk')).filter(author_num__gt=1).values('title', 'author_num')
print(res)
# <QuerySet [{'title': 'django基础', 'author_num': 2}]>
4.查询各个作者出的书的总价格
res = models.Author.objects.annotate(author_num=Sum('book__price')).values('name', 'author_num')
print(res)
# <QuerySet [{'name': 'barry', 'author_num': Decimal('38801.96')}, {'name': 'tom', 'author_num': Decimal('78602.94')}]>
F查询
'''
storage_num = models.IntegerField(verbose_name='库存数', null=True)
sale_num = models.IntegerField(verbose_name='销售额', default=100)
当表中已有数据的情况下,添加新的字段需要指定一些参数
1.设置字段值允许为空>>>null=True
2.设置字段默认值>>>default=100
3.在终端中直接给出默认值>>>1 provide a default value
'''
from django.db.models import F
'F查询:查询条件不是自定义的而是来自于表中其他字段'
1.查询库存数大于卖出数的书籍
res = models.Book.objects.filter(storage_num__gt=F('sale_num'))
print(res)
# <QuerySet [<Book: Book object (1)>, <Book: Book object (4)>]>
2.将所有书籍的价格上涨1000块
models.Book.objects.update(price=F('price') + 1000)
3.将所有书籍名称加上爆款后缀
from django.db.models.functions import Concat
from django.db.models import Value
models.Book.objects.filter(pk=4).update(title=Concat(F('title'), Value('爆款')))
Q查询
from django.db.models import Q
'Q:可以改变filter括号内多个条件关系的逻辑运算符'
res = models.Book.objects.filter(Q(pk=1), Q(publish_id=2)) # 逗号是and关系
print(res)
# <QuerySet []>
res = models.Book.objects.filter(Q(pk=1) | Q(publish_id=2)) # 管道符是or关系
print(res)
# <QuerySet [<Book: Book object (1)>, <Book: Book object (2)>, <Book: Book object (4)>]>
res = models.Book.objects.filter(~Q(pk=1) | Q(publish_id=2)) # 加波浪号是not关系
print(res)
# <QuerySet [<Book: Book object (2)>, <Book: Book object (3)>, <Book: Book object (4)>]>
'Q查询:还可以将查询条件的字段改为字符串形式'
q_obj = Q() # 产生一个q对象
q_obj.connector = 'or' # q对象默认的多个条件关系,修改为or
q_obj.children.append(('pk', 1)) # 在q对象内添加条件
q_obj.children.append(('publish_id', 2))
res = models.Book.objects.filter(q_obj) # 查询匹配q对象内条件的数据
print(res)
# <QuerySet [<Book: Book object (1)>, <Book: Book object (2)>, <Book: Book object (4)>]>
ORM查询优化
1.django orm默认都是惰性查询
当orm的语句都在后续代码中真正需要使用的时候才会执行
2.django orm自带limit分页
3.only()
将括号内的字段封装成数据对象,对象可以通过点字段的方法获取数据
也可以点击括号内没有的字段,但是每次使用括号没有的字段就会在从数据库中查询一次
res = models.Book.objects.only('title', 'price')
for obj in res:
print(obj.title)
print(obj.price)
print(obj.publish_time)
4.defer()
将括号内的字段封装成数据对象,对象可以通过点字段的方法获取数据
也可以点击括号内没有的字段,而且使用括号没有的字段不需要从数据库中查询一次
res = models.Book.objects.defer('title', 'price')
for obj in res:
print(obj.publish_time)
5.all()
只能获取书对象,每获取一次都要从数据库中查询一次
res = models.Book.objects.all()
for obj in res:
print(obj.publish.name)
6.select_related()
括号内只能放一对一、一对多的外键字段,会自动将表拼接到一起,这样获取数据后在点表中数据的时候都不会走数据库查询
res = models.Book.objects.select_related('publish')
for obj in res:
print(obj.publish.name)
7.prefetch_related()
底层其实是子查询,将查询之后的结果也一次性封装到对象中,用户使用时是感觉不出来的
res = models.Book.objects.prefetch_related('publish')
for obj in res:
print(obj.publish.name)
ORM事务操作
'在orm中使用事务,需要导入transaction'
from django.db import transaction
try:
with transaction.atomic():
pass # 多条ORM语句
except Exception as e:
print(e)
字段
模型层常见字段类型
| 字段 |
作用 |
| AutoField() |
int自增列 |
| CharField() |
对应MySQL数据库中的varchar |
| IntegerField() |
存十位一般不用它来存手机号,位数也不够 |
| BigIntegerField() |
长整型 |
| DateField() |
日期字段 |
| DateTimeField() |
日期时间字段 |
| EmailField() |
字符串类型 |
| BooleanField() |
传布尔值存数字0(Ture)或1(False) |
| TextField() |
文本类型,存储大段文本 |
| FileField() |
存储文件数据,自动找指定位置存储,字段存具体路径 |
| ForeignKey() |
用来表示外键关键关系 |
| OneToOneField() |
一对一字段 |
ORM还支持自定义字段
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
info = MyCharField(max_length=32)
ORM常见字段参数
| 关系字段 |
功能 |
| to |
设置关联的表 |
| to_field |
设置要关联的字段 |
| related_name |
指定外键属性 |
| on_delete |
当删除关联表中数据 |
max_length
verboses_name
auto_now
auto_now_add
null
default
max_digits
decimal_places
unique=True # 该字段在此表中必须是唯一的
db_index=True # 代表此字段设置索引
choices
1.当字段数据的可能性是可以完全列举出来的时候,应该考虑使用参数
1.1创建表
class UserInfo(models.Model):
username = models.CharField(max_length=32)
gender_choice = (
(1, '男性'),
(2, '女性'),
(3, '其他'),
)
gender = models.IntegerField(choices=gender_choice)
1.2获取数据
user_obj = models.UserInfo.objects.filter(pk=1).first()
print(user_obj.gender) # 数据库中的真实数据
print(user_obj.get_gender_display()) # 查找提前写好的对应关系,如果没有则按照真实数据展示
2.models.CASCADE
级联操作,当主表中连接的一条数据删除时,从表中所有与之关联的数据同时删除
3.models.SET_NULL
当主表中连接的一行数据删除时,从表中所有与之关联的数据的相关字段设置为null
'定义外键时,这个字段必须可以允许为空'
4.models.PROTECT
当主表中的一行数据删除时,由于从表中相关字段是受保护的外键,所以都不允许删除
5.models.SET_DEFAULT
当主表中的一行数据删除时,从表中所有相关的数据的关联字段设置为默认值
'定义外键时,这个外键字段应该有一个默认值'
6.models.SET()
当主表中的一条数据删除时,表中所有的关联数据字段设置为SET()中设置的值,与models.SET_DEFAULT相似,只不过此时从表中的相关字段不需要设置default参数
7.models.DO_NOTHING
什么都不做,一切都看数据库级别的约束
'数据库级别的默认约束为RESTRICT,这个约束与django中的models.PROTECT相似'
多对多三种创建方式
1.自动创建
authors = models.ManyToManyField(to='Author')
'优点是第三张表自动创建,缺点是第三张表扩展性差'
2.手动创建
class Book(models.Model):
pass
class Author(models.Model):
pass
class Book2Author(models.Model):
book_id = models.ForeignKey(to="Book")
author_id = models.ForeignKey(to="Author")
'优点就是扩展性强,缺点是无法使用正反向查询以及多对多四个方法'
3.半自动创建
class Book(models.Model):
authors = models.ManyToManyField(to='Author',
through='Book2Author'
through_fields=('book_id','author_id'))
class Author(models.Model):
pass
class Book2Author(models.Model):
book_id = models.ForeignKey(to="Book")
author_id = models.ForeignKey(to="Author")
'优点是展性强并且支持正反向查询,缺点是无法使用多对多四个方法'