Python筆記:Django框架的模型層詳解

Model模型

  • 模型是你的數據的唯一的、權威的信息源。它包含你所儲存數據的必要字段和行爲。
  • 通常,每個模型對應數據庫中唯一的一張表。
  • 每個模型都是django.db.models.Model的一個Python 子類。
  • 模型的每個屬性都表示爲數據庫中的一個字段。
  • Django 提供一套自動生成的用於數據庫訪問的API;
  • 這極大的減輕了開發人員的工作量,不需要面對因數據庫變更而導致的無效勞動。
  • 備註:本文基於django版本1.11展開, 技術棧會有所升級, 僅供參考!

模型與數據庫的關係

  • 模型(Model)負責業務對象和數據庫的關係映射(ORM)
  • ORM是“對象-關係-映射”的簡稱,主要任務是:
    • 根據對象的類型生成表結構
    • 將對象、列表的操作,轉換爲sql語句
    • 將sql查詢到的結果轉換爲對象、列表
  • 使用ORM,我們就可以不用像以前一樣寫sql語言了

爲什麼要用模型

  • Model是MVC框架中重要的一部分,主要負責程序中用於處理數據邏輯的部分。通常模型對象負責在數據庫中存取數據
  • 它實現了數據模型與數據庫的解耦,即數據模型的設計不需要依賴於特定的數據庫,通過簡單的配置就可以輕鬆更換數據庫

配置Mysql數據庫

1 ) 在當前環境中安裝mysql

  • $ sudo apt-get install mysql-server
  • $ sudo apt install mysql-client
  • $ sudo apt install libmysqlclient-dev

2 ) 在當前python環境中安裝 pymysql

  • $ pip3 install pymysql

3 ) 在mysql中創建數據庫

  • $ create databases d_models_db default charset=utf8

4 ) 在Django項目中配置數據庫

  • 修改settings.py文件中的DATABASE配置項
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql',
            'NAME': 'd_models_db', # 選擇數據庫的名, 請確認你的mysql中有這個庫
            'USER': 'root',
            'PASSWORD': '',
            'HOST': 'localhost',
            'PORT': '3306',
            }
    }
    

5 ) 告訴Django在接下來的mysql操作中使用pymysql

  • 打開主項目目錄下的__init__.py,寫入以下代碼導入pymysql:
    import pymysql
    pymysql.install_as_MySQLdb()
    

開發流程

1 ) 在models.py中定義模型類,要求繼承自models.Model

from django.db import models
from datetime import datetime

# Create your models here.
class Users(models.Model):
 name = models.CharField(max_length=32)
 age = models.IntegerField(default=20)
 phone = models.CharField(max_length=16)
 addtime=models.DateTimeField(default=datetime.now)

 class Meta:
    db_table = "users"  # 指定表名

2 ) 把應用加入settings.py文件的installed_app項

  • 編輯主項目目錄下的settings.py文件,並將項目應用文件名添加到該INSTALLED_APPS設置。

    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'app',
    ]
    
  • 生成遷移文件

    • $ python3 manage.py makemigrations
      django.core.exceptions.ImproperlyConfigured: Error loading MySQLdb module: No module named 'MySQLdb'.
      Did you install mysqlclient or MySQL-python?
      # 安裝上面"配置Mysql數據庫"的第五步配置,或安裝:pip install mysqlclient後就可以了
      
    • 若執行 $python3 manage.py makemigrations命令(可能把migrations文件夾給刪了),會提示"No changes detected." 可能有用的解決方式如下:
      • 先 $python3 manage.py makemigrations --empty yourappname 生成一個空的initial.py
      • 再 $python3 manage.py makemigrations 生成原先的model對應的migration file
  • 執行遷移

    • $ python3 manage.py migrate
  • 使用模型類進行crud操作

  • 備註:在實際項目中生成遷移文件和執行遷移這兩部基本不會出現,因爲我們的數據庫基本是已經設計好的

定義模型

  • 在模型中定義屬性,會生成表中的字段
  • django根據屬性的類型確定以下信息:
    • 當前選擇的數據庫支持字段的類型
    • 渲染管理表單時使用的默認html控件
    • 在管理站點最低限度的驗證
  • django會爲表增加自動增長的主鍵列,每個模型只能有一個主鍵列,如果使用選項設置某屬性爲主鍵列後,則django不會再生成默認的主鍵列
  • 屬性命名限制
    • 不能是python的保留關鍵字
    • 由於django的查詢方式,不允許使用連續的下劃線

