Django中orm相关操作

必知必会13条
示例model
单表双下划线
外键相关
多对多相关
聚合和分组
F、Q
orm性能相关
事务

必知必会13条

import os
import django

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day50_orm.settings")
django.setup()
from app01 import models

# 一、all 查询所有的数据,返回的是一个对象列表(QuerySet)
ret = models.Person.objects.all()
print('all',ret)
# 二、get 获取一个存在且唯一的数据,返回的是对象,没有或者是多个就报错
ret = models.Person.objects.get(pid=4)
print('get',ret.name)
# 三、filter 筛选出符合条件的,返回的是对象列表
ret = models.Person.objects.filter(age=18)
print('filter',ret)
# 四、exclude 获取不满足条件的数据,返回的是对象列表
ret = models.Person.objects.exclude(name='alex')
print('exclude',ret)
for i in ret:
    print(i.name)
# 五、order_by 排序,默认为升序,字段前加-为降序
ret = models.Person.objects.all().order_by('pid')
print('order_by',ret)
for i in ret:
    print(i.pid)
# 六、reverse 对排完序的对象列表进行反转
ret = models.Person.objects.all().order_by('pid').reverse()
print('reverse',ret)
for i in ret:
    print(i.pid)
# 七、values 不指定字段,获取数据所有的字段和值,返回的是对象列表(QuerySet)[{},{}]
ret = models.Person.objects.all().values()
print('values',ret)
# 指定字段,获取到数据指定的字段名和值,返回的是对象列表(QuerySet)[{},{}]
ret = models.Person.objects.all().values('name', 'age')
print('values',ret)
# 八、values_list 不指定字段,获取所有数据字段的值,返回的是对象列表(QuerySet)[{},{}]
ret = models.Person.objects.all().values_list()
print('values_list',ret)
# 指定字段,获取到数据指定的字段的值,返回的是对象列表(QuerySet)[{},{}]
ret = models.Person.objects.all().values_list('name', 'age')
print('values_list',ret)
# 九、distinct 对获取到的数据进行去重,返回的是对象列表(QuerySet)[{},{}]
ret = models.Person.objects.values('age').distinct()
print('distinct',ret)
# 十、count 对获取到的数据进行计数
ret = models.Person.objects.filter(age=18).count()
print('count',ret)
# 十一、first 获取第一个元素
ret = models.Person.objects.all().first()
print('first',ret.pid)
# 十二、last 获取最后一个元素
ret = models.Person.objects.all().last()
print('last',ret.pid)
# 十三、exists 判断数据是否存在,返回的是对象列表,如果对象不存在,返回的是空对象列表
ret = models.Person.objects.filter(pid=1)
print('exists',ret)
  • 返回对象列表
    • all
    • filter
    • exclude
    • order_by
    • reverse
    • values [{},{}]
    • values_list [(),()]
    • distinct [{},{}]
  • 返回对象
    • get
    • first
    • last
  • 返回数字
    • count
  • 返回布尔值
    • exists

