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