定義屬性

  • 定義屬性時,需要字段類型
  • 字段類型被定義在django.db.models.fields目錄下,爲了方便使用,被導入到django.db.models
  • 使用方式
    • 導入from django.db import models
    • 通過models.Field創建字段類型的對象,賦值給屬性
  • 對於重要數據都做邏輯刪除,不做物理刪除,實現方法是定義isDelete屬性,類型爲BooleanField,默認值爲False

1 ) 字段類型

  • AutoField:一個根據實際ID自動增長的IntegerField,通常不指定
    • 如果不指定,一個主鍵字段將自動添加到模型中
  • BooleanField:true/false 字段,此字段的默認表單控制是CheckboxInput
  • NullBooleanField:支持null、true、false三種值
  • CharField(max_length=字符長度):字符串,默認的表單樣式是 TextInput
  • TextField:大文本字段,一般超過4000使用,默認的表單控件是Textarea
  • IntegerField:整數
  • DecimalField(max_digits=None, decimal_places=None):使用python的Decimal實例表示的十進制浮點數
    • DecimalField.max_digits:位數總數
    • DecimalField.decimal_places:小數點後的數字位數
  • FloatField:用Python的float實例來表示的浮點數
  • DateField[auto_now=False, auto_now_add=False]):使用Python的datetime.date實例表示的日期
    • 參數DateField.auto_now:每次保存對象時,自動設置該字段爲當前時間,用於"最後一次修改"的時間戳,它總是使用當前日期,默認爲false
    • 參數DateField.auto_now_add:當對象第一次被創建時自動設置當前時間,用於創建的時間戳,它總是使用當前日期,默認爲false
    • 該字段默認對應的表單控件是一個TextInput. 在管理員站點添加了一個JavaScript寫的日曆控件,和一個“Today"的快捷按鈕,包含了一個額外的invalid_date錯誤消息鍵
    • auto_now_add, auto_now 這些設置是相互排斥的,他們之間的任何組合將會發生錯誤的結果
  • TimeField:使用Python的datetime.time實例表示的時間,參數同DateField
  • DateTimeField:使用Python的datetime.datetime實例表示的日期和時間,參數同DateField
  • FileField:一個上傳文件的字段
  • ImageField:繼承了FileField的所有屬性和方法,但對上傳的對象進行校驗,確保它是個有效的image

2 ) 字段選項

  • 通過字段選項,可以實現對字段的約束
  • 在字段對象時通過關鍵字參數指定
  • null:如果爲True,Django 將空值以NULL 存儲到數據庫中,默認值是 False
  • blank:如果爲True,則該字段允許爲空白,默認值是 False
  • 對比:null是數據庫範疇的概念,blank是表單驗證證範疇的
  • db_column:字段的名稱,如果未指定,則使用屬性的名稱
  • db_index:若值爲 True, 則在表中會爲此字段創建索引
  • default:默認值
  • primary_key:若爲 True, 則該字段會成爲模型的主鍵字段
  • unique:如果爲 True, 這個字段在表中必須有唯一值

3 ) 關係

  • 關係的類型包括
    • ForeignKey:一對多,將字段定義在多端中
    • ManyToManyField:多對多,將字段定義在兩端中
    • OneToOneField:一對一,將字段定義在任意一端中
  • 可以維護遞歸的關聯關係,使用’self’指定,詳見“自關聯”
  • 用一訪問多:對象.模型類小寫_set
    • bookinfo.heroinfo_set
  • 用一訪問一:對象.模型類小寫
    • heroinfo.bookinfo
  • 訪問id:對象.屬性_id
    • heroinfo.book_id

元選項

  • 在模型類中定義類Meta,用於設置元信息

  • 元信息db_table:定義數據表名稱,推薦使用小寫字母

  • 數據表的默認名稱:<app_name>_<model_name>

  • 例如: djangoApp_users

  • ordering:對象的默認排序字段,獲取對象的列表時使用,接收屬性構成的列表

    class BookInfo(models.Model):
        ...
        class Meta():
            ordering = ['id']
    
  • 字符串前加-表示倒序,不加-表示正序

    class BookInfo(models.Model):
        ...
        class Meta():
            ordering = ['-id']
    
  • 排序會增加數據庫的開銷

