1. 程式人生 > >13.Django之數據庫models&orm初探(一)

13.Django之數據庫models&orm初探(一)

try api 示例 mar 叠代 交互 reverse 一行 gre

一、使用django orm的準備操作。
django 默認支持sqlite,mysql, oracle,postgresql數據庫。
在默認情況下django的項目中會默認使用sqlite數據庫,在打開settings裏有如下設置:
技術分享圖片

當我們想改為mysql數據庫時,需要在settings.py中做以下修改。
DATABASES = {
‘default‘: {
‘ENGINE‘: ‘django.db.backends.mysql‘,
‘NAME‘: ‘test_db‘, #數據庫名稱
‘USER‘: ‘root‘, #連接mysql時使用的用戶名
‘PASSWORD‘: ‘123456‘, #連接mysql使用的密碼

‘HOST‘: ‘127.0.0.1‘, #mysql的ip
‘PORT‘: ‘3306‘, #連接mysql的端口。
}
}

當我們把連接數據的配置設置完成後,重新啟動django項目,這時會報錯!
no module named MySQLdb
這是因為django默認你導入的操作mysql的模塊是MySQLdb,可是MySQLdb對於py3有很大問題,所以我們需要的模塊是PyMySQL。

那麽如何解決呢?
首先,要找到項目目錄下的init.py文件。
然後在這個文件裏面寫兩行代碼:
import pymysql
pymysql.install_as_MySQLdb()
然後,這個報錯就會解決。
(如果依舊報錯。。。請檢查你的pymsql是否有安裝。。。)

二、在表(模型)創建之前,需要了解的概念。
首先我們先來假定下面這些概念,字段和關系。

(1)一對一關系:
假如說,一個數據庫有兩張表,其中一個表是作者的名字,另外一張表則是作者的詳細信息。
第一張表中保存有作者的姓名,第二張表則保存了,作者的詳細信息,性別,年齡,email等。
在這裏,作者的名字和作者的詳細信息就是一種一對一的關系,所以說,這種一對一的關系也沒必要拆分成兩張表。

(2)多對多關系:
拿出版商進行舉例,假如說出版商的數據在一張表中,這裏面包含出名稱,地址,所在城市,省,國家和網站。
書籍信息中包含的書名和出版日期,但是一本書,可能會有多個作者,但是一個作者又可以寫多本書。
所以說,多對多的關系就好像作者與書籍之間的關系。

(3)一對多關系:
比如一本書只能被一個出版商出版,但是一個出版商可以出版很多本書,所以說,書籍對出版商來說,就是一對多關系。
這種一對多的關系,也被稱為外鍵。

三、關於表的創建。
models.py
class UserGroup(models.Model):
uid = models.AutoField(primary_key=True)
caption = models.CharField(max_length=32,unique=True)
ctime = models.DateTimeField(auto_now_add=True, null=True)
uptime = models.DateTimeField(auto_now=True, null=True)

class UserInfo(models.Model):

id列,自增,主鍵

# 用戶名列,字符串類型,指定長度
# 字符串、數字、時間、二進制
username = models.CharField(max_length=32,blank=True,verbose_name=‘用戶名‘)
password = models.CharField(max_length=60, help_text=‘pwd‘)
email = models.CharField(max_length=60)
test = models.EmailField(max_length=19,null=True,error_messages={‘invalid‘: ‘請輸入密碼‘})
#user_group_id 數字
user_group = models.ForeignKey("UserGroup",to_field=‘uid‘) # (uid,catption,ctime,uptimew)
user_type_choices = (
    (1, ‘超級用戶‘),
    (2, ‘普通用戶‘),
    (3, ‘訪客‘),
)
user_type_id = models.IntegerField(choices=user_type_choices,default=1)

上面這段代碼,每個數據模型都是django.db.models.Model的子類,它的父類Model包含了所有必要的和數據庫交互的方法。
其次每個模型相當於單個數據庫表(多對多關系例外,會多生成一張關系表),每個屬性也是這個表中的字段。屬性名就是字段名,它的類型(例如CharField)相當於數據庫的字段類型(例如varchar)。大家可以留意下其它的類型都和數據庫裏的什麽字段對應。

然後在仔細思考下,前面說的到的三種關系:
分別是一對一,一對多,多對多。
一對一:實現一對一的本質,就是在外鍵,(author_id就是foreign key)的關系基礎上,給外鍵加了一個UNIQUE=True的屬性;

