1. 外键的跨表查询
class Book(models.Model):
title = models.CharField(max_length=32, verbose_name="书名")
publisher_date = models.DateField(auto_now_add=True)
price = models.DecimalField(max_digits=5, decimal_places=2)
publisher = models.ForeignKey(Publisher, related_name='book', related_query_name='books')
class Publisher(models.Model):
name = models.CharField(max_length=32, verbose_name="出版社名")
city = models.CharField(max_length=32, verbose_name="城市名")
class Author(models.Model):
name = models.CharField(max_length=32, verbose_name="作者姓名")
books = models.ManyToManyField(to="Book", related_name="authors")
- 正向查询基于对象
book_obj = models.Book.objects.get(id=1)
book.publisher.name
- 正向查询基于双下划线
models.Book.objects.filter(id=1).values("publisher__name")
- 反向查询基于对象
- related_query_name:改变连表查询时关联表的名称
publisher_obj = models.Publisher.objects.get(id=1)
# 没有配置related_name
publisher_obj.book_set.all()
# 配置related_name='book'
publisher_obj.book.all()
# 如果配置了related_query_name='bs'
publisher_obj.objects.filter(bs__price__gte=50)
# 如果未配置时则使用原表名
publisher_obj.objects.filter(book__price__gte=50)
- 反向查询基于双下划线
publisher_obj = models.Publisher.objects.filter(id=1).values('book__name')
- 一对多 / 多对多添加对象
# 第一步:给多表中添加对象时,如果添加对象不存在,则会报错
publishers = Publisher.objects.get(id=1)
# 创建book对象
book = Book(title='十万个为什么', price=45)
# 将book对象添加给出版社,此处会出错,因为book对象没有被保存进数据库
publishers.book.add(book)
# 保存对象时,字段指定不全,且未指定字段值不能为空时,会报错
publishers = Publisher.objects.get(id=1)
# 创建book对象
book = Book(title='十万个为什么', price=45)
book.save() # 此处会报错,因为我们未指定他的出版社,且出版社字段时必须填写字段
# 将book对象添加给出版社,此处会出错,因为book对象没有被保存进数据库
publishers.book.add(book)
# 解决方法
publishers = Publisher.objects.get(id=1)
# 创建book对象
book = Book(title='十万个为什么', price=45)
# 将book对象添加给出版社,系统会自动将publisher绑定给该书籍对象,且将该对象进行保存
publishers.book.add(book, bulk=False)
2. 分组和聚合
- 分组
from django.db.models import Count
# 根据书名统计每个书名有多少本书(可能书存在第一部、第二部...)
# 此处的order_by()是很重要的,否则可能出现同名书的多条数据
book_list = models.Book.objects.all().values('name').annotate(name_num=Count('name'))values("name", "name_num").order_by()
# 查询每本书的作者数量 related_name 值
# 将查询统计出来的结果保存为author_num字段,相当于对象多了一个属性
book_list = models.Book.objects.all().values('authors').annotate(author_num=Count('authors'))
# 输出书名和作者数量
book_list = models.Book.objects.all().values('authors').annotate(author_num=Count('authors')).values("title", "author_num")
<QuerySet [{"title": "天方夜谭", "author_num": 3},{},{}]>
- 聚合
from django.db.models import Avg, Sum, Max, Min, Count
# 求所有书本的平均价格,得到一个字典{"price__avg": 200}
models.Book.bjects.all().aggregate(Avg('price'))
# 指定返回字典的聚合名:{"book_price_avg": 200}
models.Book.bjects.all().aggregate(book_price_avg=Avg('price'))
# 统计有多少个不同的价格
models.Book.bjects.all().aggregate(book_price_count=Count('price', distinct=True))
- 按name字段分组,对所有资源聚合进行求和统计(此数据和上述模型无关)
user_bills = TenantStatement.objects.filter(tenant=tenant, user=user,
created_at__range=(current_date - timedelta(days=7),
current_date)).order_by('name')\
.values('name').annotate(instance_money=Sum(F('instance_cpu')+F('instance_mem_mb'),
output_field=DecimalField()),
hpc_money=Sum(F('hpc_cpu')+F('hpc_mem_mb'),
output_field=DecimalField()),
virtual_desktop_money=Sum(F('virtual_desktop_cpu')+F('virtual_desktop_mem_mb')+
F('virtual_desktop_bs_gb')+F('virtual_desktop_gpu'),
output_field=DecimalField()),
block_storage_money=Sum('block_storage_gb'),
file_storage_money=Sum('file_storage_gb'),
backup_storage_money=Sum('backup_storage_gb'),
virtual_app_money=Sum('virtual_app')
).values('name', 'instance_money', 'hpc_money', 'virtual_desktop_money',
'block_storage_money', 'file_storage_money', 'backup_storage_money',
'virtual_app_money')
bill_date_lists = list()
bill_money_instance = list()
bill_money_hpc = list()
bill_money_block = list()
bill_money_file = list()
bill_money_backup = list()
bill_money_desktop = list()
bill_money_virtual_app = list()
for i in user_bills:
bill_date = datetime.strptime(i['name'], '%Y-%m-%d')
bill_date_lists.append(bill_date.strftime('%m-%d'))
bill_money_instance.append(float(round(i['instance_money'], 2)))
bill_money_hpc.append(float(round(i['hpc_money'], 2)))
bill_money_block.append(round(i['block_storage_money'], 2))
bill_money_file.append(round(i['file_storage_money'], 2))
bill_money_backup.append(round(i['backup_storage_money'], 2))
bill_money_desktop.append(float(round(i['virtual_desktop_money'], 2)))
bill_money_virtual_app.append(round(i['virtual_app_money'], 2))
bill = {
'bill_date_lists': bill_date_lists,
'bill_money_instance': bill_money_instance,
'bill_money_hpc': bill_money_hpc,
'bill_money_block': bill_money_block,
'bill_money_file': bill_money_file,
'bill_money_backup': bill_money_backup,
'bill_money_desktop': bill_money_desktop,
'bill_money_virtual_app': bill_money_virtual_app
}
return bill
{
'bill_date_lists': ['09-25', '09-26', '09-27', '09-28', '09-29'],
'bill_money_instance': [46.07, 16.78, 14.69, 15.3, 37.58],
'bill_money_hpc': [0.0, 0.0, 0.0, 0.0, 0.0],
'bill_money_block': [2.07, 0.19, 0.23, 0.24, 28.55],
'bill_money_file': [0.0, 0.0, 0.0, 0.0, 0.0],
'bill_money_backup': [0.0, 0.0, 0.0, 0.14, 7.44],
'bill_money_desktop': [1.22, 0.0, 0.0, 0.0, 0.0],
'bill_money_virtual_app': [0.0, 0.0, 0.0, 0.0, 0.0]
}
- 这是我目前为止写的最长的一条查询语句,作以记录
3. F查询
- 查询评论数大于收藏数的书籍
from django.db.models import F
models.Book.objects.filter(comment_num__gt=F('fav_num'))
- 查询评论数小于收藏数2倍的书籍
models.Book.objects.filter(commnet_num__lt=F('fav_num')*2)
- 修改操作也可以使用F函数,比如将每一本书的价格提高10元
models.Book.objects.all().update(price=F("price")+10)
- Char字段类型操作:把所有书名后面加上(第一版)
from django.db.models.functions import Concat
from django.db.models import Value
models.Book.objects.all().update(title=Concat(F("title"), Value("("), Value("第一版"),Value(")")))
4. Q查询
- 查询价格小于30的或出版日期是2019年的
models.Book.objects.filter(Q(price__lt=30) | Q(publish_date__year=2019))
5. .values
- 获取查找结果中的部分字段,返回字典格式数据
from django.db import models
from django.db.models import Count
# 查找结果只需要返回书的id、名称、作者名
models.Book.objects.all().values('id', 'title', 'author__name')
# {"id": xxx, "name": xxx, "author__name": xxx}
# 作者名变量,重新指定为auth_name,切忌指定的变量名不能是存在于Book表中的字段名
models.Book.objects.all().values('id', 'title', auth_name='author__name')
# {"id": xxx, "name": xxx, "auth_name": xxx}
# 在.values中使用聚合函数(统计每个出版社出版的书籍数量)
models.Publisher.objects.all().values('id', 'name', 'city', book_count=Count('book__id'))
# {"id": xxx, "name": xxx, "city": xxx, "book_count": xxx}
6. .values_list
- 类似于values,只不过返回结果为元祖,只有字段值没有字段名
from django.db import models
# 查找结果只需要返回书的id、名称、作者名
models.Book.objects.all().values_list('id', 'title', 'author__name')
# (xxx, xxx, xxx)
# 当返回结果只有一个字段时,我们可以给定参数flat=True,此时直接返回数据真实值,而不是元祖形式,便于我们处理
models.Book.objects.all().values_list('id')
# xxx
7. .select_related:产生一条查询语句
- 在提取某个模型数据时,也提前将相关的数据提取出来,比如提取书籍数据,可以使用
select_related
将book信息提取出来,以后再次使用book.publisher的时候就不需要再次去访问数据库了,可以减少数据库的查询次数。 select_related
只能用在多对一
、一对一
关系表中,即从多表查询一表时使用。
from django.db import models
book = models.Book.objects.get(id=1)
book.publisher.name # 此处重新执行了一次数据库查询(查询出版社信息)
# 可以指定多个关系外键
publisher = models.Book.objects.select_related('publisher').get(id=1)
book.publisher.name # 不需要再次执行数据库查询(第一次查询书籍信息时已经查询出来出版社相关相关信息了)
8. .prefetch_related:产生两条查询语句
prefetch_related
方法和select_related
方法相似,就是在访问多个表中的数据时,减少查询次数。这个方法是为了解决多对多
和一对多
的关系查询问题,即从一表查询多表时使用。
from django.db import models
publisher= models.Publisher.objects.get(id=1)
publisher.book.title # 此处重新执行了一次数据库查询(查询书籍信息)
# 可以指定多个关系外键
publisher = models.Publisher.objects.prefetch_related('book').get(id=1)
publisher.book.title # 不需要再次执行数据库查询(第一次查询出版社信息时已经查询出来书籍相关相关信息了)
# 正常查询
publishers = Publisher.objects.all() # 获取所有出版社信息
for publisher in publishers:
books = publisher.book.all() # 循环获取每个出版社下的所有书籍信息(每次循环都去book表中查询)
for book in books: # 循环每个出版社下的每本书籍的名称
print(book.title)
# 优化查询 book_set
publishers = Publisher.objects.prefetch_related('book') # 获取所有出版社信息
for publisher in publishers:
# books = publisher.book_set.all()
books = publisher.book.all() # 循环获取每个出版社下的所有书籍信息(不再去book表中查询)
for book in books: # 循环每个出版社下的每本书籍的名称
print(book.title)
- 注意点:如果对使用prefetch_related查询出来的结果进行过滤的话,会导致prefetch_related方法没意义,因为过滤时会再次去关联表中查询,示例如下:
# 优化查询 book_set
publishers = Publisher.objects.prefetch_related('book') # 获取所有出版社信息
for publisher in publishers:
# books = publisher.book_set.all()
books = publisher.book.filter(price__gte=10) # 此时会再去book表中进行查询,导致 prefetch_related 方法失去意义
for book in books:
print(book.title)
- 解决方法
from django.db.models import Prefetch
prefetch = Prefetch('book', queryset=Book.objects.filter(price__gte=10))
publishers = Publisher.objects.prefetch_related(prefetch)
for publisher in publishers:
# books = publisher.book_set.all()
books = publisher.book.all() # 此处不要进行过滤等操作,如果必要,则去prefetch中操作
for book in books:
print(book.title)
9. .defer
- 在一些表中,可能存在很多的字段,但是一些字段的数据可能是比较庞大的,而此时你又不需要,这时候就可以使用
defer
来进行过滤掉
那些不需要的字段,这个字段跟values
有点类似,只不过defer返回的不是字典,而是模型对象。
book_obj = Book.objects.defer('price') # id字段操作不了
for book in book_obj:
print(type(book))
print(book.title)
# 如果此时再次查询price字段值,依然是可以查询出来的,因为它又重新去数据库查询了一次
print(book.price) # 这样做就失去了意义,所以如果需要你就将其,如果过滤掉了,你就不要再去查询该字段
10. .only
- 与defer相反,
过滤出
需要的字段
book_obj = Book.objects.only('title') # id字段操作不了
for book in book_obj:
print(type(book))
print(book.title)
# 如果此时再次查询price字段值,依然是可以查询出来的,因为它又重新去数据库查询了一次
print(book.price) # 这样做就失去了意义
11. .get_or_create
- 根据条件进行查找,如果查找到了就返回这条数据,如果没有查找到,那么就创建一条新数据,返回值为元组,(对象实例,是否是新创建的(False / True))
obj, created = Book.objects.get_or_create(
title='天方夜谭',
price=90,
defaults={'publisher_date': 'xxx', 'publisher': 'xxx'},
)
- 如果查询结果存在,则返回该条件查询出的对象,否则,使用
defaults
进行创建对象
11. .bulk_create
- 批量创建
from book.models import Publisher
publisher_list = []
for i in range(100):
publisher_list.append({
"name": '沙河出版社' + str(i),
"city": '北京' + str(i)
})
Publisher.objects.bulk_create([Publisher(**publisher) for publisher in publisher_list])
12. 事务
import os
if __name__ == '__main__':
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "BMS.settings")
import django
django.setup()
import datetime
from app01 import models
try:
from django.db import transaction
with transaction.atomic(): # with语句内部代码块出错,将会导致with代码块内部的所有语句都回滚到最初状态,例如出版社创建成功,但书籍对象创建时指定了一个不存在的出版社id,所以会创建失败,此时,将会执行数据库回滚操做,之前创建成功的出版社对象也将消失。
new_publisher = models.Publisher.objects.create(name="火星出版社")
models.Book.objects.create(title="橘子物语", publish_date=datetime.date.today(), publisher_id=10) # 指定一个不存在的出版社id
except Exception as e:
print(str(e))
Django终端打印SQL语句
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console':{
'level':'DEBUG',
'class':'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'propagate': True,
'level':'DEBUG',
},
}
}
在Python脚本中调用Django环境
import os
if __name__ == '__main__':
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "BMS.settings")
import django
django.setup()
from app01 import models
books = models.Book.objects.all()
print(books)