示例演示

  • 創建test2項目,並創建booktest應用,使用mysql數據庫
  • 定義圖書模型
    class BookInfo(models.Model):
        btitle = models.CharField(max_length=20)
        bpub_date = models.DateTimeField()
        bread = models.IntegerField(default=0)
        bcommet = models.IntegerField(default=0)
        isDelete = models.BooleanField(default=False)
    
  • 英雄模型
    class HeroInfo(models.Model):
        hname = models.CharField(max_length=20)
        hgender = models.BooleanField(default=True)
        isDelete = models.BooleanField(default=False)
        hcontent = models.CharField(max_length=100)
        hbook = models.ForeignKey('BookInfo')
    
  • 定義index、detail視圖
  • index.html、detail.html模板
  • 配置url,能夠完成圖書及英雄的展示

測試數據

  • 模型BookInfo的測試數據

    insert into booktest_bookinfo(btitle,bpub_date,bread,bcommet,isDelete) 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)
    
  • 模型HeroInfo的測試數據

    insert into booktest_heroinfo(hname,hgender,hbook_id,hcontent,isDelete) 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 ) 類的屬性

  • objects:是Manager類型的對象,用於與數據庫進行交互
  • 當定義模型類時沒有指定管理器,則Django會爲模型類提供一個名爲objects的管理器
  • 支持明確指定模型類的管理器
    class BookInfo(models.Model):
    ...
    books = models.Manager()
    
  • 當爲模型類指定管理器後,django不再爲模型類生成名爲objects的默認管理器

2 ) 創建對象

  • 當創建對象時,django不會對數據庫進行讀寫操作
  • 調用save()方法才與數據庫交互,將對象保存到數據庫中
  • 使用關鍵字參數構造模型對象很麻煩,推薦使用下面的兩種之式
  • 說明:init方法已經在基類models.Model中使用,在自定義模型中無法使用

3 ) 實例的屬性

  • DoesNotExist:在進行單個查詢時,模型的對象不存在時會引發此異常,結合try/except使用

4 ) 實例的方法

  • __str__(self):重寫object方法,此方法在將對象轉換成字符串時會被調用
  • save():將模型對象保存到數據表中
  • delete():將模型對象從數據表中刪除

模型查詢

  • 查詢集表示從數據庫中獲取的對象集合
  • 查詢集可以含有零個、一個或多個過濾器
  • 過濾器基於所給的參數限制查詢的結果
  • 從Sql的角度,查詢集和select語句等價,過濾器像where和limit子句
  • 接下來主要討論如下知識點
    • 查詢集
    • 字段查詢:比較運算符,F對象,Q對象

1 ) 查詢集

  • 在管理器上調用過濾器方法會返回查詢集
  • 查詢集經過過濾器篩選後返回新的查詢集,因此可以寫成鏈式過濾
  • 惰性執行:創建查詢集不會帶來任何數據庫的訪問,直到調用數據時,纔會訪問數據庫
  • 何時對查詢集求值:迭代,序列化,與if合用
  • 返回查詢集的方法,稱爲過濾器
    • all()
    • filter()
    • exclude()
    • order_by()
    • values() : 一個對象構成一個字典,然後構成一個列表返回
  • 寫法:
    filter(鍵1=值1,鍵2=值2)
    等價於
    filter(鍵1=值1).filter(鍵2=值2)
    
  • 返回單個值的方法
    • get():返回單個滿足條件的對象
      • 如果未找到會引發"模型類.DoesNotExist"異常
      • 如果多條被返回,會引發"模型類.MultipleObjectsReturned"異常
    • count():返回當前查詢的總條數
    • first():返回第一個對象
    • last():返回最後一個對象
    • exists():判斷查詢集中是否有數據,如果有則返回True

限制查詢集

  • 查詢集返回列表,可以使用下標的方式進行限制,等同於sql中的limit和offset子句
  • 注意:不支持負數索引
  • 使用下標後返回一個新的查詢集,不會立即執行查詢
  • 如果獲取一個對象,直接使用[0],等同於[0:1].get(),但是如果沒有數據,[0]引發IndexError異常,[0:1].get()引發DoesNotExist異常
    #這會返回前5個對象 LIMIT 5
    Entry.objects.all()[:5]
    #這將返回第六個到第十個對象 OFFSET 5 LIMIT 5
    Entry.objects.all()[5:10]
    

