ORM

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')
  1. 一对多 / 多对多添加对象
# 第一步:给多表中添加对象时,如果添加对象不存在,则会报错

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. 分组和聚合

  1. 分组
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},{},{}]>
  1. 聚合
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))
  1. 按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查询

  1. 查询评论数大于收藏数的书籍
from django.db.models import F

models.Book.objects.filter(comment_num__gt=F('fav_num'))
  1. 查询评论数小于收藏数2倍的书籍
models.Book.objects.filter(commnet_num__lt=F('fav_num')*2)
  1. 修改操作也可以使用F函数,比如将每一本书的价格提高10元
models.Book.objects.all().update(price=F("price")+10)
  1. 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查询

  1. 查询价格小于30的或出版日期是2019年的
models.Book.objects.filter(Q(price__lt=30) | Q(publish_date__year=2019))

5. .values

  1. 获取查找结果中的部分字段,返回字典格式数据
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

  1. 类似于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:产生一条查询语句

  1. 在提取某个模型数据时,也提前将相关的数据提取出来,比如提取书籍数据,可以使用select_related将book信息提取出来,以后再次使用book.publisher的时候就不需要再次去访问数据库了,可以减少数据库的查询次数。
  2. 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:产生两条查询语句

  1. 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)
  1. 注意点:如果对使用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)
  1. 解决方法
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

  1. 在一些表中,可能存在很多的字段,但是一些字段的数据可能是比较庞大的,而此时你又不需要,这时候就可以使用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

  1. 与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

  1. 根据条件进行查找,如果查找到了就返回这条数据,如果没有查找到,那么就创建一条新数据,返回值为元组,(对象实例,是否是新创建的(False / True))
obj, created = Book.objects.get_or_create(
    title='天方夜谭',
    price=90,
    defaults={'publisher_date': 'xxx', 'publisher': 'xxx'},
)
  1. 如果查询结果存在,则返回该条件查询出的对象,否则,使用defaults进行创建对象

11. .bulk_create

  1. 批量创建
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)
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值