第十二章 ORM的多表操作

1.多对多操作

1.1 环境准备
# test.py
import os, django
os.environ.setdefault('DJANGO_SETTINGS_MODULE', '项目名.settings')
django.setup()
from app01 import models
# models.py
from django.db import models
# 出版社和书的一对多关系
class Publisher(models.Model):
    name = models.CharField(max_length=32)
    def __str__(self):
        return '{}'.format(self.name)
# 书籍信息 
class Book(models.Model):
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=6, decimal_places=2)
    pub = models.ForeignKey('Publisher', on_delete=models.CASCADE)
    def __str__(self):
        return '{}'.format(self.title)
# 作者信息
class Author(models.Model):
    name = models.CharField(max_length=32)
    books = models.ManyToManyField('Book', related_name='authors', related_query_name='xxx')
    def __str__(self):
        return '{}'.format(self.name)
1.2 基于对象的查询
  • 正向查询

author = models.Author.objects.get(pk=1)
# 关系管理对象,从1向多的方向拿都是关系管理对象
author.books
# 所有书籍
models.Author.objects.get(pk=1).books.all()
  • 反向查询(book表中没有外键字段)

  • 多对多也同样适用

# book对象
book = models.Book.objects.filter(title='python之旅').first()
# 关系管理对象,不指定related_name参数
book.author_set
# 获取所有作者
book.author_set.all()
# 指定related_name='authors'
book.authors.all()
1.3 基于字段
# 不指定related_name/ related_query_name
author = models.Author.objects.filter(books__title='python之旅')
print(author)
# 不指定related_name/ related_query_name
book = models.Book.objects.filter(author__name='echo')
print(book)
# 指定related_name和related_query_name优先使用related_query_name='xxx'
book = models.Book.objects.filter(xxx__name='echo')
print(book)
1.4 关系管理对象方法(6)
  • 通过关系管理对象获取多个关联值

# 关系管理对象
author = models.Author.objects.get(pk=1)
book = models.Book.objects.get(pk=1)
publisher = models.Publisher.objects.get(pk=1)

1.all()

books = author.books.all()
2. set([])
  • 重新设置数据

  • 值或对象

# 会覆盖历史数据
ret = author.books.set(['书籍的id1','书籍的id2'...])
# 返回值ret为None
# 可以写对象
author.books.set(modles.Book.objects.filter(pk__in[1,2,3]))
3. add()
  • 值或对象

# 添加数据,已有数据不会新增
author.books.add('书籍的id1','书籍的id2'...)
# 添加对象,* 表示打散
author.books.add(*models.Book.objects.filter(pk__in=['书籍的id1','书籍的id2'...]))
4. remove()
  • 值或对象

# 删除数据
author.books.remove('书籍的id1','书籍的id2'...)
# 删除对象
author.books.remove(*models.Book.objects.filter(pk__in=['书籍的id1','书籍的id2'...]))

5.clear()

# 清除author对象的所有的多对多关系
author.books.clear()

6.create(字段=值)

# 创建书的信息,并添加关联信息
obj = author.books.create(title='python', pub_id=1)
# 通过书创建作者
book = models.objects.get(pk=1)
obj = book.authors.create(name='diane')
1.5 外键的方法
  • 在外键中只能使用对象

  • pub和book关系

  • fitlter、get

1. all()
# 不指定related_name和related_query_name时,使用 类名_set获取关系管理对象
pub = models.Publisher.objects.get(pk=1)
pub.book_set.all()
2. set(QuerySet对象)
  • 对象列表

publisher = models.Publisher.objects.get(pk=1)
# 不能使用id ,只能使用对象
publisher.books.set(models.Book.bojects.fitler(pk__in[4,5]))
3. add(*QuerySet)
  • 一个个对象

publisher.books.add(*models.Book.bojects.fitler(pk__in[4,5]))
4. remove(*QuerySet)
  • 一个个对象

# remove/clear, 外键必须设置成 null=True参数
publisher.books.remove(*models.Book.bojects.fitler(pk__in[4,5]))

5.clear()

publisher.books.clear()

6.create(字段=值)

models.Publisher.objects.get(pk=1).books.create(title='xxx', price=10)
Note
  1. 对于所有类型的关联字段,add()、create()、remove()和clear(),set()都会马上更新数据库。换句话说,在关联的任何一端,都不需要再调用save()方法。

2. 聚合和分组

  1. aggregate()是QuerySet的一个终止子句,意思是说,它返回一个包含一些键值对的字典。

  2. 键的名称是聚合值的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。

  3. 用到的内置的聚合函数