查詢集的緩存

  • 每個查詢集都包含一個緩存來最小化對數據庫的訪問
  • 在新建的查詢集中,緩存爲空,首次對查詢集求值時,會發生數據庫查詢,django會將查詢的結果存在查詢集的緩存中,並返回請求的結果,接下來對查詢集求值將重用緩存的結果
  • 情況一:這構成了兩個查詢集,無法重用緩存,每次查詢都會與數據庫進行一次交互,增加了數據庫的負載
    print([e.title for e in Entry.objects.all()])
    print([e.title for e in Entry.objects.all()])
    
  • 情況二:兩次循環使用同一個查詢集,第二次使用緩存中的數據
    querylist=Entry.objects.all()
    print([e.title for e in querylist])
    print([e.title for e in querylist])
    
  • 何時查詢集不會被緩存:當只對查詢集的部分進行求值時會檢查緩存,但是如果這部分不在緩存中,那麼接下來查詢返回的記錄將不會被緩存,這意味着使用索引來限制查詢集將不會填充緩存,如果這部分數據已經被緩存,則直接使用緩存中的數據

2 ) 字段查詢

  • 實現where子名,作爲方法filter()、exclude()、get()的參數
  • 語法:屬性名稱__比較運算符=值
  • 表示兩個下劃線,左側是屬性名稱,右側是比較類型
  • 對於外鍵,使用“屬性名_id”表示外鍵的原始值
  • 轉義:like語句中使用了%與,匹配數據中的%與,在過濾器中直接寫,例如:filter(title__contains="%")=>where title like ‘%%%’,表示查找標題中包含%的

比較運算符

  • exact:表示判等,大小寫敏感;如果沒有寫“ 比較運算符”,表示判等

    • filter(isDelete=False)
  • contains:是否包含,大小寫敏感

    • exclude(btitle__contains='www')
  • startswithendswith:以value開頭或結尾,大小寫敏感

    • exclude(btitle__endswith='com')
  • isnullisnotnull:是否爲null

    • filter(btitle__isnull=False)
  • 在前面加個i表示不區分大小寫,如iexacticontainsistarswithiendswith

  • in:是否包含在範圍內

    • filter(pk__in=[1, 2, 3, 4, 5])
  • gtgteltlte:大於、大於等於、小於、小於等於

    • filter(id__gt=3)
  • yearmonthdayweek_dayhourminutesecond:對日期間類型的屬性進行運算

    • filter(bpub_date__year=1980)
    • filter(bpub_date__gt=date(1980, 12, 31))

跨關聯關係的查詢:處理join查詢

  • 語法:模型類名 <屬性名> <比較>
  • 注:可以沒有__<比較>部分,表示等於,結果同inner join
  • 可返向使用,即在關聯的兩個模型中都可以使用
    • filter(heroinfo_ _hcontent_ _contains='八')

聚合函數

  • 使用aggregate()函數返回聚合函數的值
  • 函數:AvgCountMaxMinSum
    from django.db.models import Max
    maxDate = list.aggregate(Max('bpub_date'))
    
  • count的一般用法
    • count = list.count()

Model模型的實戰

1 ) 創建數據庫和表(若是執行表遷移,可跳過下面操作)

  • 進入MySQL數據庫創建數據庫:d_models_db
  • 進入數據庫創建數據表:users
    CREATE TABLE `myapp_users` (                          
    `id` int(10) unsigned NOT NULL AUTO_INCREMENT,       
    `name` varchar(32) NOT NULL,                         
    `age` tinyint(3) unsigned NOT NULL DEFAULT '20',     
    `phone` varchar(16) DEFAULT NULL,
    `addtime` datetime(6) NOT NULL,                    
    PRIMARY KEY (`id`)                                   
    ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
    
  • 備註:當然也可以通過遷移文件生成users表(需要使用Meta來改名)
  • 添加幾條測試數據

2 ) 創建項目djangoApp和應用app

  • 創建項目框架 djangoApp $ django-admin startproject djangoApp && cd djangoApp
  • 在項目中創建一個myapp應用 $ python3 manage.py startapp app
  • 創建模板目錄 $ mkdir tpls && cd mkdir tpls/app
  • 注:最外層的目錄Django-1.11.11-model 是改名而來的
  • 項目目錄結構:
    Django-1.11.11-model
        ├── djangoApp
        │   ├── __init__.py
        │   ├── settings.py
        │   ├── urls.py
        │   └── wsgi.py
        ├── manage.py
        ├── app
        │   ├── admin.py
        │   ├── apps.py
        │   ├── __init__.py
        │   ├── models.py
        │   ├── tests.py
        │   └── views.py
        └── tpls
            └── app
    