示例model模型

  from django.db import models
  
  class MyCharField(models.Field):
      """
      自定义的char类型的字段类
      """
  
      def __init__(self, max_length, *args, **kwargs):
          self.max_length = max_length
          super(MyCharField, self).__init__(max_length=max_length, *args, **kwargs)
  
      def db_type(self, connection):
          """
          限定生成数据库表的字段类型为char,长度为max_length指定的值
          """
          # return 'char(%s)' % self.max_length
          return f'char{self.max_length}'
  class Person(models.Model):
      pid = models.AutoField(primary_key=True)
      name = models.CharField(db_column='nick', max_length=32, )
      age = models.IntegerField(null=True, default=18)
      phone = MyCharField(verbose_name='电话', max_length=11, blank=True, unique=True)
      birth = models.DateTimeField(auto_now_add=True)
      sex = models.BooleanField(choices=((True, '男'), (False, '女')))
      gender = models.IntegerField(choices=((1, '男'), (2, '女'), (3, '不详')))
      
      def __str__(self):
          return f'{self.pid}-{self.name}-{self.age}'
      
      class Meta:
          db_table = 'person'  # 数据库中表的名字
          verbose_name = '个人信息'  # admin中的表名称
          verbose_name_plural = '所有用户信息'
          index_together = [
              ('name', 'age')
          ]  # 联合索引,查询两个存在的字段,全部查询速度快,单查左边速度快
          unique_together = ('name', 'age')  # 联合唯一约束,查询两个存在的字段
  
  
  class Publisher(models.Model):
      name = models.CharField(max_length=32)
  
      def __str__(self):
          return f'<Publisher object>:{self.id}-{self.name}'
  
  class Book(models.Model):
      name = models.CharField(max_length=32)
      pub = models.ForeignKey('Publisher', on_delete=models.CASCADE, related_name='book')
  
      def __str__(self):
          return f'<Book object>:{self.id}-{self.name}-{self.pub.name}'
  
  class Author(models.Model):
      name = models.CharField(max_length=32, verbose_name='作者')
      books = models.ManyToManyField('Book')
  
      def __str__(self):
          return f'<Author object>:{self.id}-{self.name}'

单表查询之神奇的下划线

import os, django
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day50_orm.settings")
django.setup()
from app01 import models

ret = models.Person.objects.filter(pid__lt=5)  # less than
ret = models.Person.objects.filter(pid__lte=5)  # less than equal

ret = models.Person.objects.filter(pid__gt=5)  # greater than
ret = models.Person.objects.filter(pid__gte=5)  # greater than equal

ret = models.Person.objects.filter(pid__in=[5, 6, 11]) # 判断在列表里面
ret = models.Person.objects.exclude(pid__in=[5, 6, 11])
ret = models.Person.objects.filter(pid__range=[1, 10]) # 判断范围在1-10,包括10.

ret = models.Person.objects.filter(name__contains='a') # 判断name属性里包不包含a
ret = models.Person.objects.filter(name__icontains='a') # 大小写不敏感

ret = models.Person.objects.filter(name__startswith='s') # 判断name是否已a开头
ret = models.Person.objects.filter(name__istartswith='s') # 大小写不敏感

ret = models.Person.objects.filter(name__endswith='t') # 判断name是否以t结尾
ret = models.Person.objects.filter(name__iendswith='t') # 大小写不敏感

ret = models.Person.objects.filter(birth__year='2020') # 判断年是不是2020
ret = models.Person.objects.filter(birth__contains='-04-') # 判断月份是不是4月
# ret = models.Person.objects.filter(birth__month='02')
# ret = models.Person.objects.filter(birth__day='21')

ret = models.Person.objects.filter(age__isnull=True) # 判断age这个字段是否为空

print(ret)

外键(ForeignKey)查询的相关操作

import os, django
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day50_orm.settings")
django.setup()
from app01 import models

# 基于对象的查询
# 正向查询
    book_obj = models.Book.objects.get(id=1)  # 获取对应的book对象
    print(book_obj.pub)  # 关联的出版社对象
    print(book_obj.pub_id)  # 关联的出版社id
# 反向查询
    pub_obj = models.Publisher.objects.get(id=1)
    print(pub_obj)  # 出版社对象
    # 不指定related_name,使用类名_set获取到关系关联对象,在通过.all()获取到对应的所有对象
    print(pub_obj.book_set.all())  # pub_obj.book_set关系关联对象,类名_set.all()得到出版社所有的书籍
    print(type(pub_obj.book_set))  # pub_obj.book_set关系关联对象
    # 指定related_name,使用related_name的值获取到关系关联对象,在通过.all()获取到对应的所有对象
    print(pub_obj.book.all())  # pub_obj.book_set关系关联对象

