Python學習——django模型

django框架中的模型models是用來控制數據庫的,屬於MVT中的M層。有關數據的操作的都要經過models模塊。
模型的使用步驟:

  1. 配置數據庫
  2. models.py定義模型類
  3. 激活模型
  4. 使用模型

Django默認使用的是sqlite,但在生產環境中一般會用MySQL、postgrsql、oracle等關係型數據庫。
1、數據庫配置
在項目的settings.py文件中找到DATABASES配置項,將其信息修改爲:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',  #mysql數據庫引擎
        'NAME': 'test',                        #數據庫名
        'HOST':'localhost',                    #數據庫服務器地址
        'USER': 'test',                        #mysql數據庫用戶名
        'PASSWORD': 'test123',                 #密碼
        'PORT':3306,                         #端口號,可選
    }
}


在虛擬開發環境中,安裝MySQL的數據庫驅動pymysql

(django) python@ubuntu:~/project/hello$ pip install pymysql


Django默認使用MySQLdb模塊連接MySQL,但Python3中還沒有此模塊,所以需要使用pymysql來代替,在項目的__init__.py中寫入:

import pymysql
pymysql.install_as_MySQLdb()


2、ORM
對象關係映射(Object Relational Mapping,簡稱ORM)模式是一種爲了解決面向對象與關係數據庫存在的互不匹配的現象的技術,簡單的說,ORM是通過使用描述對象和數據庫之間映射的元數據,自動生成sql語句,將程序中的對象自動保存到關係數據庫中。
優點:

  •  隱藏了數據庫訪問的細節,簡化了sql的使用,提高了開發效率
  •  解耦了業務邏輯(view)和數據處理層(model),簡化了開發流程,提高了系統的可移植性
  •  提高了安全性

缺點:

  •  執行效率低
  •  對複雜sql無能爲力
  •  增加了學習成本

2.1基本概念

面向對象概念 面向關係概念
對象 記錄(一行)
屬性 字段(屬性、列)

2.2模型屬性
模型中的屬性和數據庫表的字段對應,必須定義。

#屬性定義語法爲:
屬性 = models.字段類型(選項)

屬性命名規則:

  • 不能是python的保留關鍵字
  • 不允許使用連續的下劃線,因爲連續下劃線在查詢中會用到
  • 定義屬性時需要指定字段類型
  • 主鍵一般不用自己定義,django會自動創建自增長主鍵列,如果自己定義了主鍵,則django不會自動生成主鍵

2.2.1字段類型

字段類型 字段說明 參數
AutoField 一個根據實際Id自動增長的 IntegerField(通常不指定 自動生成)  
CharField 字符串,默認的表單樣式是 TextInput max_length=字符長度
TextField 大文本字段,一般超過4000使用,默認的表單控件是Textarea  
IntegerField 整數  
DecimalField 使用python的Decimal實例表示的十進制浮點數 max_digits總位數;decimal_places小數位數
FloatField 用Python的float實例來表示的浮點數  
BooleanField true/false 字段,此字段的默認表單控制是CheckboxInput  
NullBooleanField 支持null、true、false三種值  
DateField 使用Python的datetime.date實例表示的日期,該字段默認對應的表單控件是一個TextInput auto_now和auto_now_add、default這三個參數不能同時共存
TimeField 使用Python的datetime.time實例表示的時間 參數同DateField
DateTimeField 使用Python的datetime.datetime實例表示的日期和時間 參數同DateField
ImageField 繼承了FileField的所有屬性和方法,但對上傳的對象進行校驗,確保它是個有效的image  

auto_now:每次保存對象時,自動設置該字段爲當前時間,用於最後一次修改的時間戳,它總是使用當前日期,默認爲false

auto_now_add:當對象第一次被創建時自動設置當前時間,用於創建的時間戳,它總是使用當前日期,默認爲false

2.2.2字段選項

適用於任何字段,可以實現字段的約束,在生成字段時通過方法的關鍵字參數指定。

可選參數 說明
null 如果True,Django將null在數據庫中存儲空值,默認是False。不要在字符串字段上使用。null是數據庫範疇的概念
blank 如果True,該字段允許爲空。默認是False。同null不同, 如果字段有blank=True,則表單驗證將允許輸入空值。如果字段有blank=False,則需要該字段。
db_column 用於此字段的數據庫列的名稱。如果沒有給出,Django將使用該字段的名稱。
unique 如果True,該字段在整個表格中必須是唯一的。
primary_key 如果True,此字段是模型的主鍵。
default 默認值 當前字段如果不給值則執行默認值

