1. 程式人生 > 其它 >Django聚合、分組查詢、ORM查詢優化

Django聚合、分組查詢、ORM查詢優化

內容概要

  • 聚合查詢
  • 分組查詢
  • F與Q查詢
  • ORM查詢優化
  • ORM欄位型別及引數
  • ORM事務操作

聚合查詢

MySQL聚合函式:max\min\sum\count\avg
    
from django.db.models import Max, Min, Sum, Avg, Count
res = models.Book.objects.aggregate(Max('price'))
print(res)  # {'price__max': Decimal('13330.11')}
'''沒有分組也可以使用聚合函式 預設整體就是一組'''
res = models.Book.objects.aggregate(Max('price'),
                                        Min('price'),
                                        Sum('price'),
                                        Avg('price'),
                                        Count('pk')
                                        )
print(res)
# {'price__max': Decimal('13330.11'), 'price__min': Decimal('6666.66'), 'price__sum': Decimal('29996.76'), 'price__avg': 9998.92, 'pk__count': 3}

分組查詢

MySQL分組操作:group by
"""ORM執行分組操作 如果報錯 可能需要去修改sql_mode 移除only_full_group_by"""
ORM操作分組關鍵字:annotate

from django.db.models import Max, Min, Sum, Avg, Count
# 統計每本書的作者個數
res = models.Book.objects.annotate(author_num=Count('authors__pk')).values('title', 'author_num')

# 統計每個出版社買的最便宜的書的名稱和價格
res = models.Publish.objects.annotate(Min_price=Min('book__price')).values(
    'name',
    'book__title',
    'Min_price'
)
print(res)

# 統計不止一個作者的圖書
res = models.Book.objects.annotate(
    author_num=Count('authors__pk')
	).filter(author_num__gt=1).values('title','author_num')

# 統計每個作者出的書的總價格
res = models.Author.objects.annotate(book_sum_price=Sum('book__price')).values(
    'name',
    'book_sum_price'
)

"""上述操作都是以表為單位做分組 如果想要以表中的某個欄位分組如何操作"""
 # values('age')在分組前 類似於SQL的where 在分組之後 having
models.Author.objects.values('age').annotate() 
# 統計每個出版社主鍵值對應的書籍個數
res = models.Book.objects.values('publish_id').annotate(
        book_num=Count('pk')
		).values('publish_id','book_num')
print(res)

F與Q查詢

"""
當表中已經有資料的情況下 新增額外的欄位 需要指定預設值或者可以為null
	方式1
		IntegerField(verbose_name='銷量',default=1000)
	方式2
		IntegerField(verbose_name='銷量',null=True)
	方式3
		在遷移命令提示中直接給預設值
"""
# 查詢庫存大於銷量的書籍
# res = models.Book.objects.filter(kucun > maichu)    不行
# res = models.Book.objects.filter(kucun__getmaichu)  不行# 當查詢條件的左右兩表的資料都需要

F查詢

表中的資料 可以使用F查詢
from django.db.models import F
res = models.Book.objects.filter(kucun__gt=F('maichu'))

# 將所有書的價格提升1000塊
res = models.Book.objects.update(price=F('price') + 1000)

# 將所有書的名稱後面加上_爆款字尾
res = models.Book.objects.update(title=F('title') + '_爆款')  # 不可以
'''如果要修改char欄位咋辦(千萬不能用上面對數值型別的操作!!!) 需要使用下列兩個方法'''
from django.db.models.functions import Concat
from django.db.models import Value
res = models.Book.objects.update(name=Concat(F('title'), Value('爆款')))

Q查詢

# 查詢價格大於20000或者賣出大於1000的書籍
    # res = models.Book.objects.filter(price__gt=20000,maichu__gt=1000)
    # print(res)
    # print(res.query)
    '''filter括號內多個條件預設是and關係 無法直接修改'''
    
    from django.db.models import Q
    '''使用Q物件 就可以支援邏輯運算子'''
    # res = models.Book.objects.filter(Q(price__gt=20000), Q(maichu__gt=1000))  # 逗號是and關係
    # res = models.Book.objects.filter(Q(price__gt=20000) | Q(maichu__gt=1000))  # 管道符是or關係
    # res = models.Book.objects.filter(~Q(price__gt=20000))  # ~是not操作
    # print(res.query)
    '''
    Q物件進階用法
        filter(price=100)
        filter('price'=100)
    當我們需要編寫一個搜尋功能 並且條件是由使用者指定 這個時候左邊的資料就是一個字串
    '''
    q_obj = Q()
    q_obj.connector = 'or'  # 預設是and 可以改為or
    q_obj.children.append(('price__gt',20000))
    q_obj.children.append(('maichu__gt',1000))
    res = models.Book.objects.filter(q_obj)
    print(res.query)