from django.db.models import Avg, Sum, Max, Min, Count, F, Q
2.1 聚合
  • aggregate(聚合函数)

ret = models.Book.objects.all().aggregate(Max('price'))
# 省略all()也会生效
ret = models.Book.objects.aggregate(Max('price'))
# dict 类型,可以
print(ret)
# 如果给聚合结果重命名,注意位置和关键字传参的原则
ret = models.Book.objects.all().aggregate(Avg('price'),max=Max('price'))
# 返回值为字典,给终止子句
ret = models.Book.objects.filter(pk_gt=3).aggregate(Avg('price'),max=Max('price'))
2.2 分组
1. 基于字段
  • annotate注释,基于当前对象,添加一个注释字段

# .all()可以省略不写
ret = models.Book.objects[.all()].annotate(count=Count('author'))
for i in ret:
    print(i.count)
2.分组方式1
# 以出版社的ID进行分组
ret = models.Publisher.objects.annotate(Min('book__price').values()
for i in ret:
    print(i)
3. 分组方式2
  • values表示分组的字段

ret = models.Book.objects.values('pub/pub_id/pub__name').annotate(Min('price'))
for i in ret:
  print(i)
# 错误示范,如果values添加额外字段,则分组条件也会添加这个字段
ret = models.Book.objects.values('pub/pub_id/pub__name').annotate(Min('price')).values()
# 正确
ret = models.Book.objects.values('pub/pub_id/pub__name').annotate(min=Min('price')).values('pub_id', min)
  • 示例
# 统计每本书的作者个数
obj = models.Book.objects.values('title').annotate(Count('xxx__name'))
for i in obj:
    print(i)
    
    
# 统计出每个出版社买的最便宜的书的价格
obj = models.Publisher.objects.values('name').annotate(Min('xxx__price'))
for i in obj:
  print(i)

# 统计不止一个作者的图书,比较两种分组方式的区别
ret=models.Book.objects.annotate(count=Count('author__id')).filter(count__gt=1)
print(ret)
obj=models.Book.objects.values('title').annotate(count=Count('xxx__id')).filter(count__gt=1)
print(obj)

# 根据一本图书作者数量的多少对查询集 QuerySet进行排序
obj = models.Author.objects.annotate(count=Count('books__id')).order_by('count')
print(obj)

# 查询各个作者书的总价格
ret = models.Author.ojects.annotate(Sum('books__price')).values()
print(ret)

3. F和Q查询

3.1 F查询
  • F('字段名'),取出字段值进行相应操作

ret = models.Book.objects.filter(price__gt=100)
print(ret)
1.比较两个字段值

# F,动态获取字段值
from django.db.models import F
ret = models.Book.objects.filter(sale__gt=F('inventory'))

2.更新操作
# 更改一个对象,会更新所有的对象
obj = models.Book.objects.get(pk=1)
obj.sale = 100
obj.save()
  • 批量更新某一字段,update效率较高
# 批量更新,queryset对象支持update, 只更新sale字段
obj = models.Book.objects.filter(pk=1).update(sale=100)
# 直接更新到数据库
models.Book.objects.filter(pk=1).update(sale=F('sale')*2+10)
3.2 Q查询
  • 表示条件进行使用。

  • 使用逻辑关系(| 或, & 与,~ 非)

from django.db.models import Q
ret = models.Book.objects.filter(Q(pk__gt=3)|Q(pk__lt=2))
print(ret)
# q条件的组合,逻辑判断
models.Book.objects.filter(~Q(Q(pk__gt=3)|Q(pk__lt=2))&Q(price__gt=50))

4. 事务

  • 原子性、完整性

from django.db import transaction
try:
  with transaction.atomic():
    # orm操作为事务操作
      models.Publiser.objects.create(name='xxx')
      int('sss')
      models.Publiser.objects.create(name='xxx2')
except Exception as e:
   print(e)

5. Django终端打印SQL语句

  • Django项目的settings.py文件中,在最后复制粘贴如下代码

  • 即为Django项目配置上一个名为django.db.backends的logger实例即可查看翻译后的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',
        },
    }
}

6.执行原生sql

from django.db import connection
cursor=connection.cursor()
# 插入操作
cursor.execute("insert into hello_author(name) values('钱钟书')")
# 更新操作
cursor.execute("update hello_author set name='abc' where name='bcd'")
# 删除操作
cursor.execute("delete from hello_author where name='abc'")
# 查询操作
cursor.execute("select * from hello_author")
raw=cursor.fetchone()  # 返回结果
cursor.fetchall()      # 读取所有

 

posted @ 2020-05-28 15:57  炜琴清  阅读(197)  评论(0编辑  收藏  举报