四十三、python學習之Django框架(三):資料庫,資料庫配置,定義模型,shell工具,資料庫操作,查詢集
一、資料庫:
1. ORM框架:
object relation mapping: 物件關係對映,在ORM框架中,它幫我們把類和資料表進行了一個對映,可以讓我們通過類和類物件就能夠操作它所對應的表格中的資料.ORM框架還有一個功能,它可以根據我們設計的類自動幫我們生成資料庫中的表格,省去了自己建表的過程.
django中內嵌了ORM框架,不需要直接面向資料庫程式設計,而是定義模型類,通過模型類和物件完成資料表的增刪改查操作.
使用django進行資料庫開發的步驟如下:
- 配置資料庫連線資訊;
- 在models.py中定義模型類;
- 資料庫遷移;
- 通過類和物件完成資料增刪改查
ORM的作用:
說明:
- 我們這裡可以看到ORM可以幫助我們將模型類的操作轉換為sql語句,也可以講MySQL資料庫的返回結果轉換成模型類物件.
- 因為ORM可以幫助我們科資料米行嘞轉換成sql語句,我們在程式中看到的是模型類,不關心資料庫的型別,那麼可以操作的資料庫就可能不是一種資料庫.可是是SQLLite,也可以是Mysql,甚至是Orcle都是可行的.
- 第二個就是我們還需要在使用MySQL資料庫的時候配置一個PyMySQL的資料庫驅動,這個驅動的主要作用是連結django和資料. django程式和MySQL之間的連結是通過網路連結的,這就意味著我們對資料庫進行操作的時候需要先連線上資料庫才能進行對應的操作,那麼這個是句酷驅動就是連線django和資料庫之間的一種方式.
- ORM是把模型類轉換成sql語句,但是這個語句仍然停留在django中,沒有到達資料庫,需要資料庫驅動傳送給資料庫,
注意:
- 我們使用django和MySQL的時候, 會有問題:
-
- django中的ORM框架,在和MySQL對接的時候, ORM只認MySQL官方的MySQL-Python這個包.但是這個安裝包安裝完成之後, 它的副檔名字叫做mysqldb.
-
- 但是mysqldb這個庫只支援python2的版本, 它不支援python3的版本. 如果我們想要在python3中使用, 那麼我們使用的不是MySQL-Python, 而是PyMySQL. 但是ORM又只認mysqldb這個副檔名, 所以我們會把PyMySQL的副檔名改為mysqldb.
- ORM -----只認可----->mysqldb這個副檔名.
- MySQL-Python------安裝完成後生成的副檔名為:mysqldb-------只支援python2
- PyMySQL-------支援python3 ---------副檔名不是:mysqldb
- PyMySQL------安裝完後,轉化為: mysqldb
二、配置資料庫:
1. django專案中的配置:
在django專案建立成功之後,setting.py檔案中自動配置了資料的內容, 配置的是sqlite3資料庫:
DATABASES = {
'default': {
# 預設的配置, 使用的是sqlite3資料庫.
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
這裡使用的是MySQL資料庫,所以在進行以下修改修改:
2.為django專案配置mysql資料庫:
2.1 使用MySQL資料庫首先安裝驅動程式:
第一步: 安裝MySQl資料庫驅動
pip install PyMySQL
2.2 在Django的工程同名子目錄的init.py檔案中新增如下語句:
第二步:進行轉換,讓pymysql的內容轉換為mysqldb,以便於ORM能夠匹配
# 複製下面的內容,拷貝到django工程同名子目錄的init.py檔案中
from pymysql import install_as_MySQLdb
install_as_MySQLdb()
作用:讓Django的Orm能以mysqldb的方式來呼叫PyMySQL
2.3 修改DATABASES配置資訊:
第三步:對資料庫進行配置:修改資料庫的ip地址,埠號,使用者名稱等內容
# 將以下內容覆蓋到原來setting.py檔案中
DATEBASES = {
'default': {
# 我們這裡需要把sqlite3修改為mysql
'ENGINE': 'django.db.backends.mysql',
'HOST': '127.0.0.1', # 資料庫主機
'PORT': 3306, # 資料庫埠
'USER': 'root', # 資料庫使用者名稱
'PASSWORD': 'mysql', # 資料庫使用者密碼
'NAME': 'django_demo' # 資料庫名字
}
}
2.4 在mysql中建立資料庫:
第四步:修改完成後,就可以建立資料庫定義模型類了
- 開啟命令列:開啟mysql資料庫
mysql -u root -p
- -建立資料庫:
create database django_demo charset=utf8;
- 檢視資料庫是否建立成功:
show databases;
三、定義模型類:
這裡定義模型類和Flask類似,但是需要注意:
- 模型類被定義在"子應用/modeles.py"檔案中;
- 模型類必須繼承子django.db.models類;
from django.db import models
1.定義
接下來首先以"圖書-英雄"管理為例進行演示
- 建立子應用koobset
- 在子應用的models.py檔案中定義模型類
第一張表:
# 從django,db中匯入models
from django.db import models
# 定義圖書模型BookInfo
# 注意:一定要繼承自model.Model
class BookInfo(model.Models):
"""
定義圖書模型類
這裡定義的屬性對應的都是資料庫中的欄位
"""
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
說明:
- django中定義的屬性(對應資料庫中的欄位),不需要宣告是否存在,只需要寫在這裡的都是預設存在的,即寫在上面的btitle等屬性,等程式執行起來的時候都會在表中被創建出來;
- 我們可以在屬性的小括號中聲明當前欄位的修飾符,例如:做大長度,預設值等等;
- django中定義模型類的時候,我們不需要建立主鍵,djang會幫助我們建立一個主鍵(整型,自增);
- 除非我們自己想要指定一個主鍵,這個時候,我們可以在這個屬性後邊新增primary_key=true;
- 預設情況下宣告欄位都是不能為空的;
第二張表:
from django.db import models
#定義英雄模型類HeroInfo
class HeroInfo(models.Model):
# 這個是元組是一個可選項,元組中也是元組, 其中:
# 0, 1: 是在資料庫中真實存在的值
# 意味著hgender這個欄位在資料庫中儲存的要麼是0,要麼是1
# male, female: 用於顯示在admin站點中
# 類似於: verbose_name
GENDER_CHOICES = (
(0, 'male'),
(1, 'female')
)
hname = models.CharField(max_length=20, verbose_name='名稱')
# 性別我們使用的是類似於列舉型別來做的:
# choices: 可選範圍,傳入的是個元組型別,在上面定義的.
hgender = models.SmallIntegerField(choices=GENDER_CHOICES, default=0, verbose_name='性別')
hcomment = models.CharField(max_length=200, null=True, verbose_name='描述資訊')
# 利用ForeignKey這個屬性指定hbook這個欄位為外來鍵.
# 第一個引數: 表示外來鍵關聯到書籍表
# 注意: 如果呼叫外來鍵hbook的話: 返回的是book這個模型物件.
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
說明:
- CharField: 字串型別
- SmallInteger: 小整形,儲存的資料量比較小
1.1 資料庫名:
模型類如果未指明表名,Django預設以 小寫app應用名_小寫模型類名 為資料庫表名。
可通過db_table 指明資料庫表名。
1.2 關於主鍵:
django會為表建立自動增長的主鍵列,每個模型只能有一個主鍵列,如果使用選項設定某屬性為主鍵列後django不會再建立自動增長的主鍵列。
預設建立的主鍵列屬性為id,可以使用pk代替,pk全拼為primary key。
1.3 屬性命名限制:
-
不能是python的保留關鍵字。
-
不允許使用連續的下劃線,這是由django的查詢方式決定的。
-
定義屬性時需要指定欄位型別,通過欄位型別的引數指定選項,語法如下:
屬性名=models.欄位型別(選項)
1.4 欄位型別:
型別 | 說明 |
---|---|
AutoField | 自動增長的IntegerField,通常不用指定,不指定時Django會自動建立屬性名為id的自動增長屬性 |
BooleanField | 布林欄位,值為True或False |
NullBooleanField | 支援Null、True、False三種值 |
CharField | 字串,引數max_length表示最大字元個數 |
TextField | 大文字欄位,一般超過4000個字元時使用 |
IntegerField | 整數 |
DecimalField | 十進位制浮點數, 引數max_digits表示總位數, 引數decimal_places表示小數位數 |
FloatField | 浮點數 |
DateField | 日期, 引數auto_now表示每次儲存物件時,自動設定該欄位為當前時間,用於"最後一次修改"的時間戳,它總是使用當前日期,預設為False; 引數auto_now_add表示當物件第一次被建立時自動設定當前時間,用於建立的時間戳,它總是使用當前日期,預設為False; 引數auto_now_add和auto_now是相互排斥的,組合將會發生錯誤 |
TimeField | 時間,引數同DateField |
DateTimeField | 日期時間,引數同DateField |
FileField | 上傳檔案欄位 |
ImageField | 繼承於FileField,對上傳的內容進行校驗,確保是有效的圖片 |
1.5 選項:
選項 | 說明 |
---|---|
null | 如果為True,表示允許為空,預設值是False (判斷是否允許在資料庫系統中欄位為空) |
blank | 如果為True,則該欄位允許為空白,預設值是False, 這個和django中的表無關,和表單有關,django自帶有表單系統,有時候使用者提交的 |
db_column | 欄位的名稱,如果未指定,則使用屬性的名稱 |
db_index | 若值為True, 則在表中會為此欄位建立索引,預設值是False |
default | 預設 |
primary_key | 若為True,則該欄位會成為模型的主鍵欄位,預設值是False,一般作為AutoField的選項使用 |
unique | 如果為True, 這個欄位在表中必須有唯一值,預設值是False |
null是資料庫範疇的概念,blank是表單驗證範疇的
1.6 外來鍵:
在設定外來鍵時,需要通過on_delete選項指明主表刪除資料時,對於外來鍵引用表資料如何處理,在django.db.models中包含了可選項:
選項 | 說明 |
---|---|
CASCADE | 級聯,刪除主表資料時同時一起刪除外來鍵表中資料, 級聯刪除。 |
PROTECT | 保護模式,如果採用該選項,刪除的時候,會丟擲ProtectedError錯誤。阻止使用者的刪除. |
SET_NULL | 置空模式,刪除的時候,外來鍵欄位被設定為空,僅在該欄位null=True: 允許為null時可用. |
SET_DEFAULT | 置預設值,刪除的時候,外來鍵欄位設定為預設值,所以定義外來鍵的時候注意加上一個預設值。 |
DO_NOTHING | 不做任何操作,如果資料庫前置指明級聯性,此選項會丟擲IntegrityError異常 |
SET() | 自定義一個值,該值只能是對應的實體 |
例如:SET()選項
# 當刪除mymodel對應的user的時候,mymodel不會刪除掉,而是找到一個名叫 deleted的user,與之重建關聯
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),
)
2. 資料庫遷移:
將模型類同步到資料庫中.
2.1 生成遷移檔案:
python manage.py makemigrations
2.2 同步到資料庫
python manage.py migrate
2.3 使用資料庫:
新增測試資料
insert into tb_books(btitle,bpub_date,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提供了sheel命令工具,幫助我們配置好當前工程的執行環境(如連線資料庫等),以便可以直接在終端中執行測試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
把68,69行前面的#去除,然後儲存並使用如下命令重啟mysql服務。
sudo service mysql restart
使用如下命令開啟mysql日誌檔案。
# 可以實時檢視資料庫的日誌
tail -f /var/log/mysql/mysql.log
日誌檔案路徑的檢視可以參考: 連結
五、資料庫的操作:
1. 增加:
增加資料有兩種方法: 1.建立物件新增資料,然後嗲用save()儲存 2.通過模型類的create()函式儲存
sava()
通過建立模型類物件,執行物件的save()方法儲存到資料中.
> from datetime import date
> book = BookInfo(
btitle="西遊記",
bpub_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()
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()
2.2 過濾查詢:
實現SQL中的where功能,包括:
- filter: 過濾出多個結果;
- exclude: 排除掉符合條件剩下的結果
- get: 過濾單一結果 對於過濾條件的使用,上述三個方法相同, 故僅以filter進行解釋: 過濾條件的表達與法如下:
屬性名稱和比較運算子間使用兩個下劃線,所以屬性名不能包括多個下劃線 屬性名__比較運算子=值
1) 相等:
exact:表示判等 例: 查詢編號為1的圖書
BookInfo.objects.filter(id__exact=1)
# 也可以簡寫為:
BookInfi.objects.filter(id=1)
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.objectsfilter(id__gt=3)
不等於的運算子,使用exclude()過濾器 例:查詢編號不等於3的圖書
BookInfo.objects.exclude(id=3)
6) 日期查詢:
year、month、day、week_day、hour、minute、second:對日期時間型別的屬性進行運算。 例:查詢1980年發表的圖書。
BookInfo.objects.filter(bpub_date__year=1980)
例:查詢1980年1月1日後發表的圖書。
BookInfo.objects.filter(bpub_date__gt=date(1990, 1, 1))
F物件:
比較兩個屬性的值使用F物件。F物件被定義在django.db.models中 語法如下:
F(屬性名)
例如: 查詢閱讀量大於等於評論量的圖書
from django.db.models import F
BookInfo.objects.filter(bread__gte = F('bcomment'))
``
可以在F物件上使用算術運算.
例: 查詢閱讀量大於2倍的評論量的圖書
```python
BookInfo.objects.filter(bread__gt=F('bcomment')*2)
Q物件:
一般我們在Django程式中查詢資料庫操作都是在QuerySe中進行的, 例如下程式碼:
>>> q1 = BookInfo.objects.filter(headline__startswith="What")
>>> q2 = q1.exclude(pub_date__gte=datetime.date.today())
>>> q3 = q1.filter(pub_date__gte=datetime.date.today())
或者將其組合起來 ,如下:
>>>q1 = BookInfo.objects.filter(headline_startswith="What").exclude(pub_date_gte=datetime.date.today())
隨著我們的程式越來越複雜,查詢的條件也跟著複雜起來,這樣簡單的通過一個filter()來進行查詢的條件將導致我們的查詢越來越長。 Q( )物件就是為了將這些條件組合起來。
我們在查詢的條件中需要組合條件時(例如兩個條件“且”或者“或”)時。我們可以使用Q( )查詢物件。
例如下面的程式碼:
# 匯入Q物件
from django.db.models imports Q
# 使用Q物件查詢:
q=Q(question_startswith="What")
這樣就生成了一個Q()物件 我們可以使用符號 & 或者 | 將多個Q( )物件組合起來傳遞給filter(),exclude(),get()等函式。
當多個Q( )物件組合起來時,Django會自動生成一個新的Q( )。
例如下面程式碼就將兩個條件組合成了一個
Q(question__startswith = 'Who') | Q(question__startswith = 'What')
使用上述程式碼可以使用SQL語句這麼理解:
WHERE question LIKE 'Who%' OR question LIKE 'What%'
另外: 我們可以在Q()物件的前邊使用字元"~“來代表"非”, 例如:
Q(question__startswith="Who") | ~Q(pub_date__year=2005)
對應的SQL語句理解為:
WHERE question like "Who%" OR year(pub_date) !=2005
總結
Q物件被義在django.db.models中 使用時,需要匯入: from django.db.models import Q
語法如下: Q(屬性名__運算子=值)
Q物件可以使用 &、| 連線
& 表示邏輯與 | 表示邏輯或。
例1:查詢閱讀量大於20,或編號小於3的圖書:
BookInfo.objects.filter(Q(bread__gt=20) | Q(pk__lt=3))
例2:
BookInfo.objects.get(
Q(question__startswith='Who'),
Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))
)
上面的程式碼可以理解為:
SELECT * from BookInfo WHERE question LIKE 'Who%' AND (pub_date