Django基礎四之測試環境和ORM查詢
Django基礎四之測試環境和ORM查詢
1. 搭建測試環境
Django
是一個整體,不能單獨測試某一個.py
文件,要想測試需要搭建測試環境。
1.1 測試環境搭建方法:
方法一:
在項目里創建一個py文件,名字隨意起。在這個py文件里寫:
"""
從manage.py里拷出來前四行有非注釋的程式碼。
import os
import sys
def main():
"""Run administrative tasks."""
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'day05templates.settings')
# 然後增加兩行固定程式碼:
import django
django.setup()
# 下面是自己寫的測試程式碼:
from templateByValue import models
res=models.Books.objects.all()
print(res)
if __name__ == '__main__':
main()
"""
方法二:
使用pycharm
pycharm ---> python Console
1.2 使用測試環境對資料庫進行CURD
使用ORM向表插入數據時,一定要先插入沒有主鍵的表,否則會報錯。如果一定要先操作有主鍵的表,則要在settings.py
裡面的DATABASES
增加取消主鍵檢查的操作
DATABASES = {
'default': {
'ENGINE'--->'django.db.backends.mysql',
'NAME'--->'orm',
'USER'--->'root',
'PASSWORD'--->'123456',
'HOST'--->'192.168.1.109',
'PORT'--->'3306',
'OPTIONS':{
"init_command":"SET foreign_key_checks = 0;", # 取消主鍵檢查
},
}
}
測試環境:
test.py:
import os
import sys
def main():
"""Run administrative tasks."""
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'day06Library.settings')
import django
django.setup()
from lib01 import models
"""1. 增加一條數據"""
res = models.Book.objects.create(title='平凡的世界',price=30.9)
"""create 返回值是當前創建的數據對象"""
print(res)
"""2. 查詢全部"""
res = models.Book.objects.all()
""".query能列印出來對應的SQL語句"""
print(res.query)
"""使用filter查詢如果沒有條件也是列印全部 """
res = models.Book.objects.filter()
"""2.1 filter()過濾,返回一個query對象(filter裡面可以設置多個參數,是AND的關係) """
res = models.Book.objects.filter(id=1)
""" .get和.filter()一樣"""
res = models.Book.objects.get(title='平凡的世界')
"""但是get如果沒找到,則會報錯"""
print(type(gres))
lib01.models.DoesNotExist: Book matching query does not exist.
""""3. 修改"""
"""Pk能夠自動查找 到當前表的主鍵欄位,我們不需要查看當表主鍵欄位名"""
models.Book.objects.filter(pk=1).update(price=39)
"""4. 刪除"""
models.Book.objects.filter(pk=1).delete()
"""5 first和last"""
# 拿第一個
res = models.Book.objects.all().first()
print(res)
# 拿最後一個
res = models.Book.objects.all().last()
print(res)
# 還支援切片
res = models.Book.objects.all().first()[0]
"""6. 只取指定的欄位"""
res = models.Book.objects.all().values('title')
print(res) # 只取title欄位
# all()其實可加可不加。
res = models.Book.objects.all().values_list('title')
"""values_list獲取的結果,類似於列表套元組"""
"""
order_by排序: 默認是升序,降序是加個減號(-)
"""
res = models.Book.objects.order_by('price')# 升序
res = models.Book.objects.order_by('-price')#降序
"""count()計數"""
res = models.Book.objects.count()
"""distinct()去重
去重的前提是數據必須是一模一樣,一定不能忽略主鍵
"""
res = models.Book.objects.values('title').distinct()
""" exclude() 排除"""
res = models.Book.objects.exclude(title='平凡的世界')
'''reverse() 反轉
reverse需要先排序之後再反轉
'''
res = models.Book.objects.order_by('price').reverse()
"""exists() query包含數據返回True,否則返回False"""
if __name__ == '__main__':
main()
1.3 返回QuerySet對象的方法
all()
filter()
exclude()
order_by()
reverse()
distinct()
**特殊的QuerySet **
values() 返回可迭代的字典序列
values_list() 返回可迭代的元組序列
1.4 返回具體對象的方法
get()
first()
last()
1.5 返回布爾值的方法
exists()
1.6 返回數字的方法
count()
1.7 範圍查找(雙下劃線查詢)
1. 價格大於30的書籍:
res = models.Book.objects.filter(price__gt=30)
__gt 大於
2. 價格小於30的書籍:
res = models.Book.objects.filter(price__lt=30)
__lt小於
3. 價格大於等於30的書籍:
res = models.Book.objects.filter(price__gte=30)
__gte 大於等於
4. 價格小於等於30的書籍:
res = models.Book.objects.filter(price__lte=30)
__lte小於等於
5, 價格要麼是10元,要麼是20,要麼是30的
res = models.Book.objects.filter(price__in=[10,20,30])
"""
Python時面的數字類型精確度不高,很多時候會使用字元串存儲數字類型,特別是小數,用的時候再從字元串轉成小數
"""
6, 價格在10元到30元之間的
res = models.Book.objects.filter(price__range=(10,30))
7, 查詢書名包含字母a的書籍
res = models.Book.objects.filter(title__contains='a')
# 上面是區分大小寫,如果忽略大小寫:
res = models.Book.objects.filter(title__icontains='a')
8, 以什麼開頭, 結尾:
# 開頭:
res = models.Book.objects.filter(title__startswith='a')
# 結尾:
res = models.Book.objects.filter(title__endswith='a')
9, 查詢出版日期是2000年的書籍:
res = models.Book.objects.filter(publish_time__year=2000)
10, 查詢出版日期是8月的書籍:
res = models.Book.objects.filter(publish_time__month=8)
1.8 外鍵欄位操作
外鍵欄位
# 直接傳主鍵值
"""
models.Book.objects.create(title='聊齋',price=666.98,publish_id=1)
models.Book.objects.create(title='聊齋志異2',price=666.98,publish_id=2)
"""
# 傳數據對象
"""
publish_obj = models.Publish.objects.filter(pk=1).first()
models.Book.objects.create(title='神鵰俠侶', price=234.56, publish=publish_obj)
models.Book.objects.filter(pk=1).update(publish_id=2)
models.Book.objects.filter(pk=3).update(publish=publish_obj)
"""
多對多外鍵欄位
# 增
"""
book_obj = models.Book.objects.filter(pk=1).first()
# 主鍵值
book_obj.authors.add(1) # 去第三張關係表中 與作者主鍵為1的綁定關係
# 作者對象
author_obj = models.Author.objects.filter(pk=2).first()
book_obj.authors.add(author_obj)
# 括弧內支援傳多個參數
book_obj.authors.add(1,2)
author_obj1 = models.Author.objects.filter(pk=1).first()
author_obj2 = models.Author.objects.filter(pk=2).first()
book_obj.authors.add(author_obj1,author_obj2)
# 改
book_obj.authors.set([1,])
book_obj.authors.set((1,2))
book_obj.authors.set([author_obj1, ])
book_obj.authors.set([author_obj1,author_obj2 ])
# 刪
book_obj.authors.remove(1)
book_obj.authors.remove(1,2)
book_obj.authors.remove(author_obj1,author_obj2)
# 清空
book_obj.authors.clear() # 去第三張關係表中刪除所有改書籍對應的記錄
"""
總結:
add() remove() 括弧內既可以傳數字也可以傳對象 逗號隔開即可 set() 括弧內必須傳遞可迭代對象 可迭代對象內既可以傳數字也可以傳對象 支援多個 clear() 清空操作 無需傳值
2. 跨表查詢
2.1 正向查詢和反向查詢
正向查詢: 外鍵欄位在誰哪,誰查另外的表就是正向 例如: 書 表和出版社表,外鍵在書表上,要查一本書對應的出版社就是正向查詢 反正查詢: 表中沒有外鍵,它要去查是反向 例如: 作者表和作者詳情表,作者表中只有id和作者姓名,作者詳情表裡有作者的電話,地址等。作者表中有外鍵。用作者的電話查對應的作者就是反射查詢 查詢技巧: 正向查詢按外鍵欄位 反向查詢按表名小寫加_set(如何出現:應用名.表名.None 則再加.all()) 先查詢出一個對象,然後基於對象再去找
2.2 基於對於的跨表查詢(子查詢)
正向查詢:
1.查詢《三重門》書籍對應的出版社名稱
book_obj = models.Book.objects.filter(b_name='三重門').first().publish
res = book_obj.publish.p_name
print(res)
2.查詢《三重門》對應的作者
book_obj = models.Book.objects.filter(b_name='三重門').first()
res = book_obj.authors.first()
print(res.a_name)
3.查詢余華的地址
author_obj = models.Author.objects.filter(a_name='余華').first()
res = author_obj.authorDetail.a_address
print(res)
# 反向查詢:
4.查詢作家出版社出版過的書籍
publish_obj = models.Publish.objects.filter(p_name='作家出版社').first()
res = publish_obj.book_set.all()
print(res)
5.查詢莫言寫過的書
author_obj = models.Author.objects.filter(a_name='莫言').first()
res = author_obj.book_set.all()
print(res)
6.查詢電話是0536123456的作者姓名
author_obj = models.AuthorDetail.objects.filter(a_phone='0536123456').first()
res = author_obj.author.a_name
print(res)
2.3 雙下劃線跨表查詢(連表查詢)
正向查詢:
1.查詢《十三步書籍》對應的出版社名稱
res=models.Book.objects.filter(b_name='十三步').values('publish__p_name')
print(res)
2.查詢《活著》對應的作者和年齡
res = models.Book.objects.filter(b_name='活著').values('authors__a_name','authors__a_age')
print(res)
3.查詢韓寒的地址
res = models.Author.objects.filter(a_name='韓寒').values("authorDetail__a_address")
print(res)
正向查詢:
4.查詢《許三觀賣血記》書籍對應的出版社名稱(不用model.BOOK)
res = models.Publish.objects.filter(book__b_name='許三觀賣血記').values("p_name")
print(res)
5.查詢《紅高粱家族》對應的作者和年齡(不用model.BOOK)
res = models.Author.objects.filter(book__b_name='紅高粱家族').values('a_name','a_age')
print(res)
6.查詢莫言的地址(不用作者表)
res = models.AuthorDetail.objects.filter(author__a_name='莫言').values("a_address")
print(res)
7.查詢《像少年啦飛馳》對應的作者的電話和地址
res = models.Author.objects.filter(book__b_name='像少年啦飛馳').values('authorDetail__a_address','authorDetail__a_phone')
print(res)
8. 查詢價格為 50,25.5,或19.9的所有書籍和出版社名稱 res=models.Book.objects.filter(b_price__in=(50,25.5,19.9)).values('b_name','publish__p_name')
print(res)
9. 查詢"作家出版社"出版的圖書按價格從高到低排序
res = models.Publish.objects.filter(p_name='作家出版社').values('book__b_name', 'book__b_price').order_by('-book__b_price')
print(res)
3. 聚合查詢
聚合函數
sum() max() min() count() avg()
聚合函數的使用
1. 計算全部圖書的平均價格
from django.db.models import Sum, Max, Min, Avg, Count
res = models.Book.objects.all().aggregate(Avg('b_price'))
print(res)
2. 全部圖書中價格最高的:
from django.db.models import Sum, Max, Min, Avg, Count
res = models.Book.objects.all().aggregate(Max('b_price'))
print(res)
3. 全部圖書中價格總價格:
from django.db.models import Sum, Max, Min, Avg, Count
res = models.Book.objects.all().aggregate(Sum('b_price'))
print(res)
3. 全部圖書中價格最低的:
from django.db.models import Sum, Max, Min, Avg, Count
res = models.Book.objects.all().aggregate(Min('b_price'))
print(res)
4. 全部圖書的數量:
from django.db.models import Sum, Max, Min, Avg, Count
res = models.Book.objects.all().aggregate(Count('b_price'))
print(res)
5. 韓寒出版圖書的總價格:
from django.db.models import Sum, Max, Min, Avg, Count
res = models.Author.objects.filter(a_name='韓寒').values('book__b_price').aggregate(Sum('book__b_price'))
print(res)
6.作家出版社中出版的價格最高的圖書
from django.db.models import Sum, Max, Min, Avg, Count
res = models.Publish.objects.filter(p_name='作家出版社').values('book__b_name').aggregate(Max('book__b_price'))
print(res)
7. 聚合函數結果重命名:
res = models.Book.objects.all().aggregate(boo_sum = Sum('b_price'))
print(res)
也可以把多個聚合函數放在一起:
計算圖書的總價格和平均價格
res = models.Book.objects.all().aggregate(boo_sum = Sum('b_price'),book_avg =Avg('b_price'))
print(res)
4. F查詢和Q查詢
4.1 F查詢
F
查詢就是取出某個欄位對應的值
# 找出閱讀書比評論數高的書籍
from django.db.models import F
res = models.Book.objects.filter(read_number__gt=F('commit_number'))
print(res)
# 給每本書的價格都加1元
from django.db.models import F
res = models.Book.objects.all().update(b_price=F("b_price")+1)
print(res)
4.2 Q查詢
Q查詢
就是構造出 與&
、或|
、非~
默認情況下在filter()
裡面的關係為and 與
的關係,要想表示或和非
就要用Q查詢
。
# 查詢書名為<活著>或價格大於40的書籍
from django.db.models import Q
res = models.Book.objects.filter(Q(b_name='活著')|Q(b_price__gt=40))
print(res)
# 查詢書名不是<活著>的書籍
from django.db.models import Q
res=models.Book.objects.filter(~Q(b_name='活著'))
print(res)
4.3 查看原生SQL
1. Queryset對象.query()
2. 在settings.py裡面配置日誌列印:
settings.py:
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console':{
'level':'DEBUG',
'class':'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'propagate': True,
'level':'DEBUG',
},
}
}
5. 聚合分組
1. 查詢每個出版社id,以及它所出版書的平均價格:
原生SQL:
SELECT publish_id,AVG(b_price) from lib01_book GROUP BY publish_id;
ORM實現:
"""
annotate()內寫聚合函數
values 在前,表示 group by 的欄位
values 在後,表示取欄位
filter 在前, 表示 where條件
filter 在後,表示having
from django.db.models import Sum, Max, Min, Avg, Count
res = models.Book.objects.all().values('publish_id').annotate(price_avg = Avg('b_price')).values('publish_id','price_avg')
print(res.query)
"""
2. 查詢出版社id大於1的出版社ID,以及所出書的平均價格
原生SQL:
"""
select publish_id, avg(b_price) from lib01_book where publish_id >1 GROUP BY publish_id;
"""
ORM實現:
"""
from django.db.models import Sum, Max, Min, Avg, Count res=models.Book.objects.filter(publish_id__gt=1).values('publish_id').annotate(price_avg=Avg('b_price')).values('publish_id', 'price_avg')
print(res)
"""
3. 查詢出版社id大於1的出版社ID,以及所出書的平均價格大於30的
原生SQL:
"""
select publish_id, avg(b_price) as price_avg from lib01_book where publish_id >1 GROUP BY publish_id having price_avg>30 ;
"""
ORM實現:
"""
from django.db.models import Sum, Max, Min, Avg, Count res=models.Book.objects.filter(publish_id__gt=1).values('publish_id').annotate(price_avg=Avg('b_price')).filter(price_avg__gt=30).values('publish_id', 'price_avg')
print(res)
"""
4,查詢每個出版社的名字和出版的書籍數量
原生SQL:
"""
select p_name, count(b.b_name) from lib01_publish p, lib01_book b where p.id=b.publish_id group by p.id ;
"""
ORM:
"""
聯表操作最後以group by的表作為基表
from django.db.models import Sum, Max, Min, Avg, Count
res = models.Publish.objects.values('id').annotate(num=Count('book__id')).values('p_name','num')
print(res.query)
如果基表是group by的表,可以不寫values
models.Publish.objects.annotate(num=Count('book__id')).values('p_name','num')
"""
5. 查詢每個作者出版過書籍的最高價格,列印作者名和最高價格
原生SQL:
"""
select a.a_name, max(b.b_price) from lib01_author a, lib01_book b, lib01_book_authors ba where b.id=ba.book_id and a.id = ba.author_id group by a.a_name;
"""
ORM:
"""
from django.db.models import Sum, Max, Min, Avg, Count
res = models.Author.objects.annotate(price_max=Max('book__b_price')).values('a_name', 'price_max')
print(res)
"""
6. 查詢每個書籍的名稱,以及對應的作者個數
原生SQL:
"""
select a.b_name, count(b.a_name) from lib01_book a, lib01_author b, lib01_book_authors c where a.id=c.book_id and b.id= c.author_id group by a.b_name;
"""
ORM:
"""
from django.db.models import Sum, Max, Min, Avg, Count res=models.Book.objects.values('b_name').annotate(author_count=Count('authors__id')).values('b_name', 'author_count')
print(res)
"""
7. 統計價格大於25書籍和作者
原生SQL:
"""
select a.b_name, c.a_name from lib01_book a inner join lib01_book_authors b on a.id=b.book_id inner join lib01_author c on c.id=b.author_id where a.b_price >25;
"""
ORM:
"""
res = models.Book.objects.filter(b_price__gt=25).values('b_name','authors__a_name')
print(res.query)
"""
6. 事務
Django 默認的事務行為是自動提交。
測試test.py:
import os
def main():
"""Run administrative tasks."""
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'day06Library.settings')
import django
django.setup()
from lib01 import models
res = models.Book.objects.filter(pk=3).first()
print(res.b_name, res.b_price)
from django.db import transaction
try:
with transaction.atomic(): # 開啟事務
models.Book.objects.filter(pk=3).update(b_price=50)
res = models.Book.objects.filter(pk=3).first()
print(res.b_name, res.b_price)
raise Exception("Error")
# 其實如果開啟了事務,那麼只要報錯這個事務就會回滾,使用try....except只是不讓程式報錯退出
except Exception as e:
print("回滾")
transaction.rollback() # 事務回滾
print("回滾成功")
res = models.Book.objects.filter(pk=3).first()
print(res.b_name, res.b_price)
if __name__ == '__main__':
main()
開啟事務:
使用裝飾器
from django.db import transaction
@transaction.atomic
def viewfunc(request):
# This code executes inside a transaction.
do_stuff()
使用上下文件管理
from django.db import transaction
def viewfunc(request):
# This code executes in autocommit mode (Django's default).
do_stuff()
with transaction.atomic():
# This code executes inside a transaction.
do_more_stuff()
7. choices參數
例如一張表要存儲學生成績,'A'為'優秀','B'為'良好','C'為'及格','D'為'不及格',如果查詢為A則列印優秀,查詢D列印不及格。
方法一:
查詢出來後自己寫程式碼判斷輸出。】
方法二:
1. 在資料庫里創建表時使用choices參數
models.py創建表:
class Score(models.Model):
score_choices = (
('A', '優秀'),
('B', '良好'),
('C', '及格'),
('D', '不及格'),
)
name = models.CharField(max_length=128)
score = models.CharField(max_length=12,choices=score_choices)
2.插入數據
3.查詢:
res = models.Score.objects.filter(pk=1).first()
print(res.name, res.score)
# 結果為: Z A
res = models.Score.objects.filter(pk=1).first()
print(res.name, res.get_score_display())
# 結果為: Z 優秀
總結:
1.傳值:
對於choices參數,數據類型該怎麼選? 判斷依據是:小元組裡面第一個參數的數據類型
2.取值:
固定語法結構取值:get_欄位名_display() 如果查詢出來的數據不再choices範圍內,會顯示原始數據。
8. 常用欄位
8.1 常用欄位
AutoField(Field)
int自增列,必須填入參數 primary_key=True
BigAutoField(AutoField)
bigint自增列,必須填入參數 primary_key=True
當model中如果沒有自增列,則自動會創建一個列名為id的列
IntegerField
一個整數類型(有符號的),範圍在 -2147483648 to 2147483647。
SmallIntegerField(IntegerField):
小整數 -32768 ~ 32767
PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
正小整數 0 ~ 32767
PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正整數 0 ~ 2147483647
BigIntegerField(IntegerField):
長整型(有符號的) -9223372036854775808 ~ 9223372036854775807
BooleanField(Field)
布爾值類型
NullBooleanField(Field):
可以為空的布爾值
CharField(Field)
字元類型
必須提供max_length參數, max_length表示字元長度
TextField(Field)
文本類型
EmailField(CharField):
字元串類型(Email),Django Admin以及ModelForm中提供驗證機制
IPAddressField(Field)
字元串類型,Django Admin以及ModelForm中提供驗證 IPV4 機制
GenericIPAddressField(Field)
字元串類型,Django Admin以及ModelForm中提供驗證 Ipv4和Ipv6
參數:
protocol,用於指定Ipv4或Ipv6, 'both',"ipv4","ipv6"
unpack_ipv4, 如果指定為True,則輸入::ffff:192.0.2.1時候,可解析為192.0.2.1,開啟此功能,需要protocol="both"
URLField(CharField)
字元串類型,Django Admin以及ModelForm中提供驗證 URL
SlugField(CharField)
字元串類型,Django Admin以及ModelForm中提供驗證支援 字母、數字、下劃線、連接符(減號)
CommaSeparatedIntegerField(CharField)
字元串類型,格式必須為逗號分割的數字
UUIDField(Field)
字元串類型,Django Admin以及ModelForm中提供對UUID格式的驗證
FilePathField(Field)
字元串,Django Admin以及ModelForm中提供讀取文件夾下文件的功能
參數:
path, 文件夾路徑
match=None, 正則匹配
recursive=False, 遞歸下面的文件夾
allow_files=True, 允許文件
allow_folders=False, 允許文件夾
FileField(Field)
字元串,路徑保存在資料庫,文件上傳到指定目錄
參數:
upload_to = "" 上傳文件的保存路徑
storage = None 存儲組件,默認django.core.files.storage.FileSystemStorage
ImageField(FileField)
字元串,路徑保存在資料庫,文件上傳到指定目錄
參數:
upload_to = "" 上傳文件的保存路徑
storage = None 存儲組件,默認django.core.files.storage.FileSystemStorage
width_field=None, 上傳圖片的高度保存的資料庫欄位名(字元串)
height_field=None 上傳圖片的寬度保存的資料庫欄位名(字元串)
DurationField(Field)
長整數,時間間隔,資料庫中按照bigint存儲,ORM中獲取的值為datetime.timedelta類型
FloatField(Field)
浮點型
DecimalField(Field)
10進位小數
參數:
max_digits,小數總長度
decimal_places,小數位長度
BinaryField(Field)
二進位類型
DateField
日期欄位,日期格式 YYYY-MM-DD,相當於Python中的datetime.date()實例。
DateTimeField
日期時間欄位,格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ],相當於Python中的datetime.datetime()實例
8.2 ORM與MySQL中對應關係
'AutoField' ---> 'integer AUTO_INCREMENT',
'BigAutoField' ---> 'bigint AUTO_INCREMENT',
'BinaryField' ---> 'longblob',
'BooleanField' ---> 'bool',
'CharField' ---> 'varchar(%(max_length)s)',
'CommaSeparatedIntegerField'--->'varchar(%(max_length)s)',
'DateField' ---> 'date',
'DateTimeField' ---> 'datetime',
'DecimalField' ---> 'numeric(%(max_digits)s, %(decimal_places)s)',
'DurationField' ---> 'bigint',
'FileField' ---> 'varchar(%(max_length)s)',
'FilePathField' ---> 'varchar(%(max_length)s)',
'FloatField' ---> 'double precision',
'IntegerField' ---> 'integer',
'BigIntegerField'--->'bigint',
'IPAddressField'---> 'char(15)',
'GenericIPAddressField'--->'char(39)',
'NullBooleanField'---> 'bool',
'OneToOneField' ---> 'integer',
'PositiveIntegerField'---> 'integer UNSIGNED',
'PositiveSmallIntegerField'---> 'smallint UNSIGNED',
'SlugField' ---> 'varchar(%(max_length)s)',
'SmallIntegerField'---> 'smallint',
'TextField' ---> 'longtext',
'TimeField' ---> 'time',
'UUIDField' ---> 'char(32)',
8.3 關係欄位
8.3.1 ForeignKey
外鍵類型在ORM中用來表示外鍵關聯關係,一般把ForeignKey欄位設置在 『一對多』中』多』的一方。
ForeignKey可以和其他表做關聯關係同時也可以和自身做關聯關係。
to 設置要關聯的表
to_field 設置要關聯的表的欄位
related_name 反向操作時,使用的欄位名,用於代替原反向查詢時的』表名_set』。
例如要查某個作者寫了哪些書:
models.Author.objects.filter(name='xx').book_set.all()
當在ForeignKey欄位中添加了參數 related_name 後,
class Book(models.Model):
name = models.CharField(max_length=32)
Author = models.ForeignKey(to="author", related_name="book_author")
再查某個作者的書籍是:
models.Author.objects.filter(name='xx').book__author.all()
related_query_name 反向查詢操作時,使用的連接前綴,用於替換表名。
on_delete 當刪除關聯表中的數據時,當前表與其關聯的行的行為。(1.x版本中不用寫,2.x 3.x版本必須指定)
"""
models.CASCADE
刪除關聯數據,與之關聯也刪除
models.DO_NOTHING
刪除關聯數據,什麼都不做
models.PROTECT
刪除關聯數據,引發錯誤ProtectedError
models.SET_NULL
刪除關聯數據,與之關聯的值設置為null(前提FK欄位需要設置為可空)
models.SET_DEFAULT
刪除關聯數據,與之關聯的值設置為默認值(前提FK欄位需要設置默認值)
models.SET
刪除關聯數據,
a. 與之關聯的值設置為指定值,設置:models.SET(值)
b. 與之關聯的值設置為可執行對象的返回值,設置:models.SET(可執行對象)
"""
db_constraint 是否在資料庫中創建外鍵約束,默認為True。
8.3.2 OneToOneField
一對一欄位。
一對一的關聯關係多用在當一張表的不同欄位查詢頻次差距過大的情況下,將本可以存儲在一張表的欄位拆開放置在兩張表中,然後將兩張表建立一對一的關聯關係。例如作者表和作者詳情表。
to 設置要關聯的表。
to_field 設置要關聯的欄位。
on_delete 同ForeignKey欄位。
8.3.3 ManyToManyField
用於表示多對多的關聯關係。在資料庫中通過第三張表來建立關聯關係
to 設置要關聯的表
related_name 同ForeignKey欄位。
related_query_name 同ForeignKey欄位。
symmetrical 僅用於多對多自關聯時,指定內部是否創建反向操作的欄位。默認為True。
through 在使用ManyToManyField欄位時,Django將自動生成一張表來管理多對多的關聯關係。
但我們也可以手動創建第三張表來管理多對多關係,此時就需要通過through來指定第三張表的表名。
through_fields 設置關聯的欄位。
db_table 默認創建第三張表時,資料庫中表的名稱。
9. 多對多關係中第三張表的創建方式
9.1 自行創建第三張表
class Book(models.Model):
title = models.CharField(max_length=32, verbose_name="書名")
class Author(models.Model):
name = models.CharField(max_length=32, verbose_name="作者姓名")
# 自己創建第三張表,分別通過外鍵關聯書和作者
class Book2Author(models.Model):
book = models.ForeignKey(to="Book")
author = models.ForeignKey(to="Author")
class Meta:
unique_together = ("book","author" )
9.2 通過ManyToManyField自動創建第三張表
之前一直使用的方法
class Book(models.Model):
title = models.CharField(max_length=32, verbose_name="書名")
Authors = models.ManyToManyField(to="Author", related_name="book2authors")
# 通過ORM自帶的ManyToManyField自動創建第三張表
class Author(models.Model):
name = models.CharField(max_length=32, verbose_name="作者姓名")
9.3 設置ManyTomanyField並指定自行創建的第三張表
# 自己創建第三張表,並通過ManyToManyField指定關聯
class Book(models.Model):
title = models.CharField(max_length=32, verbose_name="書名")
authors = models.ManyToManyField(to="Author", through="Book2Author", through_fields=("book", "author"))
# through_fields接受一個2元組('field1','field2'):
# 其中field1是定義ManyToManyField的模型外鍵的名(book),field2是關聯目標模型(author)的外鍵名。
class Author(models.Model):
name = models.CharField(max_length=32, verbose_name="作者姓名")
class Book2Author(models.Model):
book = models.ForeignKey(to="Book")
author = models.ForeignKey(to="Author")
class Meta:
unique_together = ("book","author" )
注意:
當我們需要在第三張關係表中存儲額外的欄位時,就要使用第三種方式。
但是當我們使用第三種方式創建多對多關聯關係時,就無法使用set、add、remove、clear方法來管理多對多的關係了,需要通過第三張表的model來管理多對多關係。
10. META
ORM
對應的類裡面包含另一個Meta
類,而Meta
類封裝了一些資料庫的資訊
欄位
db_table
ORM在資料庫中的表名默認是 app_類名,可以通過db_table可以重寫表名。
例如:
class book(models.Model):
title = models.CharField(max_length=32)
class Meta:
db_table = "自己設置表名"
index_together
聯合索引。
unique_together
聯合唯一索引。
ordering
指定默認按什麼欄位排序。只有設置了該屬性,查詢到的結果才可以被reverse()。
# 後台管理admin中顯示的表名稱
verbose_name='自己設置'
11. ORM中執行原生SQL
Django
允許你用兩種方式執行原生 SQL
查詢:你可以使用 .raw()
來 執行原生查詢並返回模型實例,或者完全不用模型層 直接執行自定義 SQL。
11.1 使用.raw()
執行原生SQL
res = models.Book.objects.raw('select * from lib01_book')
print(res)
for i in res:
print(i.b_name,i.b_price)
# 結果:
<RawQuerySet: select * from lib01_book>
紅高粱家族 25.50
十三步 50.00
三重門 25.00
像少年啦飛馳 19.90
活著 29.50
許三觀賣血記 28.20
# 將參數傳給 raw()
可以使用 raw() 的 params 參數:
bookName="三重門"
res=models.Book.objects.raw('select * from lib01_book where b_name = %s', [bookName])
for i in res:
print(i.b_name,i.b_price)
# 結果:
三重門 25.00
params 是一個參數字典。你將用一個列表替換查詢字元串中 %s 佔位符,或用字典替換 %(key)s 佔位符(key 被字典 key 替換),不論你使用哪個資料庫引擎。這些佔位符會被 params 參數的值替換。
11.2 執行自定義 SQL
當.raw()
無法滿足需求:你可能要執行不明確映射至模型的查詢語句,或者就是直接執行 UPDATE
, INSERT
或 DELETE
語句。可以直接訪問資料庫,完全繞過模型層。
對象 django.db.connection
代表默認資料庫連接。要使用這個資料庫連接,調用 connection.cursor()
來獲取一個指針對象。然後,調用 cursor.execute(sql, [params])
來執行該 SQL 和 cursor.fetchone()
,或 cursor.fetchall()
獲取結果數據。
from django.db import connection
with connection.cursor() as cursor:
cursor.execute("SELECT b_name, b_price FROM lib01_book WHERE b_name = %s", ["三重門"])
row = cursor.fetchone()
print(row)
若你同時使用 不止一個資料庫,你可以使用 django.db.connections 獲取指定資料庫的連接(和指針)。 django.db.connections 是一個類字典對象,它允許你通過連接別名獲取指定連接:
with connections['my_db_alias'].cursor() as cursor:
示例:
from django.db import connections
with connections['user'].cursor() as cursor:
cursor.execute("SELECT * FROM lib01_book")
# row = cursor.fetchone()
row = cursor.fetchall()
connections['my_db_alias'] : 本例中使用'user',這是在settings.py中DATABASES中設置的