ForeignKey操作:書籍表(Book表)外鍵關聯出版社表(Publisher表) 正向查找: 反向查找: ManyToManyField:(使用方式二:通過ManyToManyField自動創建第三張表) models: class RelatedManager:"關聯管理器"是在一對多或者多 ...
12.329 orm單表查詢
import os if __name__ == '__main__': # 指定當前py腳本需要載入的Django項目配置信息 os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm_demo.settings") import django django.setup() # 啟動Django項目 from app01 import models #返回QuerySet對象的方法: ret = models.Book.objects.all() print(ret) # QuerySet類型:書籍對象的列表 ret = models.Book.objects.filter(title="圍城") # QuerySet類型 --> 書籍對象的列表 # id值大於1 ret = models.Book.objects.filter(id__gt=1) # id值小於3 ret = models.Book.objects.filter(id__lt=3) # 出版日期是2017年的書 ret = models.Book.objects.filter(publisher_date__year=2017) # 出版日期大於2017年 ret = models.Book.objects.filter(publisher_date__year__gt=2017) # 書名中包含'曌'的書 ret = models.Book.objects.filter(title__contains="曌") # 書名中包含'曌'的書並且出版年份是2018年 ret = models.Book.objects.filter(title__contains="曌", publisher_date__year=2018) # get方法如果符合篩選條件的對象超過一個或者沒有都會拋出錯誤。 #符合篩選條件的對象只有一個,則返回具體的類對象實例:書籍對象,而不是列表 ret = models.Book.objects.get(id=10) print(ret) #報錯 # 使用filter檢索的時候沒有滿足條件的數據就返回一個空 QuerySet ret = models.Book.objects.filter(id=10) print(ret) #[] # 將滿足條件的去掉,留下不滿足條件的 ret = models.Book.objects.exclude(id__in=[1,3,4]) print(ret) # 按欄位排序 ret = models.Book.objects.all().order_by("price") # QuerySet類型:根據price欄位對所有數據排序 ret = models.Book.objects.all().order_by("-price") #反轉 ret = models.Book.objects.all().order_by("price").reverse() # 按照出版時間排序後反轉再去欄位值 # QuerySet類型:欄位及欄位值的字典的列表 ret = models.Book.objects.all().order_by("publisher_date").reverse().values("title") #特殊的QuerySet: #values() 取欄位的值,以字典返回 ret = models.Book.objects.filter(publisher_date__year=2018).values("title", "publisher_date") print(ret) # QuerySet類型:欄位及欄位值的字典的列表 #values_list() 取欄位的值,以元組返回 ret = models.Book.objects.filter(publisher_date__year=2018).values_list("title", "publisher_date") # QuerySet類型:欄位值的元祖的列表 # 連表查詢 ret = models.Book.objects.all().values("publisher__name").distinct() print(ret) # QuerySet類型:欄位及欄位值的字典的列表,並去重 #返回數字的方法 # count 計數 ret = models.Book.objects.all().count() # 數字:結果集中數據的個數 #返回具體對象 #first()和last() ret = models.Book.objects.all().first() #結果集中的第一個對象 #get() ret = models.Book.objects.get(id=1) #返回匹配到的對象,有且僅有一個 #返回布爾值的方法 # 判斷結果集中是否有數據 ret = models.Book.objects.all().exists() # 布爾值:結果集中是否有數據
12.3210 orm多表查詢(方式二)
ForeignKey操作:書籍表(Book表)外鍵關聯出版社表(Publisher表)
正向查找:
1.基於對象(子查詢) book_obj = models.Book.objects.first() # 第一本書對象 print(book_obj.publisher) # 得到這本書關聯的出版社對象 print(book_obj.publisher.name) # 得到出版社對象的名稱 2.基於雙下劃線欄位方法(聯表查詢) print(models.Book.objects.values_list("publisher__name"))
反向查找:
1.基於對象(子查詢) publisher_obj = models.Publisher.objects.first() # 找到第一個出版社對象 books = publisher_obj.book_set.all() # 找到第一個出版社出版的所有書籍對象 titles = books.values_list("title") # 找到第一個出版社出版的所有書籍的書名 #如果設置了 related_name="books" #publisher= models.ForeignKey(to="Publisher", related_name="books") publisher_obj = models.Publisher.objects.first() ret = publisher_obj.books.all() print(ret)#<QuerySet [<Book: 西瓜物語>, <Book: 香蕉物語>]> 2.基於雙下劃線的欄位方法(聯表查詢) ret = models.Publisher.objects.filter(id=1).values_list("book__title") print(ret)#<QuerySet [('西瓜物語',), ('香蕉物語',)]> titles = models.Publisher.objects.values_list("book__title") print(titles)#<QuerySet [('西瓜物語',), ('香蕉物語',), ('番茄物語',), (None,)]> #如果related_query_name="books"或者 related_name="books"(如果兩個同時出現,則以related_query_name為準) titles = models.Publisher.objects.filter(id=1).values_list("books__title") #<QuerySet [('西瓜物語',), ('香蕉物語',)]> titles = models.Publisher.objects.filter(id=1).values("books__title") #<QuerySet [{'books__title': '西瓜物語'}, {'books__title': '香蕉物語'}]>
ManyToManyField:(使用方式二:通過ManyToManyField自動創建第三張表)
models:
class Book(models.Model): title = models.CharField(max_length=32) publish_date = models.DateField(auto_now_add=True) price = models.DecimalField(max_digits=5, decimal_places=2) memo = models.TextField(null=True) # 創建外鍵,關聯publish publisher = models.ForeignKey(to="Publisher", ) # 創建多對多關聯author author = models.ManyToManyField(to="Author") class Author(models.Model): name = models.CharField(max_length=32) age = models.IntegerField() phone = models.CharField(max_length=11) detail = models.OneToOneField(to="AuthorDetail")
class RelatedManager:"關聯管理器"是在一對多或者多對多的關聯上下文中使用的管理器。
它存在於下麵兩種情況:外鍵關係的反向查詢、多對多關聯關係,簡單來說就是當 "."後面的對象 可能存在多個的時候就可以使用以下的方法
create():創建一個新的對象,保存對象,並將它添加到關聯對象集之中,返回新創建的對象。
#models.Book.objects.first().author得到是一個class RelatedManager對象,使用.create操作author表和第三張表 1.正向創建author表數據 ret = models.Book.objects.first().author.create(name="張三",age=16,phone="18012xxxx",detail_id=4) #做了兩件事情:1. 創建了一個新的作者,2. 將新創建的作者和第一本書做關聯 ret = models.Book.objects.first().author.all().values("id") print(ret)#<QuerySet [{'id': 1}, {'id': 3}, {'id': 15}]> 2.反向創建book表數據 import datetime models.Author.objects.first().book_set.create(title="番茄物語", publish_date=datetime.date.today())
add():把指定的model對象或對象id添加到關聯對象集中
#添加對象 author_objs = models.Author.objects.filter(id__lt=3) models.Book.objects.first().author.add(*author_objs) #添加id models.Book.objects.first().author.add(*[1, 2]) models.Book.objects.first().author.add(1) #第一本書關聯的作者id ret = models.Book.objects.first().author.all().values("id") print(ret)#<QuerySet [{'id': 1}, {'id': 3}, {'id': 15}]>
set():更新model對象的關聯對象。
ret=models.Book.objects.first().author.set([2, 3])#設置第三張表的關聯關係,給第一個book對象加上id=2和3 ret = models.Book.objects.first().author.all() print(ret)#<QuerySet [<Author: 小仙女>, <Author: 大烏龜>, <Author: 張曌>]> ret = models.Book.objects.first().author.all().values("id") print(ret)#<QuerySet [{'id': 1}, {'id': 3}, {'id': 15}]>
remove():從關聯對象集中移除執行的model對象
models.Book.objects.first().author.remove(3)#找到第一個圖書對象所對應的所有作者,到第三張表中刪除它與id=3的作者的關聯關係#<QuerySet [{'id': 1}, {'id': 15},{'id'=3}]> ret = models.Book.objects.first().author.all().values("id") print(ret) #<QuerySet [{'id': 1}, {'id': 15}]>
clear():從關聯對象集中移除一切對象。
#<QuerySet [{'id': 1}, {'id': 15}]> models.Book.objects.first().author.clear() ret = models.Book.objects.first().author.all().values("id") print(ret)#<QuerySet []>
註意:對於ForeignKey對象,clear()和remove()方法僅在null=True時存在
all():
ret = models.Book.objects.first().author.all()#得到第一本書對象對應的作者對象集 print(ret) #<QuerySet [Author object,Author object,Author object]>
12.3211 聚合查詢
aggregate()是QuerySet 的一個終止子句,它返回一個包含一些鍵值對的字典。
鍵的名稱是聚合值的標識符,值是計算出來的聚合值。鍵的名稱是按照欄位和聚合函數的名稱自動生成出來的。
from django.db.models import Avg, Sum, Max, Min, Count models.Book.objects.all().aggregate(Avg("price")) #{'price__avg': 13.233333} ret = models.Book.objects.aggregate(Sum("price")) #{'price__sum': Decimal('13.10') ret = models.Book.objects.aggregate(total_price=Sum("price")) #{'total_price': Decimal('13.10')} ret = models.Book.objects.aggregate(avg_price=Avg("price"), max_price=Max("price"), min_price=Min("price")) #{'avg_price': 4.366667, 'max_price': Decimal('12.00'), 'min_price': Decimal('0.10')}
12.3212 分組查詢
單表查詢分組:按照部門分組求平均工資
select dept,AVG(salary) from employee group by dept;
orm查詢:
from django.db.models import Avg models.Employee.objects.values("dept").annotate(avg=Avg("salary") #<QuerySet [{'dept': '教學部', 'avg': 221.0}, {'dept': '銷售部', 'avg': 21.0}, {'dept': '人事部', 'avg': 999.0}]> models.Employee.objects.values("dept").annotate(avg=Avg("salary").values('dept', "avg") #<QuerySet [{'dept': '教學部', 'avg': 221.0}, {'dept': '銷售部', 'avg': 21.0}, {'dept': '人事部', 'avg': 999.0}]>
連表查詢的分組:按照部門分組求平均工資
select dept.name,AVG(salary) from employee inner join dept on (employee.dept_id=dept.id) group by dept_id;
ORM查詢:
from django.db.models import Avg models.Dept.objects.annotate(avg=Avg("employee__salary")).values("name", "avg") models.Employee.objects.values("dept__name").annotate(avg=Avg("salary")) #<QuerySet [{'dept__name': '垃圾部', 'avg': 221.0}, {'dept__name': '保全部', 'avg': 21.0}, {'dept__name': '教學部', 'avg': 999.0}]> models.Employee.objects.values("dept__name").annotate(avg=Avg("salary")).values('dept', "avg") #<QuerySet [{'dept__name': '垃圾部', 'avg': 221.0}, {'dept__name': '保全部', 'avg': 21.0}, {'dept__name': '教學部', 'avg': 999.0}]> models.Employee.objects.values("dept__name") #<QuerySet [{'dept__name': '垃圾部'}, {'dept__name': '保全部'}, {'dept__name': '教學部'}]>
作者、圖書、出版社表關係:
from django.db import models # 出版社 class Publisher(models.Model): name = models.CharField(max_length=32) city = models.CharField(max_length=32) # 書 class Book(models.Model): title = models.CharField(max_length=32) publish_date = models.DateField(auto_now_add=True) price = models.DecimalField(max_digits=5, decimal_places=2) # 創建外鍵,關聯publish publisher = models.ForeignKey(to="Publisher") # 創建多對多關聯author author = models.ManyToManyField(to="Author") # 作者 class Author(models.Model): name = models.CharField(max_length=32)
示例:
1.統計每一本書的作者個數 (1):ret = models.Book.objects.annotate(autor_num=Count("author")).values("title", "autor_num") #<QuerySet [{'title': '西瓜物語', 'autor_num': 0}, {'title': '香蕉物語', 'autor_num': 1}, {'title': '番茄物語', 'autor_num': 1}]> (2):book_list = models.Book.objects.all().annotate(author_num=Count("author")) print(book_list) #<QuerySet [<Book: 西瓜物語>, <Book: 香蕉物語>, <Book: 番茄物語>]> for obj in book_list: print(obj.author_num)#0 1 1 2.統計出每個出版社出版的最便宜的書的價格 (1):ret = models.Publisher.objects.annotate(min_price=Min("book__price")).values("name", "min_price") #<QuerySet [{'name': '清華出版社', 'min_price': Decimal('0.10')}, {'name': '香江出版社', 'min_price': Decimal('12.00')}, {'name': '北大青鳥出版社', 'min_price': None}]> (2):ret=models.Book.objects.values("publisher__name").annotate(min_price=Min("price")) #<QuerySet [{'publisher__name': '清華出版社', 'min_price': Decimal('0.10')}, #{'publisher__name': '香江出版社', 'min_price': Decimal('12.00')}]> (3):publisher_list = models.Publisher.objects.annotate(min_price=Min("book__price")) print(publisher_list) #<QuerySet [<Publisher: 清華出版社>, <Publisher: 香江出版社>, <Publisher: 北大青鳥出版社>]> for obj in publisher_list: print(obj.min_price)#0.10 12.00 None 3.統計不止一個作者的圖書 models.Book.objects.annotate(author_num=Count("author")).filter(author_num__gt=1)#<QuerySet []> 4.根據一本圖書作者數量的多少對查詢集 QuerySet進行排序 models.Book.objects.annotate(author_num=Count("author")).order_by("author_num") #<QuerySet [<Book: 西瓜物語>, <Book: 香蕉物語>, <Book: 番茄物語>]> 5.查詢各個作者出的書的總價格 models.Author.objects.annotate(sum_price=Sum("book__price")).values("name", "sum_price") #<QuerySet [{'name': '小仙女', 'sum_price': Decimal('1.00')}, {'name': '小魔女', 'sum_price': Decimal('12.00')}, #{'name': '大烏龜', 'sum_price': None}, {'name': '張san', 'sum_price': None}]>
12.3213 F查詢
F() 的實例可以在查詢中引用欄位,來比較同一個 model 實例中兩個不同欄位的值。
商品表結構:
from django.db import models class Product(models.Model): name = models.CharField(max_length=32) price = models.DecimalField(max_digits=6, decimal_places=2) # 庫存數 keep = models.IntegerField() # 賣出數 sale = models.IntegerField() def __str__(self): return "{}:{}:{}:{}".format(self.name, self.price, self.keep, self.sale)
示例:
from django.db.models import F 1.查詢出賣出數大於庫存數的商品 models.Product.objects.filter(sale__gt=F("keep")) #<QuerySet [<Product: 跟哪吒學詩歌:59.00:50:10000>, <Product: 跟苑局學三不:55.00:100:200>]> 2.Django支持F()對象之間以及F()對象和常數之間的加減乘除和取模的操作 models.Product.objects.filter(sale__gt=F('keep')*2) #<QuerySet [<Product: 跟哪吒學詩歌:59.00:50:10000>]> 3.修改操作也可以使用F函數:比如將每個產品的價格提高50元 models.Product.objects.all().update(price=F("price")+50) 4.把所有商品名後面加上"新款" from django.db.models.functions import Concat from django.db.models import Value models.Product.objects.all().update(name=Concat(F("name"), Value("新款")))
12.3214 Q查詢
filter() 等方法中的關鍵字參數查詢都是一起進行“AND” 的。 如果需要執行更複雜的查詢(例如OR語句),可以使用Q對象
#賣出數大於100 並且 價格大於100塊的 models.Product.objects.filter(sale__gt=100, price__gt=100) #<QuerySet [<Product: 跟哪吒學詩歌新款:259.00:50:10000>, <Product: 跟苑局學三不新款:255.00:100:200>]>
示例:
from django.db.models import Q 1.查詢賣出數大於100或者價格小於100的 models.Product.objects.filter(Q(sale__gt=100)|Q(price__lt=100)) #|:或 #<QuerySet [<Product: 跟哪吒學詩歌新款:259.00:50:10000>, <Product: 跟苑局學三不新款:255.00:100:200>]> 2.查詢庫存數是100並且賣出數不是0的產品 models.Product.objects.filter(Q(keep=100)&~Q(sale=0)) #&:與,~:非 models.Product.objects.filter(Q(kucun=100),~Q(maichu=0)) #<QuerySet [<Product: 跟苑局學三不新款:255.00:100:200>]> models.Product.objects.filter(Q(kucun=100)&~Q(maichu=0)).values('name') #<QuerySet [{'name': '跟苑局學三不新款'}]> models.Product.objects.filter(Q(kucun=100)&~Q(maichu=0)).values_list('name') #<QuerySet [('跟苑局學三不新款',)]>
查詢函數可以混合使用Q 對象和關鍵字參數。所有提供給查詢函數的參數(關鍵字參數或Q 對象)都將"AND”在一起。但是,如果出現Q 對象,它必須位於所有關鍵字參數的前面
#查詢產品名包含新款, 並且庫存數大於60或者價格小於100的產品 models.Product.objects.filter(Q(keep__gt=60)|Q(price__lt=100), name__contains="新款") #<QuerySet [<Product: 跟苑局學三不新款:255.00:100:200>]>
12.3215 事務
開啟一個事務可以包含一些sql語句,這些sql語句要麼同時成功,要麼都不成功,稱之為事務的原子性 作用:事務用於將某些操作的多個SQL作為原子性操作,一旦有某一個出現錯誤,即可回滾到原來的狀態,從而保證資料庫數據完整性。
import os if __name__ == '__main__': os.environ.setdefault("DJANGO_SETTINGS_MODULE", "BMS.settings") import django django.setup() import datetime from app01 import models try: from django.db import transaction with transaction.atomic(): #開啟事務 new_publisher = models.Publisher.objects.create(name="火星出版社") models.Book.objects.create(title="橘子物語", publish_date=datetime.date.today(), publisher_id=10) # 指定一個不存在的出版社id,上一行創建一條出版社數據被回滾,資料庫並未創建新數據 except Exception as e: print(str(e))
12.3216 Django ORM執行原生SQL
很多情況下我們不需要將查詢結果映射成模型,或者我們需要執行DELETE、 INSERT以及UPDATE操作,在這些情況下,我們可以直接訪問資料庫,完全避開模型層。我們可以直接從django提供的介面中獲取資料庫連接,然後像使用pymysql模塊一樣操作資料庫
from django.db import connection, connections cursor = connection.cursor() # cursor = connections['default'].cursor() cursor.execute("""SELECT * from auth_user where id = %s""", [1]) ret = cursor.fetchone()