1. 程式人生 > >Django的資料庫詳細操作

Django的資料庫詳細操作

ORM框架

O是object,也就類物件的意思,R是relation,翻譯成中文是關係,也就是關係資料庫中資料表的意思,M是mapping,是對映的意思。在ORM框架中,它幫我們把類和資料表進行了一個對映,可以讓我們通過類和類物件就能操作它所對應的表格中的資料。ORM框架還有一個功能,它可以根據我們設計的類自動幫我們生成資料庫中的表格,省去了我們自己建表的過程。

django中內嵌了ORM框架,不需要直接面向資料庫程式設計,而是定義模型類,通過模型類和物件完成資料表的增刪改查操作。

使用django進行資料庫開發的步驟如下:
1. 配置資料庫連線資訊
2. 在models.py中定義模型類
3. 遷移
4. 通過類和物件完成資料增刪改查操作


一、配置

在settings.py中儲存了資料庫的連線配置資訊,Django預設初始配置使用sqlite資料庫。

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}


1. 使用MySQL資料庫首先需要安裝驅動程式

pip install PyMySQL

 
2. 在Django的工程同名子目錄的init.py檔案中新增如下語句

from
pymysql import install_as_MySQLdb install_as_MySQLdb()

作用是讓Django的ORM能以mysqldb的方式來呼叫PyMySQL。
3. 修改DATABASES配置資訊

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'HOST': '127.0.0.1',  # 資料庫主機
        'PORT': 3306,  # 資料庫埠
        'USER': 'root',  # 資料庫使用者名稱
        'PASSWORD'
: 'mysql', # 資料庫使用者密碼 'NAME': 'django_demo' # 資料庫名字 } }


4. 在MySQL中建立資料庫

create database django_demo default charset=utf8;



二、定義模型類

  • 模型類被定義在”應用/models.py”檔案中。
  • 模型類必須繼承自Model類,位於包django.db.models中。

接下來首先以”圖書-英雄”管理為例進行演示。

1. 定義

建立應用booktest,在models.py 檔案中定義模型類。

from django.db import models

#定義圖書模型類BookInfo
class BookInfo(models.Model):
    btitle = models.CharField(max_length=20, verbose_name='名稱')
    bpub_date = models.DateField(verbose_name='釋出日期')
    bread = models.IntegerField(default=0, verbose_name='閱讀量')
    bcomment = models.IntegerField(default=0, verbose_name='評論量')
    is_delete = models.BooleanField(default=False, verbose_name='邏輯刪除')

    class Meta:
        db_table = 'tb_books'  # 指明資料庫表名
        verbose_name = '圖書'  # 在admin站點中顯示的名稱
        verbose_name_plural = verbose_name  # 顯示的複數名稱

    def __str__(self):
        """定義每個資料物件的顯示資訊"""
        return self.btitle

#定義英雄模型類HeroInfo
class HeroInfo(models.Model):
    GENDER_CHOICES = (
        (0, 'male'),
        (1, 'female')
    )
    hname = models.CharField(max_length=20, verbose_name='名稱') 
    hgender = models.SmallIntegerField(choices=GENDER_CHOICES, default=0, verbose_name='性別')  
    hcomment = models.CharField(max_length=200, null=True, verbose_name='描述資訊') 
    hbook = models.ForeignKey(BookInfo, on_delete=models.CASCADE, verbose_name='圖書')  # 外來鍵
    is_delete = models.BooleanField(default=False, verbose_name='邏輯刪除')

    class Meta:
        db_table = 'tb_heros'
        verbose_name = '英雄'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.hname

1.1 資料庫表名

模型類如果未指明表名,Django預設以 小寫app應用名_小寫模型類名 為資料庫表名。

可通過db_table 指明資料庫表名。

1.2 關於主鍵

django會為表建立自動增長的主鍵列,每個模型只能有一個主鍵列,如果使用選項設定某屬性為主鍵列後django不會再建立自動增長的主鍵列。

預設建立的主鍵列屬性為id,可以使用pk代替,pk全拼為primary key。

1.3 屬性命名限制

  • 不能是python的保留關鍵字。
  • 不允許使用連續的下劃線,這是由django的查詢方式決定的。
  • 定義屬性時需要指定欄位型別,通過欄位型別的引數指定選項,語法如下:
屬性=models.欄位型別(選項)

1.4 欄位型別
這裡寫圖片描述
1.5 選項
這裡寫圖片描述
null是資料庫範疇的概念,blank是表單驗證範疇的

