ORM

转载 FatPuffer 2019/9/7 23:17:11

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")
  1. 正向查询基于对象
book_obj = models.Book.objects.get(id=1)
book.publisher.name
  1. 正向查询基于双下划线
models.Book.objects.filter(id=1).values("publisher__name")
  1. 反向查询基于对象
publisher_obj = models.Publisher.objects.get(id=1)
# 没有配置related_name
publisher_obj.book_set.all()
# 配置related_name='book'
publisher_obj.book.all()
  1. 反向查询基于双下划线
publisher_obj = models.Publisher.objects.filter(id=1).values('book__name')
# 如果配置了related_query_name='books'
publisher_obj = models.Publisher.objects.filter(id=1).values('books__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

# 查询每本书的作者数量                                           related_name 值
# 将查询统计出来的结果保存为author_num字段,相当于对象多了一个属性
book_list = models.Book.objects.all().annotate(author_num=Count('authors'))
# 输出书名和作者数量
book_list = models.Book.objects.all().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))

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)

上一篇:Python 最常见的 170 道面试题解析:2019 最新

下一篇:python web框架中使用原生分页

赞(0)

共有 条评论 网友评论

验证码: 看不清楚?
    扫一扫关注最新编程教程