該系列教程系個人原創,並完整發布在個人官網劉江的博客和教程
所有轉載本文者,需在頂部顯著位置注明原作者及www.liujiangblog.com官網地址。
一個模型(model)就是一個單獨的、確定的數據的信息源,包含了數據的字段和操作方法。通常,每個模型映射為一張數據庫中的表。
基本的原則如下:
- 每個模型在Django中的存在形式為一個Python類
- 每個模型都是django.db.models.Model的子類
- 模型的每個字段(屬性)代表數據表的某一列
- Django將自動為你生成數據庫訪問API
簡單示例:
下面的模型定義了一個“人”,它具有first_name和last_name字段:
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
每一個字段都是一個類屬性,每個類屬性表示數據表中的一個列。
上面的代碼,相當於下面的原生SQL語句:
CREATE TABLE myapp_person (
"id" serial NOT NULL PRIMARY KEY,
"first_name" varchar(30) NOT NULL,
"last_name" varchar(30) NOT NULL
);
注意:
- 表名
myapp_person由Django自動生成,默認格式為“項目名稱+下划線+小寫類名”,你可以重寫這個規則。 - Django默認自動創建自增主鍵
id,當然,你也可以自己指定主鍵。 - 上面的SQL語句基於
PostgreSQL語法。
通常,我們會將模型編寫在其所屬app下的models.py文件中,沒有特別需求時,請堅持這個原則,不要自己給自己添加麻煩。
創建了模型之后,在使用它之前,你需要先在settings文件中的INSTALLED_APPS 處,注冊models.py文件所在的myapp。看清楚了,是注冊app,不是模型,也不是models.py。如果你以前寫過模型,可能已經做過這一步工作,可跳過。
INSTALLED_APPS = [
#...
'myapp',
#...
]
當你每次對模型進行增、刪、修改時,請務必執行命令python manage.py migrate,讓操作實際應用到數據庫上。這里可以選擇在執行migrate之前,先執行python manage.py makemigrations讓修改動作保存到記錄文件中,方便github等工具的使用。
模型字段fields
字段是模型中最重要的內容之一,也是唯一必須的部分。字段在Python中表現為一個類屬性,體現了數據表中的一個列。請不要使用clean、save、delete等Django內置的模型API名字,防止命名沖突。下面是一個展示,注意字段的寫法:
from django.db import models
class Musician(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
instrument = models.CharField(max_length=100)
class Album(models.Model):
artist = models.ForeignKey(Musician, on_delete=models.CASCADE)
name = models.CharField(max_length=100)
release_date = models.DateField()
num_stars = models.IntegerField()
字段命名約束:
Django不允許下面兩種字段名:
-
與Python關鍵字沖突。這會導致語法錯誤。例如:
class Example(models.Model):
pass = models.IntegerField() # 'pass'是Python保留字! -
字段名中不能有兩個以上下划線在一起,因為兩個下划線是Django的查詢語法。例如:
class Example(models.Model):
foo__bar = models.IntegerField() # 'foo__bar' 有兩個下划線在一起!
由於你可以自定義表名、列名,上面的規則可能被繞開,但是請養成良好的習慣,一定不要那么起名。
SQL語言的join、where和select等保留字可以作為字段名,因為Django對它們都進行了轉義。
常用字段類型
字段類型的作用:
- 決定數據庫中對應列的數據類型(例如:INTEGER, VARCHAR, TEXT)
- HTML中對應的表單標簽的類型,例如
<input type=“text” /> - 在admin后台和自動生成的表單中最小的數據驗證需求
Django內置了許多字段類型,它們都位於django.db.models中,例如models.CharField。這些類型基本滿足需求,如果還不夠,你也可以自定義字段。
下表列出了所有Django內置的字段類型,但不包括關系字段類型(字段名采用駝峰命名法,初學者請一定要注意):
| 類型 | 說明 |
|---|---|
| AutoField | 一個自動增加的整數類型字段。通常你不需要自己編寫它,Django會自動幫你添加字段:id = models.AutoField(primary_key=True),這是一個自增字段,從1開始計數。如果你非要自己設置主鍵,那么請務必將字段設置為primary_key=True。Django在一個模型中只允許有一個自增字段,並且該字段必須為主鍵! |
| BigAutoField | (1.10新增)64位整數類型自增字段,數字范圍更大,從1到9223372036854775807 |
| BigIntegerField | 64位整數字段(看清楚,非自增),類似IntegerField ,-9223372036854775808 到9223372036854775807。在Django的模板表單里體現為一個textinput標簽。 |
| BinaryField | 二進制數據類型。使用受限,少用。 |
| BooleanField | 布爾值類型。默認值是None。在HTML表單中體現為CheckboxInput標簽。如果要接收null值,請使用NullBooleanField。 |
| CharField | 字符串類型。必須接收一個max_length參數,表示字符串長度不能超過該值。默認的表單標簽是input text。最常用的filed,沒有之一! |
| CommaSeparatedIntegerField | 逗號分隔的整數類型。必須接收一個max_length參數。常用於表示較大的金額數目,例如1,000,000元。 |
| DateField | class DateField(auto_now=False, auto_now_add=False, **options)日期類型。一個Python中的datetime.date的實例。在HTML中表現為TextInput標簽。在admin后台中,Django會幫你自動添加一個JS的日歷表和一個“Today”快捷方式,以及附加的日期合法性驗證。兩個重要參數:(參數互斥,不能共存) auto_now:每當對象被保存時將字段設為當前日期,常用於保存最后修改時間。auto_now_add:每當對象被創建時,設為當前日期,常用於保存創建日期(注意,它是不可修改的)。設置上面兩個參數就相當於給field添加了editable=False和blank=True屬性。如果想具有修改屬性,請用default參數。例子:pub_time = models.DateField(auto_now_add=True),自動添加發布時間。 |
| DateTimeField | 日期時間類型。Python的datetime.datetime的實例。與DateField相比就是多了小時、分和秒的顯示,其它功能、參數、用法、默認值等等都一樣。 |
| DecimalField | 固定精度的十進制小數。相當於Python的Decimal實例,必須提供兩個指定的參數!參數max_digits:最大的位數,必須大於或等於小數點位數 。decimal_places:小數點位數,精度。 當localize=False時,它在HTML表現為NumberInput標簽,否則是text類型。例子:儲存最大不超過999,帶有2位小數位精度的數,定義如下:models.DecimalField(..., max_digits=5, decimal_places=2)。 |
| DurationField | 持續時間類型。存儲一定期間的時間長度。類似Python中的timedelta。在不同的數據庫實現中有不同的表示方法。常用於進行時間之間的加減運算。但是小心了,這里有坑,PostgreSQL等數據庫之間有兼容性問題! |
| EmailField | 郵箱類型,默認max_length最大長度254位。使用這個字段的好處是,可以使用DJango內置的EmailValidator進行郵箱地址合法性驗證。 |
| FileField | class FileField(upload_to=None, max_length=100, **options)上傳文件類型,后面單獨介紹。 |
| FilePathField | 文件路徑類型,后面單獨介紹 |
| FloatField | 浮點數類型,參考整數類型 |
| ImageField | 圖像類型,后面單獨介紹。 |
| IntegerField | 整數類型,最常用的字段之一。取值范圍-2147483648到2147483647。在HTML中表現為NumberInput標簽。 |
| GenericIPAddressField | class GenericIPAddressField(protocol='both', unpack_ipv4=False, **options)[source],IPV4或者IPV6地址,字符串形式,例如192.0.2.30或者2a02:42fe::4在HTML中表現為TextInput標簽。參數protocol默認值為‘both’,可選‘IPv4’或者‘IPv6’,表示你的IP地址類型。 |
| NullBooleanField | 類似布爾字段,只不過額外允許NULL作為選項之一。 |
| PositiveIntegerField | 正整數字段,包含0,最大2147483647。 |
| PositiveSmallIntegerField | 較小的正整數字段,從0到32767。 |
| SlugField | slug是一個新聞行業的術語。一個slug就是一個某種東西的簡短標簽,包含字母、數字、下划線或者連接線,通常用於URLs中。可以設置max_length參數,默認為50。 |
| SmallIntegerField | 小整數,包含-32768到32767。 |
| TextField | 大量文本內容,在HTML中表現為Textarea標簽,最常用的字段類型之一!如果你為它設置一個max_length參數,那么在前端頁面中會受到輸入字符數量限制,然而在模型和數據庫層面卻不受影響。只有CharField才能同時作用於兩者。 |
| TimeField | 時間字段,Python中datetime.time的實例。接收同DateField一樣的參數,只作用於小時、分和秒。 |
| URLField | 一個用於保存URL地址的字符串類型,默認最大長度200。 |
| UUIDField | 用於保存通用唯一識別碼(Universally Unique Identifier)的字段。使用Python的UUID類。在PostgreSQL數據庫中保存為uuid類型,其它數據庫中為char(32)。這個字段是自增主鍵的最佳替代品,后面有例子展示。 |
這里有如何上傳文件和圖片的方法:
1.FileField:
class FileField(upload_to=None, max_length=100, **options)[source]
上傳文件字段(不能設置為主鍵)。默認情況下,該字段在HTML中表現為一個ClearableFileInput標簽。在數據庫內,我們實際保存的是一個字符串類型,默認最大長度100,可以通過max_length參數自定義。真實的文件是保存在服務器的文件系統內的。
重要參數upload_to用於設置上傳地址的目錄和文件名。如下例所示:
class MyModel(models.Model):
# 文件被傳至`MEDIA_ROOT/uploads`目錄,MEDIA_ROOT由你在settings文件中設置
upload = models.FileField(upload_to='uploads/')
# 或者
# 被傳到`MEDIA_ROOT/uploads/2015/01/30`目錄,增加了一個時間划分
upload = models.FileField(upload_to='uploads/%Y/%m/%d/')
Django很人性化地幫我們實現了根據日期生成目錄的方式!
upload_to參數也可以接收一個回調函數,該函數返回具體的路徑字符串,如下例:
def user_directory_path(instance, filename):
#文件上傳到MEDIA_ROOT/user_<id>/<filename>目錄中
return 'user_{0}/{1}'.format(instance.user.id, filename)
class MyModel(models.Model):
upload = models.FileField(upload_to=user_directory_path)
例子中,user_directory_path這種回調函數,必須接收兩個參數,然后返回一個Unix風格的路徑字符串。參數instace代表一個定義了FileField的模型的實例,說白了就是當前數據記錄。filename是原本的文件名。
2. ImageField
class ImageField(upload_to=None, height_field=None, width_field=None, max_length=100, **options)[source]
用於保存圖像文件的字段。其基本用法和特性與FileField一樣,只不過多了兩個屬性height和width。默認情況下,該字段在HTML中表現為一個ClearableFileInput標簽。在數據庫內,我們實際保存的是一個字符串類型,默認最大長度100,可以通過max_length參數自定義。真實的圖片是保存在服務器的文件系統內的。
height_field參數:保存有圖片高度信息的模型字段名。
width_field參數:保存有圖片寬度信息的模型字段名。
使用Django的ImageField需要提前安裝pillow模塊,pip install pillow即可。
使用FileField或者ImageField字段的步驟:
- 在settings文件中,配置
MEDIA_ROOT,作為你上傳文件在服務器中的基本路徑(為了性能考慮,這些文件不會被儲存在數據庫中)。再配置個MEDIA_URL,作為公用URL,指向上傳文件的基本路徑。請確保Web服務器的用戶賬號對該目錄具有寫的權限。 - 添加FileField或者ImageField字段到你的模型中,定義好
upload_to參數,文件最終會放在MEDIA_ROOT目錄的“upload_to”子目錄中。 - 所有真正被保存在數據庫中的,只是指向你上傳文件路徑的字符串而已。可以通過url屬性,在Django的模板中方便的訪問這些文件。例如,假設你有一個ImageField字段,名叫
mug_shot,那么在Django模板的HTML文件中,可以使用{{ object.mug_shot.url }}來獲取該文件。其中的object用你具體的對象名稱代替。 - 可以通過
name和size屬性,獲取文件的名稱和大小信息。
安全建議
無論你如何保存上傳的文件,一定要注意他們的內容和格式,避免安全漏洞!務必對所有的上傳文件進行安全檢查,確保它們不出問題!如果你不加任何檢查就盲目的讓任何人上傳文件到你的服務器文檔根目錄內,比如上傳了一個CGI或者PHP腳本,很可能就會被訪問的用戶執行,這具有致命的危害。
3. FilePathField
class FilePathField(path=None, match=None, recursive=False, max_length=100, **options)[source]
一種用來保存文件路徑信息的字段。在數據表內以字符串的形式存在,默認最大長度100,可以通過max_length參數設置。
它包含有下面的一些參數:
path:必須指定的參數。表示一個系統絕對路徑。
match:可選參數,一個正則表達式,用於過濾文件名。只匹配基本文件名,不匹配路徑。例如foo.*\.txt$,只匹配文件名foo23.txt,不匹配bar.txt與foo23.png。
recursive:可選參數,只能是True或者False。默認為False。決定是否包含子目錄,也就是是否遞歸的意思。
allow_files:可選參數,只能是True或者False。默認為True。決定是否應該將文件名包括在內。它和allow_folders其中,必須有一個為True。
allow_folders: 可選參數,只能是True或者False。默認為False。決定是否應該將目錄名包括在內。
比如:
FilePathField(path="/home/images", match="foo.*", recursive=True)
它只匹配/home/images/foo.png,但不匹配/home/images/foo/bar.png,因為默認情況,只匹配文件名,而不管路徑是怎么樣的。
4. UUIDField:
數據庫無法自己生成uuid,因此需要如下使用default參數:
import uuid # Python的內置模塊
from django.db import models
class MyUUIDModel(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
# 其它字段
