1. 程式人生 > >Django學習入門筆記(三)

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) 表達邏輯與 但是太麻煩不怎麼用