1.6 外來鍵
在設定外來鍵時,需要通過on_delete選項指明主表刪除資料時,對於外來鍵引用表資料如何處理,在django.db.models中包含了可選常量:

  • CASCADE 級聯,刪除主表資料時連通一起刪除外來鍵表中資料
  • PROTECT 保護,通過丟擲ProtectedError異常,來阻止刪除主表中被外來鍵應用的資料
  • SET_NULL 設定為NULL,僅在該欄位null=True允許為null時可用
  • SET_DEFAULT 設定為預設值,僅在該欄位設定了預設值時可用
  • SET() 設定為特定值或者呼叫特定方法,如
from django.conf import settings
from django.contrib.auth import get_user_model
from django.db import models

def get_sentinel_user():
    return get_user_model().objects.get_or_create(username='deleted')[0]

class MyModel(models.Model):
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET(get_sentinel_user),
    )
  • DO_NOTHING 不做任何操作,如果資料庫前置指明級聯性,此選項會丟擲IntegrityError異常

2. 遷移

將模型類同步到資料庫中。

2.1 生成遷移檔案

python manage.py makemigrations

2.2 同步到資料庫中

python manage.py migrate

3. 新增測試資料

insert into tb_books(btitle,bread,bcomment,is_delete) values
('射鵰英雄傳','1980-5-1',12,34,0),
('天龍八部','1986-7-24',36,40,0),
('笑傲江湖','1995-12-24',20,80,0),
('雪山飛狐','1987-11-11',58,24,0);
insert into tb_heros(hname,hgender,hbook_id,hcomment,is_delete) values
('郭靖',1,1,'降龍十八掌',0),
('黃蓉',0,1,'打狗棍法',0),
('黃藥師',1,1,'彈指神通',0),
('歐陽鋒',1,1,'蛤蟆功',0),
('梅超風',0,1,'九陰白骨爪',0),
('喬峰',1,2,'降龍十八掌',0),
('段譽',1,2,'六脈神劍',0),
('虛竹',1,2,'天山六陽掌',0),
('王語嫣',0,2,'神仙姐姐',0),
('令狐沖',1,3,'獨孤九劍',0),
('任盈盈',0,3,'彈琴',0),
('嶽不群',1,3,'華山劍法',0),
('東方不敗',0,3,'葵花寶典',0),
('胡斐',1,4,'胡家刀法',0),
('苗若蘭',0,4,'黃衣',0),
('程靈素',0,4,'醫術',0),
('袁紫衣',0,4,'六合拳',0);

三、演示工具使用
1. shell工具

Django的manage工具提供了shell命令,幫助我們配置好當前工程的執行環境(如連線好資料庫等),以便可以直接在終端中執行測試python語句。

通過如下命令進入shell

python manage.py shell

匯入兩個模型類,以便後續使用

from booktest.models import BookInfo, HeroInfo

2. 檢視MySQL資料庫日誌

檢視mysql資料庫日誌可以檢視對資料庫的操作記錄。 mysql日誌檔案預設沒有產生,需要做如下配置:

sudo vi /etc/mysql/mysql.conf.d/mysqld.cnf

修改或增加內容:

general_log_file    =/var/log/mysql/mysql.log
general_log         =1

然後儲存並使用如下命令重啟mysql服務。

sudo service mysql restart

使用如下命令開啟mysql日誌檔案。

tail -f /var/log/mysql/mysql.log  # 可以實時檢視資料庫的日誌內容

Mac上可以這樣操作:
開啟 general log 將所有到達MySQL Server的SQL語句記錄下來。

一般不會開啟開功能,因為log的量會非常龐大。但個別情況下可能會臨時的開一會兒general log以供排障使用。
相關引數一共有3:general_log、log_output、general_log_file

show variables like 'general_log';  -- 檢視日誌是否開啟
set global general_log=on; -- 開啟日誌功能
show variables like 'general_log_file';  -- 看看日誌檔案儲存位置
set global general_log_file='tmp/general.lg'; -- 設定日誌檔案儲存位置
show variables like 'log_output';  -- 看看日誌輸出型別  table或file
set global log_output='table'; -- 設定輸出型別為 table
set global log_output='file';   -- 設定輸出型別為file

注:具體詳情請參照:
https://blog.csdn.net/yanpenggong/article/details/82286793


四、資料庫操作—增、刪、改、查

1. 增加

增加資料有兩種方法。

1.1 save

通過建立模型類物件,執行物件的save()方法儲存到資料庫中。