3 ) 執行數據庫連接配置,網站配置

  • 編輯djangoApp/init.py文件,添加Pymysql的數據庫操作支持

    import pymysql
    pymysql.install_as_MySQLdb()
    
  • 編輯djangoApp/settings.py文件,配置數據庫連接

    # ...
    #配置自己的服務器IP地址
    ALLOWED_HOSTS = ['localhost','127.0.0.1','*']
    
    # ...
    #添加自己應用
    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'app',
    ]
    
    # ...
    
    # 配置模板路徑信息
    TEMPLATES = [
        {
            'BACKEND': 'django.template.backends.django.DjangoTemplates',
            'DIRS': [os.path.join(BASE_DIR,'tpls')],
            'APP_DIRS': True,
            'OPTIONS': {
                'context_processors': [
                    'django.template.context_processors.debug',
                    'django.template.context_processors.request',
                    'django.contrib.auth.context_processors.auth',
                    'django.contrib.messages.context_processors.messages',
                ],
            },
        },
    ]
    
    # ...
    # 數據庫連接配置
    DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'd_models_db',
        'USER': 'root',
        'PASSWORD': '', # 填寫自己的數據庫密碼
        'HOST': 'localhost',
        'PORT': '3306',
    }
    # ...
    

4 ) 定義Model類

  • 編輯djangoApp/models.py

    from django.db import models
    from datetime import datetime
    
    class Users(models.Model):
        name = models.CharField(max_length=32)
        age = models.IntegerField(default=20)
        phone = models.CharField(max_length=20)
        createTime = models.DateTimeField(default=datetime.now)
        
        def __str__(self):
            return self.name+":"+self.phone
    
        class Meta:
            db_table = "users"  # 指定表名
    
  • 測試Model類的使用, 執行 $ python3 manage.py shell

    [root@localhost djangoApp]#  python3 manage.py shell
    >>> from app.models import Users
    >>> Users.objects.all()
    <QuerySet [<Users: 張三:12345678901>, <Users: aa:13456789023>]>
    >>> s = Users.objects.get(id=1)
    >>> s.id
    1
    >>> s.name
    '張三'
    >>> s.age
    20
    >>> s.phone
    '12345678901'
    

5 ) 實現Web端訪問

  • 編輯djangoApp/settings.py文件.做 ALLOWED_HOSTS 主機訪問配置(若之前已做可跳過此步驟)

    • ALLOWED_HOSTS = ['localhost','127.0.0.1','這裏填寫主機的ip地址', '*']
  • 編寫項目主路由urls配置,配置對djangoApp應用路由的訪問連接配置

    from django.conf.urls import include,url
    from django.contrib import admin
    
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^', include('app.urls')),
    ]
    
  • 配置當前應用djangoApp的路由配置

    • 在djangoApp應用目錄下創建一個路由文件urls.py文件,注意此文件編碼爲utf-8(建議複製一個)。
    • 編輯應用中的路由配置文件:djangoApp/app/urls.py, 內容如下:
      from django.conf.urls import url
      from . import views
      
      urlpatterns = [
          url(r'^$', views.index, name="index"),
      ]
      
  • 編輯視圖文件:djangoApp/views.py,內容如下

    #from django.shortcuts import render
    from django.http import HttpResponse
    from myapp.models import Users
    
    def index(request):
        try:
            s = Users.objects.get(id=1)
            return HttpResponse(s)
        except:
            return HttpResponse("沒有找到對應的信息!")
    
  • 測試環節:在項目根目錄下運行 $ python3 manage.py runserver命令,開啓服務

  • 打開瀏覽器,在瀏覽其中輸入網址測試:127.0.0.1:8000

  • 繼續上面操作完成Web版的Users信息的crud操作,詳見下面示例倉庫

6 ) 示例操作代碼倉庫詳見

發佈了410 篇原創文章 · 獲贊 221 · 訪問量 69萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章