ORM常見欄位

# ORM                                  			  MySQL
AutoField()         	  自增     			int auto_increment
CharField() 	   		字元型別			  varchar
'必須提供max_length引數'   
IntergerField()			  整型				int
BigIntergerField()		 長整型			   bigint
DecimalField()			10進位制小數			  decimal
DateField()				日期格式			  date
DateTimeField()			日期+時間格式			datetime
BooleanField()  存0和1	布林值				 boolean
TextField()		儲存大段文字
FileField()		傳檔案自動儲存到指定位置並存檔案路徑
EmailField()	本質還是varchar型別

# 自定義欄位型別
class MyCharField(models.Field):
    def __init__(self, max_length, *args, **kwargs):
        self.max_length = max_length
        super().__init__(max_length=max_length, *args, **kwargs)

    def db_type(self, connection):
        return 'char(%s)' % self.max_length

ORM重要引數

primary_key					主鍵
max_length					字元長度
verbose_name				說明
null						用於表示某個欄位可以為空
default						為該欄位設定預設值。
max_digits					總位數(不包括小數點和符號)
decimal_places				小數位數
unique						如果設定為unique=True 則該欄位在此表中必須是唯一的 。
db_index					如果db_index=True 則代表著為此欄位設定索引。
auto_now				配置上auto_now=True,每次更新資料記錄的時候會更新該欄位。
auto_now_add			配置auto_now_add=True,建立資料記錄的時候會把當前時間新增到資料庫。
choices					用於可以被列舉完全的資料,用來給欄位提供選擇項。
to							設定要關聯的表。
to_field					設定要關聯的欄位。
db_constraint				是否在資料庫中建立外來鍵約束,預設為True。

ps:外來鍵欄位中可能還會遇到related_name引數
"""
外來鍵欄位中使用related_name引數可以修改正向查詢的欄位名
"""

chioces使用

	用於可以被列舉完全的資料
  	eg:性別 學歷 工作經驗 工作狀態
      	class User(models.Model):
          username = models.CharField(max_length=32)
          password = models.IntegerField()
          gender_choice = (
              (1,'男性'),
              (2,'女性'),
              (3,'變性')
          )
          gender = models.IntegerField(choices=gender_choice)
				user_obj.get_gender_display()  
        # 有對應關係就拿 沒有還是本身

事務操作

MySQL事務:四大特性(ACID)
  	原子性
    一致性
    獨立性
    永續性
    start transcation;    # 開啟事務
    rollback;			 # 事務回滾
    commit;				 # 事務提交
    
ORM事務操作
# 事務會自動提交
from django.db import transaction
    try:
        with transaction.atomic():
            pass
    except Exception:
        pass

ORM執行原生SQL

# 方式1
from django.db import connection, connections
cursor = connection.cursor()  
cursor = connections['default'].cursor()
cursor.execute("""SELECT * from auth_user where id = %s""", [1])
cursor.fetchone()

# 方式2
models.UserInfo.objects.extra(
                    select={'newid':'select count(1) from app01_usertype where id>%s'},
                    select_params=[1,],
                    where = ['age>%s'],
                    params=[18,],
                    order_by=['-age'],
                    tables=['app01_usertype']
                )

多對多三種建立方式

# 全自動(常見)
	orm自動建立第三張表 但是無法擴充套件第三張表的欄位
	authors = models.ManyToManyField(to='Author')
    
# 全手動(使用頻率最低)
	優勢在於第三張表完全自定義擴充套件性高 劣勢在於無法使用外來鍵方法和正反向
  class Book(models.Model):
  		title = models.CharField(max_length=32)
  class Author(models.Model):
    	name = models.CharField(max_length=32)
  class Book2Author(models.Model):
    	book_id = models.ForeignKey(to='Book')
    	author_id = models.ForeignKey(to='Author')
        
# 半自動(常見)
	正反向還可以使用 並且第三張表可以擴充套件 
    唯一的缺陷是不能用: add\set\remove\clear四個方法
  
  class Book(models.Model):
    	title = models.CharField(max_length=32)
    	authors = models.ManyToManyField(
      					to='Author',
    					through='Book2Author',  # 指定表
      					through_fields=('book','author')  # 指定欄位
    )
  class Author(models.Model):
    	name = models.CharField(max_length=32)
    '''多對多建在任意一方都可以 如果建在作者表 欄位順序互換即可'''
    	books = models.ManyToManyField(
      					to='Author',
    					through='Book2Author',  # 指定表
      					through_fields=('author','book')  # 指定欄位
    )
  class Book2Author(models.Model):
    	book = models.ForeignKey(to='Book')
    	author = models.ForeignKey(to='Author')