>>> from datetime import date
>>> book = BookInfo(
    btitle='西遊記',
    bput_date=date(1988,1,1),
    bread=10,
    bcomment=10
)
>>> book.save()
>>> hero = HeroInfo(
    hname='孫悟空',
    hgender=0,
    hbook=book
)
>>> hero.save()
>>> hero2 = HeroInfo(
    hname='豬八戒',
    hgender=0,
    hbook_id=book.id
)
>>> hero2.save()

1.2 create

通過模型類.objects.create()儲存。

>>> HeroInfo.objects.create(
    hname='沙悟淨',
    hgender=0,
    hbook=book
)
<HeroInfo: 沙悟淨>

2. 查詢

2.1 基本查詢
get :查詢單一結果,如果不存在會丟擲模型類.DoesNotExist異常。
all :查詢多個結果。
count :查詢結果數量。

>>> BookInfo.objects.all()
<QuerySet [<BookInfo: 射鵰英雄傳>, <BookInfo: 天龍八部>, <BookInfo: 笑傲江湖>, <BookInfo: 雪山飛狐>, <BookInfo: 西遊記>]>
>>> book = BookInfo.objects.get(btitle='西遊記')
>>> book.id
5

>>> BookInfo.objects.get(id=3)
<BookInfo: 笑傲江湖>
>>> BookInfo.objects.get(pk=3)
<BookInfo: 雪山飛狐>
>>> BookInfo.objects.get(id=100)
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/Users/delron/.virtualenv/dj/lib/python3.6/site-packages/django/db/models/manager.py", line 85, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/Users/delron/.virtualenv/dj/lib/python3.6/site-packages/django/db/models/query.py", line 380, in get
    self.model._meta.object_name
db.models.DoesNotExist: BookInfo matching query does not exist.

>>> BookInfo.objects.count()
6

2.2 過濾查詢

實現SQL中的where功能,包括

  • filter 過濾出多個結果
  • exclude 排除掉符合條件剩下的結果
  • get 過濾單一結果

對於過濾條件的使用,上述三個方法相同,故僅以filter進行講解。

過濾條件的表達語法如下:

屬性名稱__比較運算子=值
# 屬性名稱和比較運算子間使用兩個下劃線,所以屬性名不能包括多個下劃線

1)相等

exact:表示判等。

例:查詢編號為3的圖書。

BookInfo.objects.filter(id__exact=3)
可簡寫為:
BookInfo.objects.filter(id=3)

2)模糊查詢

contains:是否包含。

說明:如果要包含%無需轉義,直接寫即可。

例:查詢書名包含’遊’的圖書。

BookInfo.objects.filter(btitle__contains='遊')

startswith、endswith:以指定值開頭或結尾。
例:查詢書名以’記’結尾的圖書

BookInfo.objects.filter(btitle__endswith='記')

以上運算子都區分大小寫,在這些運算子前加上i表示不區分大小寫,如iexact、icontains、istartswith、iendswith.

3) 空查詢

isnull:是否為null。

例:查詢書名不為空的圖書。

BookInfo.objects.filter(btitle__isnull=False)

4) 範圍查詢

in:是否包含在範圍內。

例:查詢編號為1或3或5的圖書

BookInfo.objects.filter(id__in=[1, 3, 5])

5)比較查詢

gt :大於 (greater then)
gte :大於等於 (greater then equal)
lt :小於 (less then)
lte :小於等於 (less then equal)
例:查詢編號大於3的圖書

BookInfo.objects.filter(id__gt=3)

不等於的運算子,使用exclude()過濾器。

例:查詢編號不等於3的圖書

BookInfo.objects.exclude(id=3)

6)日期查詢

yearmonthdayweek_dayhourminutesecond:對日期時間型別的屬性進行運算。

例:查詢1980年發表的圖書。

BookInfo.objects.filter(bpub_date__year=1980)

例:查詢1980年1月1日後發表的圖書。

BookInfo.objects.filter(bpub_date__gt=date(1990, 1, 1))

F物件

之前的查詢都是物件的屬性與常量值比較,兩個屬性怎麼比較呢? 答:使用F物件,被定義在django.db.models中。

語法如下:

F(屬性名)

例:查詢閱讀量大於等於評論量的圖書。

from django.db.models import F
BookInfo.objects.filter(bread__gte=F('bcomment'))

可以在F物件上使用算數運算。

例:查詢閱讀量大於2倍評論量的圖書。

BookInfo.objects.filter(bread__gt=F('bcomment') * 2)

Q物件

多個過濾器逐個呼叫表示邏輯與關係,同sql語句中where部分的and關鍵字。

