Django學習入門筆記(三)
模型部分講解
本文是自己學習做的筆記,如有雷同純屬巧合,如有問題請指出,謝謝!
基於環境Ubuntu16.04
python3.6
Django 2.07
[儲備知識] ORM(物件object-關係relationship-對映map)
ORM的作用->完全面向物件不需要對任何sql語句進行操作,這樣修改資料庫(orcale/sqlite3/mysql)時只需要對少量程式碼進行修改
(1)資料庫的更換
1-在mysql下建立資料庫,如名為test2(Django預設是sqlite3的資料庫,換庫配置需要換)
2-更換mysql資料庫: 在settings檔案中的資料庫做如下配置:
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'test2', 'USER': 'root', 'PASSWORD': 'mysql', 'HOST': 'localhost', 'PORT': '3306' } }
這裡的name為需要使用到的mysql資料庫的名稱,USER PASSWORD為登陸資料庫的使用者名稱密碼
HOST為資料庫的IP地址,PORT預設為3306
注意python3可能安裝不上MYSQLdb可以用pymysql代替
方法為
import pymysql
pymysql.install_as_MySQLdb()
Django根據屬性的型別確定:當前選擇資料庫支援欄位的型別以及預設的控制元件型別,比如搜尋框等
Django會為表自動新增primary key每個模型只有一個pk(primary key)如果已經賦予了一個PK則不會自動新增
在models裡面定義表時,表名注意:1-不能設為python預留的名稱 2-不能包含__兩個以上連續的下劃線(__在後面做查詢的時候用到)
[一]表內屬性的定義型別
1-欄位型別
常用的欄位型別有:
from django.db.models as models
models.BooleanField
models.FloatField
models.TimeField
models.DateTimeField
models.DecimalField(max_digits,decimal_places) # 第一個引數是總的數的位數,第二個數是小數的位數
models.FileField: 一個上傳檔案的欄位 models.ImageField:保證上傳是個有效的image
# 這二者一般而言不會用,一般只會把檔案上傳到雲
# 然後上傳磁碟上的地址就可以
………………等欄位的型別,都以Field結尾
(在設計表的時候可以定義一個isDelete的屬性做邏輯刪除不做物理刪除)
2-欄位選項
null --->True Django將null儲存到資料庫
blank --->True允許該欄位為空白
db_column --->欄位的名稱,如果未指定就用屬性的名稱
db_index --->如果為True則在表中為欄位建立索引(加速查詢)
default --->預設值
primary --->是否為主鍵 此時欄位設定為AutoField。
unique --->如果為True這個欄位在表中必須有唯一值
用法如下
hname = models.CharField(max_length=20, db_column='Hero Name')
hgender = models.BooleanField(default=True)
3-關係(都在models中)
ForeignKey: 一對多,將欄位定義在多的端中
ManyToMany: 多對多,定義在連短中
OneToOneField: 一對一將欄位定義在任意一端中
用一訪問多: 物件.模型類小寫_set book.heroinfo_set
用一對一訪問: 物件.模型類小寫 hero.bookinfo
訪問id 物件.屬性_id hero.book_id
Example:
class HeroInfo(models.Model):
hname = models.CharField(max_length=20, db_column='Hero Name')
hgender = models.BooleanField(default=True)
isDelete = models.BooleanField(default=False)
hcontent = models.CharField(max_length=100)
hbook = models.ForeignKey(BookInfo)
book = BookInfo()
這裡就和之前的學習筆記一樣,一對多的情況把外來鍵放在多的一端
查詢一本書裡面的英雄 book.heroinfo_set
在這裡把一對多 一認為是上端,而多那個端為下端
查詢下端的方法 例項名.類屬性名_set用來查詢外來鍵資訊(在子關聯的地方會講到上端的查詢方法)
查詢英雄所在的書籍 物件.屬性 hero.book
比如:肯德基裡面有很多食品,做相互關聯
class food():
"""定義了很多類屬性"""
h_shop = models.ForeignKey(Shop)
[二]元選項
在模型類中定義類Meta用於設定元資訊
class BookInfo(models.Model):
btitle = models.CharField(max_length=20, null=False)
bpub_date = models.DateTimeField(null=True, db_column='publication date')
bread = models.IntegerField(default=0)
bcomment = models.IntegerField(default=0)
isDelete = models.BooleanField(default=False)
class Meta:
db_table='bookinfo'
ordering=['id']
books1 = models.Manager()
books2 = BookInfoManaer()
db_table: 定義資料表名, 預設名稱<app_name>_<model_name> 這個資料表的名字更改的是他在資料庫中的名字
例如:原模型類名為BookInfo
這裡將db_table = 'book1'
則在資料庫中建立表的時候名為book1而不是bookinfo(這裡都轉換為了小寫)
ordering: 物件的預設排序欄位,獲取物件的列表時使用(獲取查詢時候的順序)
ordering=['id']按照id正向輸出,ordering=[-'id']倒序輸出
元選項是在表生成前定義的表的屬性,例如排序以及表的名稱……
[三]管理器的說明
模型定義完後有很多類方法
比如BookInfo.objects.all()等 objects 是繼承的Manager型別物件,負責和資料庫進行互動,一般不作修改
每個Django模型必須有至少一個管理器,如果自己重新定義,objects預設就沒了
自定義管理器的主要作用:
1-向管理器中新增額外的方法(例如下面提到的create方法)
2-修改管理器返回的原始查詢集:重寫get_queryset()方法 修改原始的方法
class BookInfoManager(models.Manager):
def get_queryset(self):
# 修改了查詢的方法
# 這個改寫的意思是首先先繼承父類的get_queryset方法,然後過濾出未被邏輯刪除的顯示
return super(BookInfoManager, self).get_queryset().filter(isDelete=False)
class BookInfo(models.Model):
……
books1 = BookInfoManager()
books2 = models.Manger()
# 如果自己定義了一個管理器那麼一定要建立一個指向models.Manger(),因為自己建立了管理器,預設的
# object 就不會被生成
接下來總結管理器的第二個功能:
定義了上述的books1, books2可以在shell中
b = BookInfo(), b.btitle=xxx
BookInfo.books2.all() #進行查詢 會顯示所有資料
若用BookInfo.books1.all() 按照上面改寫的Manager()只會現實未被邏輯刪除的結果
但是這樣的方法太繁瑣,如果能夠直接增加刪除資料就更加方便了:
首先對property屬性、類方法和靜態方法進行復習:
特性property:property可以保護似有屬性,通過一段函式返回特定的類屬性,保護了私有屬性
import math
class Circle:
def __init__(self,radius): #圓的半徑radius
self.__radius=radius
@property
def area(self):
return math.pi * self.radius**2 #計算面積
@property
def perimeter(self):
return 2*math.pi*self.radius #計算周長
c=Circle(10)
print(c.area) #可以向訪問資料屬性一樣去訪問area,會觸發一個函式的執行,動態計算出一個值
print(c.perimeter) #同上
輸出結果:
314.1592653589793
62.83185307179586
私有屬性 radius不被外界訪問
靜態方法:一般用於類內使用,在繼承的過程中不能繼承,用於類內的動態賦值
class Date:
def __init__(self,year,month,day):
self.year=year
self.month=month
self.day=day
@staticmethod
def now(): #用Date.now()的形式去產生例項,該例項用的是當前時間
t=time.localtime() #獲取結構化的時間格式
return Date(t.tm_year,t.tm_mon,t.tm_mday) #新建例項並且返回
@staticmethod
def tomorrow():#用Date.tomorrow()的形式去產生例項,該例項用的是明天的時間
t=time.localtime(time.time()+86400)
return Date(t.tm_year,t.tm_mon,t.tm_mday)
a=Date('1987',11,27) #自己定義時間
b=Date.now() #採用當前時間
c=Date.tomorrow() #採用明天的時間
print(a.year,a.month,a.day)
print(b.year,b.month,b.day)
print(c.year,c.month,c.day)
這裡b c 通過呼叫內部的靜態方法,來定義了各自類內部的self.year, self.month, self.day
類方法:在繼承過程中能進行傳遞
import time
class Date:
def __init__(self,year,month,day):
self.year=year
self.month=month
self.day=day
# @staticmethod
# def now():
# t=time.localtime()
# return Date(t.tm_year,t.tm_mon,t.tm_mday)
@classmethod #改成類方法
def now(cls):
t=time.localtime()
return cls(t.tm_year,t.tm_mon,t.tm_mday) #哪個類來呼叫,即用哪個類cls來例項化
class EuroDate(Date):
def __str__(self):
return 'year:%s month:%s day:%s' %(self.year,self.month,self.day)
e=EuroDate.now() # 類方法的呼叫是可以不是例項物件,是面向類的
print(e) #我們的意圖是想觸發EuroDate.__str__,此時e就是由EuroDate產生的,所以會如我們所願
輸出結果:
year:2017 month:3 day:3
若不用類方法,僅用靜態方法輸出結果如下
輸出結果:
<__main__.Date object at 0x1013f9d68>
靜態方法一般用於類內部
類屬性用於類之間,因為可繼承
對錶進行操作時,若要快速新增一條資訊
用b=BookInfo(),b.btitle=xxx這樣程式化的輸入可以封裝成函式
__init__已經在models裡面寫了,通過改寫init方法很麻煩
方法一:通過類方法進行建立
進入shell模式後
通過b = BookInfo.create(xx, datetime(xx, xx, xx))進行類方法的建立
class BookInfo(models.Model):
btitle = models.CharField(max_length=20, null=False)
bpub_date = models.DateTimeField(null=True, db_column='publication date')
bread = models.IntegerField(default=0)
bcomment = models.IntegerField(default=0)
isDelete = models.BooleanField(default=False)
books1 = models.Manager()
books2 = BookInfoManaer()
@classmethod
def create(cls, btitle, bpub_date):
b = BookInfo()
b.btitle = btitle
b.bpub_date = bpub_date
b.bread = 0
b.bcomment = 0
b.isDelete = False
b.save()
方法二:通過管理器manager實現模型類的快速建立:
class BookInfoManaer(models.Manager):
def get_queryset(self):
return super(BookInfoManaer, self).get_queryset().filter(isDelete=False)
def create(self, btitle, bpub_date):
b = BookInfo()
b.btitle = btitle
b.bpub_date = bpub_date
b.bread = 0
b.bcomment = 0
b.isDelete = False
b.save()
class BookInfo(models.Model): # 模型類
btitle = models.CharField(max_length=20, null=False)
bpub_date = models.DateTimeField(null=True, db_column='publication date')
bread = models.IntegerField(default=0)
bcomment = models.IntegerField(default=0)
isDelete = models.BooleanField(default=False)
class Meta:
db_table='bookinfo'
ordering=['id']
books1 = models.Manager() # 自定義管理器是定義一個類 該類是模型類的一個類屬性
books2 = BookInfoManaer()
def __str__(self):
return self.btitle
然後可以通過
b = BookInfo.books2.create('abc', datetime(xx, xx, xx))
進行資料的增加
上面所屬的元選項和管理器都是模型類的成員
[四]模型查詢
1-查詢集
惰性執行:建立查詢集不會有資料庫的訪問,知道呼叫資料時才訪問資料庫
快取: 每個查詢集都包涵一個快取來最小化對於資料庫的訪問
查詢集=查詢的得到資料組成的列表(生成器)
返回查詢集的方法——過濾器:all(), filter(), exclude(), order_by(), values()
注意:
(1)values查詢將一個物件(所有欄位)構成一個字典,然後構成一個列表返回
(2)filter(鍵1=值1, 鍵2=值2) ======> filter(鍵1=值1).filter(鍵2=值2) 相當於邏輯與的關係
查詢單個值的方法
(1)get():返回單個滿足條件的物件 # 如果找到多條或為空都會報錯
(2)count():返回當前查詢的總條數
(3)first():返回第一個物件
(4)last():返回最後一個物件
(5)exisits():判斷查詢集中是否有資料
注意:
(1)對查詢集支援用下標的方式進行索引,但是不支援負索引
(2)在快取的時候只有用的資料是子集的時候不進行快取
(3)
2-欄位查詢
實現where子列名 利用filter()/exclude()/get()引數
語法使用: [屬性名稱__比較運算子=值]
例如filter(title__contains='%') 過濾出 一定是等號
對於外來鍵,使用屬性名_id表示檔案。HeroInfo.book_id即可查詢heroInfo中某個英雄對應的書的id
比較運算子
1-exact:表示判等__exat可以不寫預設判等
2-contains表示包涵(區分大小寫)
3-startswith\endswith 以value開頭或結尾的
例如exclude(btitle__endswith='d')
4-isnull\isnotnull
在上述運算子前面加一個i就不區分大小寫查詢
5-in表示查詢在這個範圍之內的資訊
filter(pk__in=[1, 2, 3])
把主鍵值為1, 2, 3的都查找出來
gt/gte/lt/lte 大於/大於等於/小於/小於等於
filter(bpub_date__year=1980)
filter(bpub_date__gt=date(1980, 12, 31))
跨關聯關係的查詢:處理join查詢
語法:模型類名<屬性名><比較>
相當於inner join:
BookInfo.books1.filter(heroinfo__hname__contains="人民")
用法:外來鍵類名(小寫)__需要過濾的列名__比較運算子=值
查詢書中英雄名中包含'人民'的書
關聯查詢的方式
3-聚合查詢
聚合函式:Avg, Count, Max, Min, Sum
利用aggregate()返回聚合函式的值
聚合函式的用法
from django.db.models import Max
a = BookInfo.books.all()
a.aggregate(Max('btitle'))
a.count()
F物件,可以使模型的欄位A與欄位B進行比較,如果欄位A寫在了等號的左邊,欄位B寫在了等號的右邊需要通過
F物件使用在等號兩邊是兩個欄位型別的時候用
F物件進行構造
a = BookInfo.books1.filter(bread__gte=F('bcomment'))
這裡bread和bcomment是一個模型類裡面的兩個不同欄位
F物件還可以支援算數運算
a = BookInfo.books1.filter(bread__gte=F('bcomment')*2)
F物件還可以支援關聯物件查詢
a = BookInfo.books1.filter(isDelete=F('heroinfo__isDelete'))
Q物件
用來表達邏輯或 而不是通過filter(條件1, 條件2)做與關係
Q(條件1)|Q(條件2) 表達或物件
Q(條件1)&Q(條件2) 表達邏輯與 但是太麻煩不怎麼用