3.定義模型

我們可以在應用的models.py中定義模型:

from django.db import models
​
class 模型名(models.Model):
    屬性名 = models.字段名(字段選項/參數)
    .....
    class Meta:   #可選,任何非字段的設置可以寫到Meta中
        db_table = 'user'   #指定表名爲uesr
  • 數據庫的表名等於:應用名_模型名,如果想指定表名,可以在Meta中使用db_table指定

  • 如果沒有指定主鍵,Django將自動給表創建一個自增長主鍵id

    id = models.AutoField(primary_key=True)

     

下面我們定義幾個模型類

from django.db import models
from django.utils import timezone
​
#用戶類
class User(models.Model):
    uid = models.AutoField(primary_key=True)  #自增主鍵
    uname = models.CharField(max_length=60)
    password = models.CharField(max_length=32)
    user_type = ((1,'超級管理員'),(2,'普通用戶'))  #用戶自定義類型對應mysql的enum類型
    type = models.IntegerField(default=2,choices=user_type)
    regtime = models.DateTimeField(default=timezone.now)  #缺省值是當前時間
    ip = models.IntegerField(null=True)
    login_type = ((1,'允許登錄'),(2,'禁止登錄'))   #用戶自定義類型對應mysql的enum類型
    allowed = models.IntegerField(default=1,choices=login_type)
    email = models.CharField(max_length=100,null=True)
    memo = models.CharField(max_length=1000,null=True)
​
    class Meta:
        db_table = 'user'   #表名

4.激活模型

  • 創建遷移文件 (此刻表並沒有創建到庫中)

    $ python3 manage.py makemigrations

     

  • 執行遷移 (將模型創建到庫中)

    $ python3 manage.py migrate

     

然後在應用的migrations目錄中應該生成了遷移文件

├── app
│   ├── admin.py
│   ├── apps.py
│   ├── __init__.py
│   ├── migrations
│   │   ├── 0001_initial.py
│   │   ├── __init__.py

生成的表結構如下:

CREATE TABLE `user` (
  `uid` int(11) NOT NULL AUTO_INCREMENT,
  `uname` varchar(60) NOT NULL,
  `password` char(32) NOT NULL,
  `type` enum('超級管理員','普通用戶') DEFAULT '普通用戶',
  `regtime` datetime DEFAULT NULL,
  `ip` int(11) DEFAULT NULL,
  `allowed` enum('允許登錄','禁止登錄') DEFAULT '允許登錄',
  `email` varchar(100) DEFAULT NULL,
  `memo` varchar(1000) DEFAULT NULL,
  PRIMARY KEY (`uid`)
) 

注意:

  • 任何對字段或表的修改都需要重新遷移

5.模型的使用

我們可以再交互式Python shel環境中,使用Django提供的免費API。要調用Python shell,請使用以下命令:

$ python manage.py shell
Python 3.6.4 (default, Sep 13 2018, 02:27:17) 
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> 

5.1 增刪改

>>> from app.models.User import User   #導入User模型
>>> from  hashlib import md5
>>> user = User(uname='admin',password=md5(b'123').hexdigest())  #實例化一個新對象
>>> user.save()   #insert 插入數據庫
>>> user.uid      #顯示uid的值

 4
#在mysql中查詢新插入的記錄

mysql> select uid,uname,password,type,allowed from user;
+-----+--------------+----------------------------------+--------------+--------------+
| uid | uname        | password                         | type         | allowed      |
+-----+--------------+----------------------------------+--------------+--------------+
|   1 | dddddksdf    | bc9858af5f37d3cf147a3923c8b9a3d9 | 普通用戶     | 允許登錄     |
|   2 | tom123       | 59f2443a4317918ce29ad28a14e1bdb7 | 普通用戶     | 允許登錄     |
|   3 | 隔壁老王     | 1e9ce4a8f8c6b2baa00b1783de4b0ef0 | 普通用戶     | 允許登錄     |
|   4 | admin        | 202cb962ac59075b964b07152d234b70 | 普通用戶     | 允許登錄     |
+-----+--------------+----------------------------------+--------------+--------------+
4 rows in set (0.00 sec)
​
>>> u1 = User.objects.get(pk=1)  #獲取uid=1的記錄
>>> u1.uid
1
>>> u1.delete()       #刪除該記錄
(1, {'app.Comment': 0, 'app.User': 1})

