Django---Django的ORM的一對多操做(外鍵操做),ORM的多對多操做(關係管理對象),ORM的分組聚合,ORM的F字段查詢和Q字段條件查詢,Django的事務操做,額外(Djang...

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

相關文章
相關標籤/搜索