例:查詢閱讀量大於20,並且編號小於3的圖書。

BookInfo.objects.filter(bread__gt=20,id__lt=3)
或
BookInfo.objects.filter(bread__gt=20).filter(id__lt=3)

如果需要實現邏輯或or的查詢,需要使用Q()物件結合|運算子,Q物件被義在django.db.models中。

語法如下:

Q(屬性名__運算子=值)

例:查詢閱讀量大於20的圖書,改寫為Q物件如下。

from django.db.models import Q

BookInfo.objects.filter(Q(bread__gt=20))

Q物件可以使用&、|連線,&表示邏輯與,|表示邏輯或。

例:查詢閱讀量大於20,或編號小於3的圖書,只能使用Q物件實現

BookInfo.objects.filter(Q(bread__gt=20) | Q(pk__lt=3))

Q物件前可以使用~操作符,表示非not。

例:查詢編號不等於3的圖書。

BookInfo.objects.filter(~Q(pk=3))

聚合函式

使用aggregate()過濾器呼叫聚合函式。聚合函式包括:Avg(平均),Count(數量),Max(最大),Min(最小),Sum(求和),被定義在django.db.models中。

例:查詢圖書的總閱讀量。

from django.db.models import Sum
BookInfo.objects.aggregate(Sum('bread'))

注意aggregate的返回值是一個字典型別,格式如下:

  {'屬性名__聚合類小寫':值}
  如:{'bread__sum':3}

使用count時一般不使用aggregate()過濾器。

例:查詢圖書總數。

BookInfo.objects.count()

注意count函式的返回值是一個數字。

2.3 排序

使用order_by對結果進行排序

BookInfo.objects.all().order_by('bread')  # 升序
BookInfo.objects.all().order_by('-bread')  # 降序

2.4 關聯查詢

由一到多的訪問語法:

一對應的模型類物件.多對應的模型類名小寫_set 例:

b = BookInfo.objects.get(id=1)
b.heroinfo_set.all()

由多到一的訪問語法:

多對應的模型類物件.多對應的模型類中的關係類屬性名 例:

h = HeroInfo.objects.get(id=1)
h.hbook

訪問一對應的模型類關聯物件的id語法:

多對應的模型類物件.關聯類屬性_id

例:

h = HeroInfo.objects.get(id=1)
h.book_id

關聯過濾查詢

由多模型類條件查詢一模型類資料:

語法如下:

關聯模型類名小寫屬性名條件運算子=值
注意:如果沒有”__運算子”部分,表示等於。

例:查詢圖書,要求圖書英雄為”孫悟空”

BookInfo.objects.filter(heroinfo__hname='孫悟空')

查詢圖書,要求圖書中英雄的描述包含”八”

BookInfo.objects.filter(heroinfo__hcomment__contains='八')

由一模型類條件查詢多模型類資料:
語法如下:

一模型類關聯屬性名__一模型類屬性名__條件運算子=

注意:如果沒有”__運算子”部分,表示等於。

例:查詢書名為“天龍八部”的所有英雄。

HeroInfo.objects.filter(hbook__btitle='天龍八部')

查詢圖書閱讀量大於30的所有英雄

HeroInfo.objects.filter(hbook__bread__gt=30)

3. 修改

修改更新有兩種方法

3.1 save

修改模型類物件的屬性,然後執行save()方法

hero = HeroInfo.objects.get(hname='豬八戒')
hero.hname = '豬悟能'
hero.save()

3.2 update
使用模型類.objects.filter().update(),會返回受影響的行數

HeroInfo.objects.filter(hname='沙悟淨').update(hname='沙僧')

4. 刪除
刪除有兩種方法

4.1 模型類物件delete

hero = HeroInfo.objects.get(id=13)
hero.delete()

4.2 模型類.objects.filter().delete()

HeroInfo.objects.filter(id=14).delete()



五、查詢集 QuerySet

1. 概念

Django的ORM中存在查詢集的概念。

查詢集,也稱查詢結果集、QuerySet,表示從資料庫中獲取的物件集合。

當呼叫如下過濾器方法時,Django會返回查詢集(而不是簡單的列表):

  • all():返回所有資料。
  • filter():返回滿足條件的資料。
  • exclude():返回滿足條件之外的資料。
  • order_by():對結果進行排序。

對查詢集可以再次呼叫過濾器進行過濾,如

BookInfo.objects.filter(bread__gt=30).order_by('bpub_date')

也就意味著查詢集可以含有零個、一個或多個過濾器。過濾器基於所給的引數限制查詢的結果。

