Django---Django的ORM的一对多操做(外键操做),ORM的多对多操做(关系管理对象),ORM的分组聚合,ORM的F字段查询和Q字段条件查询,Django的事务操做,额外(Django的终端打印SQL语句,脚本调试)
一丶Django的ORM外键操做
经过对象查找
### 正向查找 # 得到图书对象 book_obj=models.Book.objects.get(pk=1) ret=book_obj.pub #pub是Book表的外键字段,存在Book表中. 经过pub能够拿到所关联的对象 print(ret,type(ret)) # 1---沙河出版社 <class 'app02.models.Publisher'> ret2=book_obj.pub_id # pub_id是在Book表生成的字段. 获得的是一个具体字段值.仅此而已 print(ret2,type(ret2)) # 1 <class 'int'> #PS: 我的理解,经过多的一方的外键,主动去查找一的一方. 为正向查找, ### 反向查找 # 经过一的一方(没有设置外键的表),去查询被外键关联的多的一方的表. # 反查 默认是:类名的小写_set,能够设置外键字段的related_name属性,起别名, 也能够设置related_query_name属性设置别名. # related_query_name 只针对于字段查询约束, 对于对象查询不生效 # 获取 publisher对象 pub_obj=models.Publisher.objects.get(pk=1) ## 不指定 related_name ,在外键字段上不设置:related_name的'别名' , 反查默认是:类名的小写_set # 获得一个关系管理对象 ret=pub_obj.book_set print(ret,type(ret))#app02.Book.None,<class 'django.db.models...RelatedManager'> # 经过 关系管理对象, 反向查询出全部关联的对象 ret2=pub_obj.book_set.all() # 获得一个对象列表 QuerySet print(ret2,type(ret2)) # QuerySet , class 'django.db.models.query.QuerySet'> ## 指定 related_name='books' # 获得一个关系管理对象 ret=pub_obj.books # books 是设置related_name属性后的值 print(ret,type(ret))#app02.Book.None,<class 'django.db.models...RelatedManager'> # 经过 关系管理对象, 反向查询出全部关联的对象 ret2 = pub_obj.books.all() # 获得一个对象列表 QuerySet print(ret2, type(ret2)) # QuerySet , class 'django.db.models.query.QuerySet'>
经过字段查找
### 基于字段的查询,跨表查询使用 双下划线 __字段 ## 有外键字段,经过Book表的查询外键关联的对象的字段 ret=models.Book.objects.filter(pub__name='沙河出版社') # QuerySet ## 没有外键字段 .经过 Author表对象反查 Book图书表的某个字段. # 默认是: 类名小写__被查字段, ret=models.Publisher.objects.filter(book__title='新Linux') # QuerySet # 外键设置: related_name='books'时,使用 books__被查字段 ret=models.Publisher.objects.filter(books__title='新Linux') # QuerySet # 外键设置: related_query_name='booo'时,使用 booo__被查字段 ret=models.Publisher.objects.filter(books__title='新Linux') # QuerySet # PS: 设置了 : related_query_name='booo', related_query_name的优先级高于related_name
经过关系管理对象
### 外键 关系管理对象 publisher_obj=models.Publisher.objects.get(pk=1) print(publisher_obj.books,type(publisher_obj.books)) # app02.Book.None <class 'django.db.models...RelatedManager'> ### all() # 经过关系管理对象得到全部关联对象 print(publisher_obj.books.all()) ### set() 设置一对多关系 # 只能是 对象,或者对象列表 . 不能是ID publisher_obj.books.set(models.Book.objects.filter(pk__in=[2,3])) # 能够是对象列表 publisher_obj.books.set(models.Book.objects.filter(pk=2)) # 能够是一个对象 ### add() # 须要添加对象, ID不行 publisher_obj.books.add(*models.Book.objects.filter(pk__in=[2,3])) # 添加对象列表, 须要打散 publisher_obj.books.add(models.Book.objects.filter(pk=3).first()) # 添加一个对象 ### remove() 和 clear() # 因为表中的外键字段不容许为空, 因此 直接删除会报错. 须要更改数据库的外键字段能够为空 publisher_obj.books.remove(*models.Book.objects.filter(pk__in=[2,3])) publisher_obj.books.clear() ##PS :remove() 和 clear() 使用两个方法时,须要设置外键字段的容许为空 ,null=True时存在。 ### create() 建立一个关联对象, 默认是当前的pub_id 也能够设置 publisher_obj.books.create(title='XXXX',price='23',num=321,sale=213,pub_id=3)
二丶Django的ORM多对多
"关联管理器"是在一对多或者多对多的关联上下文中使用的管理器。
它存在于下面两种状况:html
1.外键关系的反向查询python
2.多对多关联关系git
# 从一的一方去拿多的一方的数据,老是先获得一个关系管理对象. 在经过关系管理对象去得到多的一方的数据
多对多查询
# 基于对象查询 author_obj=models.Author.objects.get(pk=1) # 正向 print(author_obj.name) print(author_obj.books) # 关系管理对象, print(author_obj.books.all()) #经过关系管理对象得到所关联的信息 # 反向 book_obj=models.Book.objects.get(pk=1) print(book_obj.title) print(book_obj.authors) # authors 起的别名. 多对多字段设置 # related_name='authors' print(book_obj.authors.all()) #### 基于字段查询 # 都是经过关系管理对象进行操做 # 反查 经过起的外键字段的别名 # 经过别名 __(双下划线)字段,即:authors__name='XXX' ret=models.Book.objects.filter(authors__name='alex') # 正向查 经过外键字段 # 经过多对多字段 books ___双下划线(字段),即:books__title='XX' ret=models.Author.objects.filter(books__title='金Python')
方法
## create() 建立一个所关联的对象,保存对象,创建多对多关系, 并将其与关联的对象添加到第三张表中 # 建立图书数据,并将其与做者关联,添加多对多关系. author_obj.books.create(title='新新心心念念',price='32',sale=70,num=30,pub_id=1) # 建立一个做者数据,并将其与图书关联,添加多对多关系 book_obj.authors.create(name='金山皮',) ## add() 把指定的model对象添加到关联对象 ,在原有的基础上增长 , 添加一条. 能够是 : ID , 对象 author_obj.books.add(4,5) # 添加多个对象的ID能够直接 逗号,分隔. author_obj.books.add(*[4,]) # 添加多个对象的ID ,多个添加不能是列表,使用列表必须被 * 打散 author_obj.books.add(models.Book.objects.get(pk=3),models.Book.objects.get(pk=1)) # 能够添加一个对象 逗号,分隔. author_obj.books.add(*models.Book.objects.all()) # QuerySet也是须要被打散 ## set() 更新model对象关联的对象,在第三个表清除当前对象的所关联对象,从新设置设置多对多关系.必须是列表 # 参数能够是一个列表(关联对象的id) # ID author_obj.books.set([1,4]) # 参数能够是对象列表, ,(最终转换成关联对象的id) author_obj.books.set(models.Book.objects.filter(pk__in=[2,3]))# 对象列表 ## PS: 多对多set使用set方法时,必须是一个对象列表, # 错误设置多对多关系的写法: 'Book' object is not iterable author_obj.books.set(models.Book.objects.filter(pk=1).first())# 对象列表 ## remove() 移除与关联对象信息 , 能够是 : ID , 对象 author_obj.books.remove(4,5) # 移除多个ID, 逗号分隔 author_obj.books.remove(models.Book.objects.get(pk=5),models.Book.objects.get(pk=4)) # 能够添加一个对象 逗号,分隔. author_obj.books.remove(*models.Book.objects.filter(pk__in=[1,2,3])) # 对象列表,须要打散 ## clear() 清除与关联对象一切信息 ,清空多对多关系 author_obj.books.clear() # 与当前这个做者的多对多关系所有删除
三丶聚合
'aggregate()'是'QuerySet' 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。数据库
# 导入内置函数 from django.db.models import Avg, Sum, Max, Min, Count # Count() 按照那个字段进行统计,获得是一个字典对象,必须给参数. # title_count 是起别名 ret=models.Book.objects.aggregate(title_count=Count('title')) print(ret['title_count']) # 字典取值 # Max() 求这个字段的最大值 ret=models.Book.objects.aggregate(book_max=Max('price')) print(ret) # Min() 求这个字段的最小值 ret=models.Book.objects.aggregate(book_min=Min('price')) print(ret) # Sum() 求这个字段的最小值 ret=models.Book.objects.aggregate(book_sum=Sum('price')) print(ret) # Avg() 求这个字段的最小值 ret=models.Book.objects.aggregate(book_avg=Avg('price')) print(ret) ## 联合使用多个聚合 ret=models.Book.objects.aggregate(book_avg=Avg('price'),book_max=Max('price')) print(ret)
四丶分组
与SQL的分组同理,对某一字段进行分组.
# SQL : # 按照部门Id分组 select dept_id,avg(salary) from employee group by dept_id # Django-ORM: # 按照 values() 中的字段进行分组,联合字段进行分组. from django.db.models import Avg models.Dept.objects.annotate(avg=Avg("employee__salary")).values("name", "avg")
orm分组
#### annotate() 统计每一本书的做者个数 # 默认分组 ret = models.Book.objects.annotate(author_count=Count('authors')) # annotate 注释 for el in ret: print(el.__dict__,type(el)) # el是对象 print(el.author_count) ## author_count 被封装到对象中,成为对象的一个字段 # values() 是查询出全部字段,以键值对的形式 ret = models.Book.objects.annotate(author_count=Count('authors')).values() # annotate 注释 for i in ret: print(i,type(i)) # values 是字典形式 print(i['author_count']) ### 统计出每一个出版社买的最便宜的书的价格 # 经过 做者表,正向查 ret=models.Publisher.objects.annotate(min=Min('booo__price')) for el in ret: print(el,el.min) # 经过 图书表 反向查, 按照出版社分组,取价格最小值 ret=models.Book.objects.values('pub__name').annotate(min=Min('price')) print(ret) #### 统计不止一个做者的图书 # 合适的方法 ret=models.Book.objects.annotate(author_num=Count('authors')).filter(author_num__gt=1) print(ret) # 左链接 # 不合适的方法 ret=models.Author.objects.values('books__title').annotate(author_num=Count('id')).filter(author_num__gt=1) # 右链接 print(ret) #### 根据一本图书做者数量的多少对查询集 QuerySet进行排序 # .reverse() 反转 # order_by 根据那个字段进行排序, 反向查询能够经过 authors 进行查询 ret=models.Book.objects.annotate(author_num=Count('authors')).order_by('author_num').reverse() for el in ret: print(el.author_num) #### 查询各个做者出的书的总价格 ret=models.Author.objects.annotate(sum_price=Sum('books__price')) for el in ret: print(el,el.sum_price) ret=models.Author.objects.annotate(sum_price=Sum('books__price')).values('name','sum_price') print(ret)
五丶F和Q查询
F查询
### 用于 字段之间的比较 # 查询 销售量大于库存量的图书 ret=models.Book.objects.filter(sale__gte=F('num')) for el in ret.values(): print(el) ### Django 支持F()对象之间 算数 # 更改库的某个字段的值,在原有值的基础上,进行数值操做,并保存 ret=models.Book.objects.filter(pk=1).update(sale=F('sale')*2+100) print(ret,type(ret)) ### 额外: # 修改char类型的字段 from django.db.models.functions import Concat from django.db.models import Value # 能够对querySet操做update方法 ret=models.Book.objects.filter(pk=1).update(title=Concat(F('title'),Value("("),Value("Chinese版"),Value(")"))) ret=models.Book.objects.filter(authors__name='二狗').update(title=Concat(F('title'),Value("("),Value("Chinese版"),Value(")"))) print(ret)
Q查询
### filter() 等方法中的关键字参数查询都是一块儿进行“AND” 的。 # 组合& 和|操做符以及使用括号进行分组来编写任意复杂的Q 对象。同时,Q 对象可使用~ 操做符取反,这容许组合正常的查询和取反(NOT) 查询。 #三种操做Q对象, 与& 或| 非~ ret=models.Book.objects.filter(Q(pk__gt=3)&Q(title__contains='a')) # & 与操做 ret=models.Book.objects.filter(Q(pk__gt=5)|Q(title__contains='a')) # | 或操做 ret=models.Book.objects.filter(Q(pk__gt=5)|~Q(title__contains='a')) # ~ 或操做 ### 查询函数能够混合使用Q 对象和关键字参数。全部提供给查询函数的参数(关键字参数或Q 对象)都将"AND”在一块儿。可是,若是出现Q 对象,它必须位于全部关键字参数的前面。 ret=models.Book.objects.filter(Q(price__range=[10,30])|Q(num__lte=50),title__icontains='a')
六丶Django的事务
from django.db import transaction from django.db.models import F import time ### 开启原子事务 # 事务具备原子性, 隔离行, 一致性 ,持久性ACID # 通常用于银行转帐 try: with transaction.atomic(): # 开启一个事务 models.Book.objects.filter(pk=1).update(sale=F('sale') + 50) time.sleep(3) exit(0) # 退出操做, 事务的回滚到原始状态. models.Book.objects.filter(pk=2).update(sale=F('sale') - 50) except Exception as e: print(e) ##PS: 异常处理必定要在开启事务外,在事务内开启异常处理会出现数据丢失现象
七丶预备数据和其余操做
提早预备model.py
from django.db import models # Create your models here. ## 自定义char类型 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指定的值 :param connection: :return: ''' return f'char({self.max_length})' class User(models.Model): pid = models.AutoField(primary_key=True) # 主键,自增 name = models.CharField(max_length=32, db_column='username', unique=True, verbose_name='姓名:', help_text='请输入姓名') age = models.IntegerField(null=True, blank=True) # blank表单校验约束为空 birth = models.DateTimeField(auto_now=True) # auto_now 修改就更新表, auto_now_add 只记录第一建立的时间 phone = MyCharField(max_length=11, null=True, blank=True) gender = models.BooleanField(default=True, choices=((True, '男'), (False, '女'))) def __str__(self): return f'{self.name}<---->{self.birth}' class Meta: # 在数据库中生成的表,默认app名称+下划线+类名 db_table='person' # 在admin中显示的表名称 verbose_name='我的信息' # verbose_name+s 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'{self.pk}---{self.name}' class Book(models.Model): title=models.CharField(max_length=32) price=models.DecimalField(max_digits=5,decimal_places=2) sale=models.IntegerField() num=models.IntegerField() hot_heat=MyCharField(max_length=32) pub=models.ForeignKey('Publisher',on_delete=models.CASCADE,related_name = 'books',related_query_name = 'booo') def __str__(self): return f'{self.pk}---{self.title}' class Author(models.Model): name=models.CharField(max_length=32) books=models.ManyToManyField('Book',related_name='authors') def __str__(self): return f'{self.name}'
如何Django终端打印SQL语句
在Django项目的settings.py文件中,配置以下:django
LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'console':{ 'level':'DEBUG', 'class':'logging.StreamHandler', }, }, 'loggers': { 'django.db.backends': { 'handlers': ['console'], 'propagate': True, 'level':'DEBUG', }, } } #即为你的Django项目配置上一个名为django.db.backends的logger实例便可查看翻译后的SQL语句。
在Python脚本中调用Django环境
import os if __name__ == '__main__': os.environ.setdefault("DJANGO_SETTINGS_MODULE", "BMS.settings") # 导入Django的配置文件 import django # 导入Django模块 django.setup() # 配置Django from app01 import models # 导入models文件 books = models.Book.objects.all() # 调试脚本代码 print(books) # 结果
https://www.cnblogs.com/maple-shaw/articles/9323320.html https://www.cnblogs.com/maple-shaw/articles/9403501.htmlapp