[TOC]
基于双下划线的连表查询
1from django.db import models 2 3# Create your models here. 4 5class Author(models.Model): 6 """ 7 作者表 8 """ 9 name=models.CharField( max_length=32) 10 age=models.IntegerField() 11 # authorDetail=models.OneToOneField(to="AuthorDetail",to_field="nid",on_delete=models.CASCADE) # 12 auth=models.OneToOneField("AuthorDetail",on_delete=models.CASCADE) 13 14class AuthorDetail(models.Model): 15 """ 16 作者详细信息表 17 """ 18 birthday=models.DateField() 19 telephone=models.CharField(max_length=11) 20 addr=models.CharField(max_length=64) 21 # class Meta: 22 # db_table='authordetail' #指定表名 23 # ordering = ['-id',] 24class Publish(models.Model): 25 """ 26 出版社表 27 """ 28 name=models.CharField( max_length=32) 29 city=models.CharField( max_length=32) 30 31class Book(models.Model): 32 """ 33 书籍表 34 """ 35 title = models.CharField( max_length=32) 36 publishDate=models.DateField() 37 price=models.DecimalField(max_digits=5,decimal_places=2) 38 pub=models.ForeignKey(to="Publish",on_delete=models.CASCADE,) 39 authors=models.ManyToManyField('Author',)
一对一:
1# 查询alex的电话号码 2 3正向:依靠属性 4ret = models.Author.objects.filter(name='alex').values('auth__telephone') 5# <QuerySet [{'auth__telephone': '110'}]> 6 7反向:依靠类名(表名)小写 8ret = models.AuthorDetail.objects.filter(author__name='alex').values('telephone') 9# <QuerySet [{'telephone': '110'}]> 10 11print(ret)
多对一:
1# 查询一下 xxx 这本书的出版社 2 3# 正向:依靠属性 4ret=models.Book.objects.filter(title='xxx').values('pub__name') 5# <QuerySet [{'pub__name': '人民出版社'}]> 6 7# 反向:依靠类名(表名)小写 8ret=models.Publish.objects.filter(book__title='xxx').values('name') 9# <QuerySet [{'name': '人民出版社'}]> 10 11 12# 查询 24期出版社 出版了那些书 13# 反向 14ret=models.Publish.objects.filter(name='24期出版社').values('book__title') 15 16# <QuerySet [{'book__title': 'alex与meet的爱恨'}, {'book__title': '那年的24期'}]> 17 18# 正向 19ret=models.Book.objects.filter(pub__name='24期出版社').values('title') 20 21# <QuerySet [{'title': 'alex与meet的爱恨'}, {'title': '那年的24期'}]>
多对多:
1# xxx 这本书 是有哪些作者写的 2 3# 正向查询:属性 4ret=models.Book.objects.filter(title='xxx').values('authors__name') 5# <QuerySet [{'authors__name': 'alex'}, {'authors__name': 'meet'}, {'authors__name': 'liu'}]> 6 7""" 8原生sql语句: 9select app01_author.name from app01_book INNER JOIN app01_book_authors on app01_book.id = app01_book_authors.book_id 10 INNER JOIN app01_author on app01_book_authors.author_id = app01_author.id 11 where app01_book.title='xxx; 12""" 13 14# 反向查询:类名(表名) 15ret=models.Author.objects.filter(book__title='xxx').values('name') 16 17# <QuerySet [{'name': 'alex'}, {'name': 'meet'}, {'name': 'liu'}]>
related_name
反向查询,替代 表名小写 或者 表名_set 。必须替代,否则会报错。
1pub = ForeignKey(Publish, related_name='bookList') 2 3ret=models.Book.objects.filter(title='xxx').values('bookList__name')
聚合查询 aggregate
1# 统计书的平均价格以及最高价格 2 3from django.db.models import Avg, Max 4ret = models.Book.objects.all().aggregate(Avg('price'),Max('price')) # 结果是python字典, 也就是说聚合查询是orm的结束语句,一般放在最后. 5# {'price__avg': 137.833333, 'price__max': Decimal('222.00')} 6 7ret = models.Book.objects.all().aggregate(a=Avg('price'),m=Max('price')) 8# 给键取名字 9{'a': 137.833333, 'm': Decimal('222.00')}
分组查询 annotate
必须起别名。
1# 每个出版社的书的平均价格 2 3models.Book.objects.values('pub__id').annotate(a=Avg('price')) 4 5# 按属性pub分组(按publish表的id分组) 6# <QuerySet [{'pub__id': 1, 'a': 186.0}, {'pub__id': 2, 'a': 100.0}, {'pub__id': 3, 'a': 5.0}, {'pub__id': 4, 'a': 175.0}]> 7 8 9models.Book.objects.values('pub_id').annotate(a=Avg('price')) 10 11# 按book表的pub_id字段分组 12# <QuerySet [{'pub_id': 1, 'a': 186.0}, {'pub_id': 2, 'a': 100.0}, {'pub_id': 3, 'a': 5.0}, {'pub_id': 4, 'a': 175.0}]> 13 14 15models.Publish.objects.annotate(a=Aug('book__price')).values('a') 16# 连表后拿Publish的id进行分组,a是别名 17<QuerySet [{'a': 186.0}, {'a': 100.0}, {'a': 5.0}, {'a': 175.0}]> 18
F查询--锁定本表字段
本表字段中,要对两个字段的值做比较,F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值。
1from django.db.models import F 2 3ret = models.Book.objects.filter(字段1__gt=F('字段2')) 4# 查询book表中 字段1 大于 字段2的记录 5 6django支持 F() 对象之间以及 F() 对象和常数之间的加减乘除和取模的操作。
修改操作也可以使用F函数。
1# 将所有书籍价格提高10 2 3models.Book.objects.all().update( 4 price=F('price')+10 5) 6
Q查询 -- 或 | 、与 & 、非 ~
注意:, 是 AND查询
1from django.db.models import Q 2 3# 查询书籍价格大于200或者价格小于100 4models.Book.objects.filter(Q(price__gt=200)|Q(price__lt=100)) 5
可以多层嵌套
1Q(Q(price__gt=200)|Q(price__lt=100))|Q(...) 2
与关键字参数一起使用,Q 对象必须位于所有关键字参数的前面。
1查询书籍价格大于200或者价格小于100并且id=2 2models.Book.objects.filter(Q(price__gt=200)|Q(price__lt=100),id=2) 3
作业:
1 查询每个作者的姓名以及出版的书的最高价格
1ret=models.Author.objects.annotate(max=Max('book__price')).values('name', 'max') 2 3print(ret) 4
2 查询作者id大于2作者的姓名以及出版的书的最高价格
1ret = models.Author.objects.filter(id__gt=2).annotate(max=Max('book__price')).values('name', 'max') 2
3 查询作者id大于2或者作者年龄大于等于20岁的作者的姓名以及出版的书的最高价格
1ret = models.Author.objects.filter(Q(id__gt=2)|Q(age__gte=20)).annotate(max=Max('book__price')).values('name', 'max') 2
4 查询每个作者出版的书的最高价格 的平均值
1ret = models.Author.objects.annotate(max=Max('book__price')).aggregate(Avg('max')) 2
5 每个作者出版的所有书的最高价格以及最高价格的那本书的名称
1select title,price from (select app01_author.id,app01_book.title,app01_book.price from app01_author INNER JOIN app01_book_authors on app01_author.id= 2 app01_book_authors.author_id INNER JOIN app01_book on app01_book.id= 3 app01_book_authors.book_id ORDER BY app01_book.price desc) as b GROUP BY id; 4
ORM执行原生sql语句
在模型查询API(接口)不够用的情况下,我们还可以使用原始的SQL语句进行查询。
Django 提供两种方法使用原始SQL进行查询:一种是使用raw()方法,进行原始SQL查询并返回模型实例;另一种是完全避开模型层,直接执行自定义的SQL语句。
执行原生查询
raw()管理器方法用于原始的SQL查询,并返回模型的实例:
raw()语法查询必须包含主键。
返回一个RawQuerySet对象
models.Book.objects.raw('select price from app01_book where id=2')
直接执行自定义SQL
1from django.db import connection, connections 2cursor = connection.cursor() # cursor = connections['default'].cursor() 3cursor.execute('SELECT * from auth_user where id = %s', [1]) 4ret = cursor.fetchone()
Python脚本中调用Django环境(django外部脚本使用models)
如果你想通过自己创建的python文件在django项目中使用django的models,那么就需要调用django的环境:

1import os 2 3if __name__ == '__main__': 4 os.environ.setdefault("DJANGO_SETTINGS_MODULE", "BMS.settings") 5 import django 6 django.setup() 7 8 from app01 import models #引入也要写在上面三句之后 9 10 books = models.Book.objects.all() 11 print(books)
事务与锁
Django orm中的锁
1models.Book.objects.select_for_update().filter(id=1) # 给id=1的行级加锁 2
事务开启
1. 全局开启
整个视图函数中,统一个http请求对应的所有sql都放在一个事务中执行(要么所有都成功,要么所有都失败)。
1DATABASES = { 2 'default': { 3 'ENGINE': 'django.db.backends.mysql', 4 'NAME': 'orm', 5 'HOST': '127.0.0.1', 6 'PORT': '3306', 7 'USER': 'root', 8 'PASSWORD': '123', 9 "ATOMIC_REQUESTS": True, #全局开启事务,绑定的是http请求响应整个过程 10 } 11
2. 局部开启
atomic(using=None, savepoint=True)[source]
原子性是数据库事务的一个属性。使用atomic,我们就可以创建一个具备原子性的代码块。一旦代码块正常运行完毕,所有的修改会被提交到数据库。反之,如果有异常,更改会被回滚。
被atomic管理起来的代码块还可以内嵌到方法中。这样的话,即便内部代码块正常运行,如果外部代码块抛出异常的话,它也没有办法把它的修改提交到数据库中。
1方式1:给函数做装饰器来使用 2from django.db import transaction 3 4@transaction.atomic 5def viewfunc(request): 6 # This code executes inside a transaction. 7 do_stuff() 8 9 10方式2: 作为上下文管理器来使用,其实就是设置事务的保存点 11(给某一视图函数中部分sql语句加锁) 12 13def viewfunc(request): 14 15 do_stuff() 16 with transaction.atomic(): # 设置保存点 17 pass