# 基于字段的查询
    ret = models.Book.objects.filter(pub__name__contains='晓龙出版社')
    # 不指定related_name,直接类名小写__name
    ret = models.Publisher.objects.filter(book__name='少年阿兵')
    # 指定related_name,使用related_name的值 + __name
    ret = models.Publisher.objects.filter(book__name='少年阿兵')
    # 指定了related_query_name,使用related_query_name的值
    # 不指定related_query_name,使用related_name的值
    print(ret)

###################  外键  ###################

pub_obj = models.Publisher.objects.get(id=1)
# set  add  create  [id,id]
pub_obj.books.set(*models.Book.objects.filter(id__in=[1,2,3,4] ))
pub_obj.books.add(*models.Book.objects.all())

# remove clear  外键字段参数 null=True 才有这两个方法
pub_obj.books.clear()

多对多(ManyToManyField)查询的相关操作

import os
import django
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day50_orm.settings")
django.setup()
from app01 import models

author_obj = models.Author.objects.get(id=8)  # 获取id=8的作者对象
print(author_obj.books.all())  # 获取到关系关联对象的所有数据
# 关系关联对象的方法
# all 获取所有的数据
# set 设置多对多的关系,[id,id]/[对象,对象]
author_obj.books.set([1,4])  # 通过关系关联对象给多对多表里author_id=8设置book_id
author_obj.books.set(*models.Book.objects.filter(id__in=[2,4]))

# add 添加多对多关系,[id,id]/[对象,对象]
author_obj.books.add(4,3,2)  # 通过关系关联对象给多对多表里author_id=8添加book_id
author_obj.books.add(*models.Book.objects.filter(id__in=[1,2,3,4]))

# remove 删除多对多关系,[id,id]/[对象,对象]
author_obj.books.remove(1)
author_obj.books.remove(*models.Book.objects.filter(id__in=[4]))

# clear 清空多对多关系
author_obj.books.clear()

# create 新建一个book对象与当前的对象建立关系,在book表新建一个对象,并且和当前的作者进行关联
author_obj.books.create(name='alexdsb',pub_id=2)


book_obj = models.Book.objects.get(id=1)  # 获取到id=1的book对象
print(book_obj.author_set.all())  # 通过反向查询到对应的作者对象
book_obj.author_set.set([8,9,10])  # 通过作者对象给多对多表里book_id=1的设置author_id

聚合与分组查询

import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day50_orm.settings")
import django
django.setup()
from app01 import models
from django.db.models import Max, Min, Avg, Count, Sum  # 导入聚合函数

聚合 aggregate,终止子句
  ret = models.Book.objects.aggregate(Count('price'))  # 统计有多少本书
  print(ret)
  # 统计出book_id大于3的书里的最大价格和最低价格
  ret = models.Book.objects.filter(id__gt=3).aggregate(max=Max('price'),min=Min('price'))
  # 统计所有书里最大价格和最低价格
  ret = models.Book.objects.all().aggregate(max=Max('price'),min=Min('price'))
	print(ret)
分组 annotate 注释,给聚合之后的结果添加一个字段
  # 1.统计每一本书的作者个数
  ret = models.Book.objects.annotate(Count('author__name')).values()
  for i in ret:
      print(i)
  # 2.统计出每个出版社卖的最便宜的书
  # 方法一、
  ret = models.Publisher.objects.annotate(Min('book__price')).values()
  for i in ret:
      print(i)
  # 方法二、
  ret = models.Book.objects.values('pub','pub__name').annotate(Min('price'))
  for i in ret:
      print(i)
  # 3.统计不止一个作者的图书
  ret = models.Book.objects.annotate(count=Count('author__name')).filter(count__gt=1)
  print(ret)
  # 4.根据一本图书作者的数量的多少对查询集 进行排序
  # 方式一
  ret = models.Book.objects.annotate(count=Count('author__name')).order_by('-count')
  .values()
  # 方式二
  ret = models.Author.objects.values('books','books__name').annotate(count=Count('name'))
  .order_by('-count')
  for i in ret:
      print(i)
  # 5.查询各个作者出的书的总价格
  # 方式一
  ret = models.Author.objects.annotate(sum=Sum('books__price')).values()
  # 方式二
  ret = models.Book.objects.values('author','author__name').annotate(sum=Sum('price'))
  for i in ret:
      print(i)