一對多:就是主外鍵關系;(foreign key)

多對多:自動創建第三張表(當然我們也可以自己創建第三張表:兩個foreign key。

四、orm之基本的增刪改查。
(1)增:
from app01.models import *

#create方式一: Author.objects.create(name=‘kodakumi‘)

#create方式二: Author.objects.create(**{"name":"hamasakiayumi"})

!!註意!當使用到mysql innodb的事物特性的時候,就要使用save的方式插入記錄了。!!

#save方式一: author=Author(name="amuro")
author.save()

#save方式二: author=Author()
author.name="amuro"
author.save()

(2)刪:
models.UserInfo.objects.filter(username="amuro").delete()

(3)改:
models.UserInfo.objects.filter(id=3).update(password="130")

--------------- save方法---------
obj=models.UserInfo.objects.filter(id=3)[0]
obj.password = ‘130‘
obj.save()

(4)查:

#result = models.UserInfo.objects.all() #列出表所有記錄,返回一個QuerySet類型。

!!在這裏簡單介紹一下什麽是Queryset類型!
在這裏,我們可以把Queryset理解為一個列表,這個列表中有很多對象,每個對象就是數據庫中的每一條數據!!

#result = models.UserInfo.objects.filter(username=‘root‘,password=‘123‘) #按條件查詢
    查詢相關API如下:
    【1】filter(**kwargs):      它包含了與所給篩選條件相匹配的對象。
    filter方法的參數補充:
    #在這以age列做例子:
    age_gt = 1 #age大於1的。
    age_lt = 1 #age小於1的。
    age_lte = 1 #age小於等於1
    age_gte = 1 #age大於等於1
    age_in = [1,2,3] # 找出age為1或2或3的(只要列表中有的就可以)
    age_range = [1,2]#age為這個範圍的。
    age_startwith = ‘xxx‘ #age列以xxx開頭的
    age_contains = ‘xxx‘ #包含xxx的。
    【2】all():                 查詢所有結果。
    【3】get(**kwargs):         返回與所給篩選條件相匹配的對象,返回結果有且只有一個,如果符合篩選條件的對象超過一個或者沒有都會拋出錯誤。

#===============================#
下面是對查詢結果進行處理的API
【1】values(field):        返回一個ValueQuerySet——一個特殊的QuerySet,運行後得到的並不是一系列 model的實例化對象,而是一個可叠代的字典序列.
需要特別註意!!values這個方法可以用來分組!!

values 可以取表中所有記錄中的某一個或多個列的數據。
例如:
v1 = models.UserInfo.objects.values(‘name‘) #select UserInfo.name from UserInfo
print (v1.query) #獲取到生成的sql語句。
select app01_UserInfo.name from UserInfo

!!通過values方法然後通過.annotate進行組合!
from django.db.models import Count
v2 = models.UserInfo.objects.values(‘name‘).annotate(count_num=Count(‘id‘))
print (v2.query)
#select ‘app01_UserInfo‘.‘id‘,COUNT(‘app01_UserInfo‘.id) as "count_num" from "app01_UserInfo" group by ‘app01_UserInfo‘.id‘

!!組合後的查詢結果,還可以通過filter方法來進行篩選!

from django.db.models import Count
v2=models.UserInfo.objects.values(‘name‘).annotate(count_num=Count(‘id‘)).filter(count_num = 2)
!#篩選出count_num = > 2 的記錄。
print (v2.query)
#select ‘app01_UserInfo‘.‘id‘,COUNT(‘app01_UserInfo‘.id) as "count_num" from "app01_UserInfo" group by ‘app01_UserInfo‘.id‘ having count (‘app01_UserInfo‘.id‘)> 2
#!也就是說把filter放在後面,出現的是sql語句中的having!!

【2】order_by(*field):      對查詢結果排序。
例:
user_list = models.UserInfo.objects.all().order_by(‘id‘) #以id列為基準從小到大排序。
user_list = models.UserInfo.objects.all().order_by(‘-id‘) #以id列為基準從大到小逆向排序。
    user_list = models.UserInfo.objects.all().order_by(‘-id‘,‘name‘) #以id列為基準從大到小逆向排序,當id列有值重復時,按照name從小到大排序。

【3】reverse() :對查詢的結果進行反向排序。
【4】distinct(): 從結果中刪除重復的記錄。
【5】values_list(*field): 它與values()非常相似,它返回的是一個元組序列,values返回的是一個字典序列。
【6】count(): 返回數據庫中匹配查詢(QuerySet)的對象數量。
【7】first(): 返回第一條記錄
【8】last(): 返回最後一條記錄
【9】exists(): 如果QuerySet包含數據,就返回True,否則返回False。

單表條件查詢:
models.Tb1.objects.filter(id=10,name=‘test‘) #找出id=10 並且name字段為test的記錄 。
!########下面是神奇的雙(下劃線)!########
models.Tb1.objects.filter(id
lt=10, idgt=1) # 獲取id大於1 且 小於10的值
models.Tb1.objects.filter(id
in=[11, 22, 33]) # 獲取id等於11、22、33的數據
models.Tb1.objects.exclude(id__in=[11, 22, 33]) # not in

是否為空:
Entry.objects.filter(pub_date__isnull=True)

五、通過外鍵來實現一對多的示例:
models.py
class Business(models.Model):
#id
caption = models.CharField(max_length=32)

class Host(models.Model):
nid = models.AutoField(primary_key=True)
hostname = models.CharField(max_length=32,db_index=True)
ip = models.GenericIPAddressField(protocol=‘ipv4‘,db_index=True)
port = models.IntegerField()
busi = models.ForeignKey(to=‘Business‘,to_field=‘id‘)#創建外鍵,這個外鍵以表Business的id字段為準。
不過需要註意的是,這個busi變量的內容不是某個值,而是一個特殊對象,可以通過這個對象去取到Business表中對應id的記錄。!!

當創建好外鍵關系後,在 Business表中插入了三條數據:
+----+-----------+
| id | caption |
+----+-----------+
| 1 | 運維部 |
| 2 | 開發部 |
| 3 | 測試部 |
+----+-----------+

然後在host表中插入四條主機信息:
+-----+----------+---------------+------+---------+
| nid | hostname | ip | port | busi_id |
+-----+----------+---------------+------+---------+
| 1 | host1 | 192.168.100.1 | 80 | 1 |
| 2 | host2 | 192.168.100.5 | 80 | 1 |
| 3 | host3 | 192.168.100.6 | 80 | 2 |
| 4 | host4 | 192.168.100.7 | 80 | 2 |
+-----+----------+---------------+------+---------+

這時,使用django orm進行聯表查詢:
views.py:

def host(request):
h1 = models.Host.objects.all()
for row in h1:
print(row.nid,row.hostname,row.ip,row.busi.caption)
return HttpResponse(‘host‘)
#busi 帶只了另外一張表的一行數據,這就是外鍵的特性!
結果如下:
1 host1 192.168.100.1 運維部
2 host2 192.168.100.5 運維部
3 host3 192.168.100.6 開發部
4 host4 192.168.100.7 開發部

六、關於F,Q
F:當要對數據庫記錄做修改的時候,能快速獲取到上一個值,來進行操作。
比如:
models.UserInfo.objects.all().update(age=F(‘age‘)+1)

Q:用於復雜的條件查詢,Q對象(django.db.models.Q)可以對關鍵字參數進行封裝,從而更好地應用多個查詢。可以組合使用 &(and),|(or),~(not)操作符,當一個操作符是用於兩個Q的對象,它產生一個新的Q對象。
例如:
用法1:
models.UserInfo.objects.filter(Q(id=1)|Q(id__gt=2))
##查找出id為1或者2的記錄。

用法2:直接創建一個Q對象。
q1 = Q( ) #首先創建一個Q對象。
q1.connector = ‘OR‘ #Q對象中的查詢條件的連接方式。
q1.children.append((‘id‘,1))
q1.children.append((‘id‘,2)) #在Q1這個對象裏添加兩個查詢條件。

更復雜的條件嵌套。
q1 = Q( )
q1.connector = ‘OR‘
q1.children.append((‘id‘,1))
q1.children.append((‘id‘,2))

q2 = Q( )
q2.connector = ‘OR‘
q2.children.append((‘name‘,‘ayu‘))
q2.children.append((‘name‘,‘hamasaki‘))

con = Q( )
con.add(q1,‘AND‘)
con.add(q2,‘AND‘)
#最後生成的結果是(id=1 or id=2) and (name=ayu or name=hamasaki)

13.Django之數據庫models&orm初探(一)