​
#查詢mysql

mysql> select uid,uname,password,type,allowed from user;
+-----+--------------+----------------------------------+--------------+--------------+
| uid | uname        | password                         | type         | allowed      |
+-----+--------------+----------------------------------+--------------+--------------+
|   2 | tom123       | 59f2443a4317918ce29ad28a14e1bdb7 | 普通用戶     | 允許登錄     |
|   3 | 隔壁老王      | 1e9ce4a8f8c6b2baa00b1783de4b0ef0 | 普通用戶     | 允許登錄     |
|   4 | admin        | 202cb962ac59075b964b07152d234b70 | 普通用戶     | 允許登錄     |
+-----+--------------+----------------------------------+--------------+--------------+
3 rows in set (0.00 sec)
​
# 修改單條記錄
>>> u2 = User.objects.get(pk=2)
>>> u2.uname = '小甜甜'     #update  更新
>>> u2.save()

​
# 修改多條記錄
>>> users = User.objects.all()
>>> users.update(password=md5(b'345').hexdigest())
​
#查詢mysql
mysql> select uid,uname,password,type,allowed from user;
+-----+--------------+----------------------------------+--------------+--------------+
| uid | uname        | password                         | type         | allowed      |
+-----+--------------+----------------------------------+--------------+--------------+
|   2 | 小甜甜       | 59f2443a4317918ce29ad28a14e1bdb7 | 普通用戶     | 允許登錄     |
|   3 | 隔壁老王     | 1e9ce4a8f8c6b2baa00b1783de4b0ef0 | 普通用戶     | 允許登錄     |
|   4 | admin        | 202cb962ac59075b964b07152d234b70 | 普通用戶     | 允許登錄     |
+-----+--------------+----------------------------------+--------------+--------------+
3 rows in set (0.00 sec)
  • 數據的邏輯刪除

    對於重要數據,一般不會直接刪除,會在表中增加一個字段比如:is_deleted,如果刪除的話,將這個字段置爲True,以後查詢的時候不在查詢,這種操作稱爲邏輯刪除

5.2 數據查詢

要從數據庫檢索數據,首先要獲取一個查詢集(QuerySet),查詢集表示從數據庫獲取的對象集合,它可以有零個,一個或多個過濾器。返回查詢集的方法,稱爲過濾器,過濾器根據給定的參數縮小查詢結果範圍,相當於sql語句中where或limit。

  • 在管理器上調用過濾器方法會返回查詢集

  • 查詢集經過過濾器篩選後返回新的查詢集,因此可以寫成鏈式過濾

  • 惰性執行:創建查詢集不會帶來任何數據庫的訪問,直到調用數據時,纔會訪問數據庫

  • 以下對查詢集求值:迭代、切片、序列化、與if合用、repr()/print()/len()/list()/bool()

管理器的方法 返回類型 說明
模型類.objects.all() QuerySet 返回表中所有數據
模型類.objects.filter() QuerySet 返回符合條件的數據
模型類.objects.exclude() QuerySet 返回不符合條件的數據
模型類.objects.order_by() QuerySet 對查詢結果集進行排序
模型類.objects.values() QuerySet 返回一個列表 每個元素爲一個字典
模型類.objects.reverse() QuerySet 對排序的結果反轉
模型類.objects.get() 模型對象 返回一個滿足條件的對象;<br/>如果沒有找到符合條件的對象,會引發模型類.DoesNotExist異常; <br/>如果找到多個,會引發模型類.MultiObjectsReturned 異常
模型類.objects.count() int 返回查詢集中對象的數目
模型類.objects.first() 模型對象 返回第一條數據
模型類.objects.last() 模型對象 返回最後一條數據
模型類.objects.exists() bool 判斷查詢的數據是否存在

5.2.1 返回查詢集

  • all()

    # 獲取所有數據,對應SQL:select * from User
    User.objects.all()
  • filter(**kwargs) 返回QuerySet包含與給定查找參數匹配的新查詢集。

    #等價sql:select * from User
    User.objects.filter()
    ​
    #等價sql:select * from User where uname = 'admin'
    User.objects.filter(uname='admin')
    ​
    #等級sql:select * from User where uid > 1 and type = 2
    User.objects.filter(uid__gt=1,type=2)
    ​
    #鏈式調用,等價於User.objects.filter(uid__gt=1,type=2)
    User.objects.filter(uid__gt=1).filter(type=2)

     

  • exclude(**kwargs)

    # 不匹配,對應SQL:select * from User where name != 'admin'
    User.objects.exclude(name='admin')
  • order_by(*fields)

    • 參數是字段名,可以有多個字段名,默認是升序。

    • 如果要按某個字段降序,在字段名前加'-': "-uid"表示按uid降序排列

  #按uid升序排列  等價於 order by uid
  User.objects().order_by('uid')