從SQL的角度講,查詢集與select語句等價,過濾器像where、limit、order by子句

判斷某一個查詢集中是否有資料:

  • exists():判斷查詢集中是否有資料,如果有則返回True,沒有則返回False。

2. 兩大特性

2.1 惰性執行
建立查詢集不會訪問資料庫,直到呼叫資料時,才會訪問資料庫,呼叫資料的情況包括迭代、序列化、與if合用

例如,當執行如下語句時,並未進行資料庫查詢,只是建立了一個查詢集qs

qs = BookInfo.objects.all()

繼續執行遍歷迭代操作後,才真正的進行了資料庫的查詢

for book in qs:
    print(book.btitle)

2.2 快取
使用同一個查詢集,第一次使用時會發生資料庫的查詢,然後Django會把結果快取下來,再次使用這個查詢集時會使用快取的資料,減少了資料庫的查詢次數。

例項1:如下是兩個查詢集,無法重用快取,每次查詢都會與資料庫進行一次互動,增加了資料庫的負載。

from booktest.models import BookInfo
[book.id for book in BookInfo.objects.all()]
[book.id for book in BookInfo.objects.all()]

例項二:經過儲存後,可以重用查詢集,第二次使用快取中的資料。

qs=BookInfo.objects.all()
[book.id for book in qs]
[book.id for book in qs]

3. 限制查詢集
可以對查詢集進行取下標或切片操作,等同於sql中的limit和offset子句。
        注意:不支援負數索引。

對查詢集進行切片後返回一個新的查詢集,不會立即執行查詢。

如果獲取一個物件,直接使用[0],等同於[0:1].get(),但是如果沒有資料,[0]引發IndexError異常,[0:1].get()如果沒有資料引發DoesNotExist異常。

示例:獲取第1、2項,執行檢視。

qs = BookInfo.objects.all()[0:2]



六、管理器Manager

管理器是Django的模型進行資料庫操作的介面,Django應用的每個模型類都擁有至少一個管理器。

我們在通過模型類的objects屬性提供的方法操作資料庫時,即是在使用一個管理器物件objects。當沒有為模型類定義管理器時,Django會為每一個模型類生成一個名為objects的管理器,它是models.Manager類的物件。

自定義管理器

我們可以自定義管理器,並應用到我們的模型類上。

注意:一旦為模型類指明自定義的過濾器後,Django不再生成預設管理物件objects。

自定義管理器類主要用於兩種情況:

1. 修改原始查詢集,重寫all()方法。

1) 開啟booktest/models.py檔案,定義類BookInfoManager

#圖書管理器
class BookInfoManager(models.Manager):
    def all(self):
        #預設查詢未刪除的圖書資訊
        #呼叫父類的成員語法為:super().方法名
        return super().filter(is_delete=False)

2)在模型類BookInfo中定義管理器

class BookInfo(models.Model):
    ...
    books = BookInfoManager()

3)使用方法

BookInfo.books.all()

2. 在管理器類中補充定義新的方法

1)開啟booktest/models.py檔案,定義方法create。

class BookInfoManager(models.Manager):
    #建立模型類,接收引數為屬性賦值
    def create_book(self, title, pub_date):
        #建立模型類物件self.model可以獲得模型類
        book = self.model()
        book.btitle = title
        book.bpub_date = pub_date
        book.bread=0
        book.bcommet=0
        book.is_delete = False
        # 將資料插入進資料表
        book.save()
        return book

2)為模型類BookInfo定義管理器books語法如下

class BookInfo(models.Model):
      ...
    books = BookInfoManager()

3)呼叫語法如下:

book=BookInfo.books.create_book("abc",date(1980,1,1))

4) 查詢結果集設定( 步驟 1) 中可以用下面的內容替代)
開啟booktest/models.py檔案,定義方法all,查詢過濾掉 is_delete=True 的結果

# 定義管理器類
class BookInfoManager(models.Manager):
    def all(self):
        return self.filter(is_delete=False)
# 定義圖書模型類BookInfo
class BookInfo(models.Model):
    ...
    ...
    # 補充自定義到管理器物件,模型類將不會再存在objects
    query = BookInfoManager()

查詢的方法為:
(注意:這裡提前修改了資料庫中 btitle=’笑傲江湖’is_delete=1 )

>>> BookInfo.query.all()
<QuerySet [<BookInfo: 射鵰英雄傳>, <BookInfo: 天龍八部>, <BookInfo: 雪山飛狐>,遊記>]>