F和Q查询

import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day50_orm.settings")
import django
django.setup()
from app01 import models
from django.db.models import F, Q

F操作 - 用于字段与字段之间的比较
  ret = models.Book.objects.filter(stock__gt=50)  # 统计库存大于50的书籍
  for i in ret:
      print(i)
  ret = models.Book.objects.filter(sales__gt=F('stock'))  # 统计销量大于库存的书籍
  for i in ret:
      print(i)
  # ret = models.Book.objects.all().update(stock=F('stock')+10)  # 给所有书籍的库存加10
  # ret = models.Book.objects.all().update(stock=F('stock')-10)  # 给所有书籍的库存减10
  
Q操作
  # 与 &
  # 或 |
  # 非 ~
  # 获取id小于3或者id大于4的书籍
  # 方式一、用exclude排除
  ret = models.Book.objects.exclude(id__gte=3,id__lte=4)
  print(ret)
  # 方式二、用Q操作
  ret = models.Book.objects.filter(Q(id__lt=3)|Q(id__gt=4))
  print(ret)

  # 获取id小于3或者id大于4,且书名以‘少年’开头的书籍
  ret = models.Book.objects.filter(Q(id__lt=3) | Q(id__gt=4) & Q(name__startswith='少年'))
  print(ret)
  # 获取id小于3=,且书名以‘少年’开头的书籍
  ret = models.Book.objects.filter(Q(id__lt=3) & Q(name__startswith='少年'))
  print(ret)

  # 获取id小于3或者id大于4,且书名不以‘少年’开头的书籍
  ret = models.Book.objects.filter(Q(id__lt=3) | Q(id__gt=4) & ~Q(name__startswith='少年'))
  print(ret)
  # 获取id小于3=,且书名不以‘少年’开头的书籍
  ret = models.Book.objects.filter(Q(id__lt=3) & ~Q(name__startswith='少年'))
  print(ret)

orm性能相关

Debug_tool_bar

1 能用values的尽量不用对象.的形式来获取数据
    students = models.Student.objects.all().values('classes__name') #链表查询,查询一次 queryset[{'classes__name':'27期'},{'classes__name':'27期'},{'classes__name':'27期'}]
    for s in students:
        # print(s.classes.name)  #查询多次,每次点都进行一次sql查询,效率低
        print(s['classes__name'])  

        
2 通过select_related直接进行连表查询  针对一对一和一对多
    students = models.Student.objects.all().select_related('classes')
    for s in students:
        print(s.classes.name)

3 通过prefetch_related子查询来完成
    students = models.Student.objects.all().prefetch_related('classes')
    for s in students:
        print(s.classes.name)
        
4 only和defer

		当我们进行orm查询的时候,你通过翻译出来的sql语句可以看到,每次查询都是查询了每个字段的数据,所以我们通过only和defer,可以指定查询哪些字段数据
    all_students = models.Student.objects.all().only('name')#只要这个字段数据
    all_students = models.Student.objects.all().defer('name')#排除,除了这个字段其他字段数据都要

事务

import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day50_orm.settings")
import django
django.setup()
from app01 import models
from django.db.models import F
from django.db import transaction

try:
  with transaction.atomic():
      models.Book.objects.all().update(stock=F('stock') + 20)
      models.Book.objects.all().update(sales=F('sales') - 20)
except Exception as e:
  print(e)
原文地址:https://www.cnblogs.com/zuoxiaodragon/p/13566281.html