​
  #按uid降序排列  等價於 order by uid desc
  User.objects.order_by('-uid')
​
  #多列排序 等價於 order by password,uid desc
  User.objects.order_by('password','-uid')
  • values()

    #返回所有字段
    User.objects.values()
    #返回數據:
    [{'uid': 2, 'uname': '小甜甜', 'password': '59f2443a4317918ce29ad28a14e1bdb7'type': '普通用戶', 'regtime': None, 'ip': None, 'allowed': '允許登錄', 'email': None, None},...]
     
    #返回指定字段
    User.objects.values('uname','password')
    [{'uname': '小甜甜', 'password': '59f2443a4317918ce29ad28a14e1bdb7'},...]

     

  • reverse()

    User.objects.order_by('id').reverse()  降序
    User.objects.order_by('-id').reverse()  升序

     

  • distinct() 去重

    User.objects.values('password').distinct()

5.2.2 返回單個值

下面這些方法後面不能再跟其他過濾方法,因爲他們不返回查詢集

  • get() 只匹配一條數據

    u = User.objects.get(pk=1)  #正常
    u = User.objects.get(uid__gt=20) #MultipleObjectsReturned 匹配到了多條數據
    u = User.objects.get(uid__lt=-1) #DoesNotExist 匹配失敗

     

  • first()和last()

    User.objects.all().first()  #返回結果集中第一條數據
    User.objects.all().last()   #返回結果集中最後一條數據

     

  • count()

    • 返回結果集記錄數目,等價於select count(*)

    • 不會返回整個結果集,相比len方法更有效

    User.objects.count()
  • exists()

    • 判斷查詢集中是否有記錄,有返回True,否則返回False

      User.objects.filter(uid=3).exists()

5.2.3查詢集限制

查詢集類似列表,可以使用下標進行限制,類似sql語句中的limit子句。但索引不能是負數

  • 索引

  • 切片

User.objects.all()[0]  #等同於:limit 0,1
User.objects.all()[2]  #等同於:limit 2,1
User.objects.all()[0:2] #等同於limit 2
User.objects.all()[:2] #等同於limit 2
User.objects.all()[::2] 

5.2.4 字段查詢

相當於sql語句中where子句,它可以爲filter、exclude和get方法提供參數。

屬性名稱__比較運算符=值   #是兩個下劃線

 

操作符 說明 示例
exact/= 精確判等 uname = 'admin' <br>uname__exact = 'admin'<br>uname__exact = None #uname is null
iexact 不區分大小寫判等 name__iexact = 'admin'
contains 模糊查詢,等價like '%值%' uname__contains = 'admin'
contains 不區分大小寫的模糊查詢 uname__icontains = 'Admin'
startswith 以..開頭 uname__startswith = 'a'
istartswith (不區分大小寫)以...開頭 uname__istartswith = 'a'
endswith 以...結尾 uname__endswith = 'm'
iendswith (不區分大小寫)以...結尾 uname__iendswith = 'm'
isnull 判空(等價 = None) uname__isnull = True #等價 uname is null<br>unam__isnull = False #等價 uname is not null
in 包含 uid__in = [1,2,3] #in後面必須是可迭代對象
range 範圍測試(相當between and) uid__range = [2,5] #uid >=2 and cid <=5
gt/gte 大於/大於等於 uid__gt = 2
lt/lte 小於/小於等於 uid__lte = 2
regex 正則匹配 uname__regex= r'^a'
iregex 不區分大小寫的正則匹配 uname__iregex= r'^a'
  • in後面可以跟一個子查詢,但要求子查詢只能返回一個字段

    User.objects.filter(uid__in = (2,3,4))
    User.objects.filter(uid__in = [2,3,4])
    res = User.objects.filter(uid__gt = 1).values('uid')  #查詢集中只有一個字段uid
    User.objects.filter(uid__in = res

     

  • 日期查詢

    • year、month、day、week_day、hour、minute、second

     #等價sql: select * from User where year(regtime) = 2018
     User.objects.filter(regtime__year = 2018) 

5.2.5 統計查詢

需要先導入模塊:

from django.db.models import Max,Min,Sum,Avg,Count
  • 聚合查詢:對多行查詢結果的一列進行操作

#統計記錄總數: select count(*) from user
User.objects.aggregate(Count('uid'))    #{'uid__count': 4}
User.objects.aggregate(Max('uid'))      #{'uid__max': 5}
User.objects.aggregate(Min('uid'))      #{'uid__min': 2}
  • 分組統計

#等價sql: select type,count(*) from user group by type
res = User.objects.values('type').annotate(Count('uid'))
#統計結果: [{'type': '普通用戶', 'uid__count': 3}, {'type': '超級管理員', 'uid__count': 1}]

#查看生成的sql語句
print(res.query)
#SELECT `user`.`type`, COUNT(`user`.`uid`) AS `uid__count` FROM `user` GROUP BY `user`.`type` ORDER BY NULL


 

5.2.6 Q對象和F對象

需要先導入模塊:

from django.db.models import Q,F


  • Q對象可以對關鍵字參數進行封裝,從而更好的應用多個查詢,可以組合&(and)、|(or)、~(not)操作符。

#原生sql:select * from user where uid = 2 or uid = 3
User.objects.filter(Q(uid=2)|Q(uid=3))
User.objects.filter(Q(uid__gte=2) & Q(uid__lte=3))
User.objects.filter(~Q(uid__gte=2))


  • F對象:用於比較表中兩個字段

#等價sql:select * from user where uid < type
User.objects.filter(uid__lte = F('type'))


 

6.模型成員

​ 模型類和數據庫中表對應,模型類的對象和記錄對象,模型類本身沒有數據庫訪問功能,但模型類中有一個Manager類的對象,通過管理器對象可以實現和數據庫的訪問。

​ 當我們沒有爲模型類定義管理器時,Django會爲模型類生成一個名爲objects的管理器,自定義管理器後,Django不再生成默認管理器objects。

​ 管理器是Django的模型進行數據庫操作的接口,Django應用的每個模型都擁有至少一個管理器。Django支持自定義管理器類,繼承自models.Manager。

自定義管理器類主要用於兩種情況:

  • 修改原始查詢集

  • 向管理器類中添加額外的方法,如向數據庫中插入數據。

6.1 重命名管理器

在模型類中自定義一個新的管理器,則原有的objects管理器不會存在,以後對數據庫的操作使用自己定義的管理器

#模型類
class Artical(models.Model):
    aid = models.AutoField(primary_key=True)
    title = models.CharField(max_length=100,null=False)
    content = models.TextField(max_length=10000,null=True)
    ....
    art_manager = models.Manager()  #自定義了一個新的管理器,名字爲art_manager
    
#views
def article_get(request):
    articles = Artical.art_manager.all()   #使用art_manager進行查詢
    return render(request,"articlelist.html",context={'articls':articles})

6.2 自定義管理器

  • 修改原始查詢集(由all()獲取的查詢集)

    • 修改管理器的get_queryset方法,可以改變all方法返回的原始查詢集

    #首先自定義Manager的子類
    class ArticleManager(models.Manager):
        def get_queryset(self):
            return super().get_queryset().filter(ispublished=True)  #獲取已發表的文章
    #模型類
    class Artical(models.Model):
       ....
        objects = models.Manager()  #可以有多個管理器
        publishManager = ArticleManager()
        
    #views
    def article_publish(request):
        published = Artical.publishManager.all()
        return HttpResponse("已經發表{}".format(published[0].title))

     

  • 給管理器類中添加額外的方法

    class ArticleManager(models.Manager):
        def get_queryset(self):
            return super().get_queryset().filter(ispublished=True)
        #新增一個創建對象的方法
        def create(self,title,content,publishingdate,comments,likenum,ispublished):
            article = Artical()
            article.title = title
            article.content = content
            article.publishingdate = publishingdate
            article.comments = comments
            article.likenum = likenum
            article.ispublished = ispublished
            article.save()
            return article
    #views.py中
    def article_add(request):
         # art = Artical(title='小時代',content="混亂的邏輯")
         # art.save()
         Artical.publishManager.create('中美貿易戰','川樸一豆比','2018-10-8',0,0,1)
         return HttpResponse("保存成功")

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章