Django基礎四之測試環境和ORM查詢

Hans_Wang發表於2022-03-18

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()

Django事務詳情

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()無法滿足需求:你可能要執行不明確對映至模型的查詢語句,或者就是直接執行 UPDATEINSERTDELETE 語句。可以直接訪問資料庫,完全繞過模型層。

物件 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中設定的

相關文章