Django資料庫--ORM模型
隨著專案越來越大,採用寫原生SQL的方式在程式碼中會出現大量的SQL語句,那麼問題就出現了:
- SQL語句重複利用率不高,越複雜的SQL語句條件越多,程式碼越長。會出現很多相近的SQL語句。
- 很多SQL語句是在業務邏輯中拼出來的,如果有資料庫需要更改,就要去修改這些邏輯,這會很容易漏掉對某些SQL語句的修改。
- 寫SQL時容易忽略web安全問題,給未來造成隱患。SQL注入。
ORM模型介紹
ORM,全稱Object Relational Mapping,中文叫做物件關係對映,通過ORM我們可以通過類的方式去操作資料庫,而不用再寫原生的SQL語句。通過把表對映成類,把行作例項,把欄位作為屬性,ORM在執行物件操作的時候最終還是會把對應的操作轉換為資料庫原生語句。使用ORM的優點:
- 易用性:使用ORM做資料庫的開發可以有效的減少重複SQL語句的概率,寫出來的模型也更加直觀、清晰。
- 效能損耗小:ORM轉換成底層資料庫操作指令確實會有一些開銷。但從實際的情況來看,這種效能損耗很少(不足5%),只要不是對效能有嚴苛的要求,綜合考慮開發效率、程式碼的閱讀性,帶來的好處要遠遠大於效能損耗,而且專案越大作用越明顯。
- 設計靈活:可以輕鬆的寫出複雜的查詢。
- 可移植性:Django封裝了底層的資料庫實現,支援多個關係資料庫引擎,包括流行的MySQL、PostgreSQL和SQLite。可以非常輕鬆的切換資料庫。
建立ORM模型
ORM模型一般都是放在app的models.py
settings.py
的INSTALLED_APP中進行安裝。以下是寫一個簡單的書籍ORM模型。在一個新建的專案中建立一個
book
的app,配置settings.py
,連線至mysql資料庫。
在models.py中寫入以下程式碼。
from django.db import models
#如果要將一個普通的類變成一個可以對映到資料庫中的ORM模型
#那麼必須將父類設定為models.Model或者他的子類。
class Book(models.Model):
# 1.id:int型別,自動增長,是主鍵
# 如果我們沒有設定主鍵,Django會自動給我們建立一個主鍵`id`,所以這句程式碼寫不寫都一樣。
id = models.AutoField(primary_key=True)
# 2.name:varchar(100),圖書的名字,不能為空
name = models.CharField(max_length=100,null=False)
# 3.author:varchar(100),圖書的作者,不能為空
author = models.CharField(max_length=100,null=False)
# 4.price:float型,圖書的價格,不能為空,預設為零
price = models.FloatField(null=False,default=0)
然後將book
這個app新增到settings中。
進入到manage.py的同級目錄,輸入命令
python manage.py makemigrations
這樣就生成了遷移指令碼的檔案
再執行命令
python manage.py migrate
將遷移指令碼檔案對映到資料庫中。
這樣我們就將ORM對映到資料庫中了,可以使用navicat檢視效果,因為我們沒有指定生成表的名字,所以表預設的名字是<app名>_<類名>
。
將ORM模型對映到資料庫中,總結起來就是以下幾步:
- 在settings.py中,配置好DATABASES,做好資料庫相關的配置。
- 在app中的models.py中定義好模型,這個模型必須繼承自django.db.models。
- 將這個app新增到settings.py的INSTALLED_APP中。
- 在命令列終端,進入到專案所在的路徑,然後執行命令 python manage.py makemigrations來生成遷移指令碼檔案。
- 同樣在命令列中,執行命令python manage.py migrate來將遷移指令碼檔案對映到資料庫中。
使用ORM模型
在上面我們已經建立好了我們的models,接下來我們就可以換使用models進行對資料庫的增刪改查了
在views中中寫入程式碼:
from django.shortcuts import render
from . import models
from django.http import HttpResponse
# Create your views here.
def index(request):
#1. 使用ORM新增一條資料到資料庫中
book = models.Book(name='三國演義',author='羅貫中',price=200)
book.save()
return HttpResponse('圖書插入成功')
然後配置urls等,執行專案,訪問網址,就將這條資訊插入到了資料庫中去。
from django.shortcuts import render
from . import models
from django.http import HttpResponse
# Create your views here.
def index(request):
# 1. 使用ORM新增一條資料到資料庫中
#book = models.Book(name='三國演義',author='羅貫中',price=200)
#book.save()
# 2. 查詢
# 2.1 使用主鍵進行查詢
book = models.Book.objects.get(pk=1)
print(book.name,book.author,book.price)
return HttpResponse('圖書插入成功')
然後在網頁上輸入網址,就能在控制檯中看到查詢到的資訊了
2.2 根據其他條件進行查詢
修改views中的程式碼
from django.shortcuts import render
from . import models
from django.http import HttpResponse
# Create your views here.
def index(request):
# 1. 使用ORM新增一條資料到資料庫中
#book = models.Book(name='三國演義',author='羅貫中',price=200)
#book.save()
# 2. 查詢
# 2.1 使用主鍵進行查詢
# book = models.Book.objects.get(pk=1)
# print(book.name,book.author,book.price)
# 2.2 根據其他條件進行查詢
# 這個方法返回的是一個列表,將所有滿足條件的資訊都放在列表裡面
# 即使只有一條滿足資訊,也是返回一個列表
books = models.Book.objects.filter(name='三國演義')
print(books[0].name,books[0].author,books[0].price)
return HttpResponse('圖書插入成功')
然後執行,也能在控制檯檢視到列印的資訊了。
修改views中的程式碼
from django.shortcuts import render
from . import models
from django.http import HttpResponse
# Create your views here.
def index(request):
# 1. 使用ORM新增一條資料到資料庫中
#book = models.Book(name='三國演義',author='羅貫中',price=200)
#book.save()
# 2. 查詢
# 2.1 使用主鍵進行查詢
# book = models.Book.objects.get(pk=1)
# print(book.name,book.author,book.price)
# 2.2 根據其他條件進行查詢
# 這個方法返回的是一個列表,將所有滿足條件的資訊都放在列表裡面
# 即使只有一條滿足資訊,也是返回一個列表
# books = models.Book.objects.filter(name='三國演義')
# print(books[0].name,books[0].author,books[0].price)
# 3. 刪除資料
book = models.Book.objects.get(pk = 1)
book.delete()
return HttpResponse('圖書插入成功')
執行專案,就將pk=1
這條資料刪除了。
因為我們的資料庫已經沒有資料了,剛才已經刪除了,所以我們先新增一條資料(上面第一點)
新增資料為name='紅樓夢',author='曹雪芹',price=100
然後我們修改views中的程式碼來進行修改資料中的資料
from django.shortcuts import render
from . import models
from django.http import HttpResponse
# Create your views here.
def index(request):
# 1. 使用ORM新增一條資料到資料庫中
#book = models.Book(name='三國演義',author='羅貫中',price=200)
#book.save()
# 2. 查詢
# 2.1 使用主鍵進行查詢
# book = models.Book.objects.get(pk=1)
# print(book.name,book.author,book.price)
# 2.2 根據其他條件進行查詢
# 這個方法返回的是一個列表,將所有滿足條件的資訊都放在列表裡面
# 即使只有一條滿足資訊,也是返回一個列表
# books = models.Book.objects.filter(name='三國演義')
# print(books[0].name,books[0].author,books[0].price)
# 3. 刪除資料
# book = models.Book.objects.get(pk = 1)
# book.delete()
# 4. 刪除資料
book = models.Book.objects.get(pk=2)
book.price = 200
book.save()
return HttpResponse('圖書插入成功')
這樣我們就將價錢修改為200了。
模型常用欄位
1. AutoField:
對映到資料庫中是int型別,可以有自動增長的特性。一般不需要使用這個型別。只有在我們需要自己指定一個主鍵時才會使用這個欄位。因為如果我們不指定主鍵,那麼Django會自動的生成一個叫做id的自動增長的主鍵。所以一般不用我們自己建立主鍵。(想要自定義主鍵,必須設定primary_key=True中,否則的話就是一個普通的資料)
2. BigAutoField:
64位的整形,類似於AutoField,只不過是產生的資料的範圍是從1-9223372036854775807。
3. BooleanField:
在模型層面接收的是True/False。在資料庫層面是tinyint型別。如果沒有指定預設值,預設值是None。
(一般情況下,在定義欄位的時候,如果沒有指定null=True,那麼預設的情況下,null=False,就是不能為空)
而這個欄位是不能為null的,如果想要使用可以為null的BooleanField
欄位,那麼應該使用NullBooleanField
來代替。
4.CharField:
在資料庫層面是varchar型別。在Python層面就是普通的字串。這個型別在使用的時候必須要指定最大的長度,也即必須要傳遞max_length這個關鍵字引數進去。
注意: 如果max_length超過255,就不建議使用這個欄位了,而是使用TextField,這個欄位在資料庫層面就是longtext型別。
在講時間相關的欄位前,先了解一些和時間相關的東西:
1.navie
時間和aware
時間:
1.1 navie時間
:不知道自己的時間表示的是哪個時區的。也就是不知道自己幾斤幾兩。比較幼稚。
1.2 aware時間
:知道自己的時間表示的是哪個時區的。也就是比較清醒。
2. pytz
庫:
專門用來處理時區的庫。這個庫會經常更新一些時區的資料,不需要我們擔心。並且這個庫在安裝Django的時候會預設的安裝。如果沒有安裝,那麼可以通過pip install pytz
的方式進行安裝。
3.astimezone
方法:
將一個時區的時間轉換為另外一個時區的時間。這個方法只能被aware
型別的時間呼叫。不能被navie
型別的時間呼叫。
示例程式碼如下:
import pytz
from datetime import datetime
now = datetime.now() # 這是一個navie型別的時間
utc_timezone = pytz.timezone("UTC") # 定義UTC的時區物件
utc_now = now.astimezone(utc_timezone) # 將當前的時間轉換為UTC時區的時間
>> 這個地方會丟擲一個異常:ValueError: astimezone() cannot be applied to a naive datetime
>>原因就是因為navie型別的時間不能呼叫astimezone方法,因為navie型別的時間都不知道自己時哪一個時區的。
# 下面的程式碼就能夠正確的轉換,因為我們使用了replace方法設定了時區,now就不是一個navie型別的時間了,知道自己是那一個時區。
now = now.replace(tzinfo=pytz.timezone('Asia/Shanghai'))
utc_now = now.astimezone(utc_timezone)
4. replace方法:
可以將一個時間的某些屬性進行更改。例如上面的修改時區。
5.django.utils.timezone.now方法:
會根據settings.py
中是否設定了USE_TZ=True
獲取當前的時間。如果設定了,那麼就獲取一個aware
型別的UTC
時間。如果沒有設定,那麼就會獲取一個navie
型別的時間。預設時設定的USE_TZ=True
。
6. django.utils.timezone.localtime方法:
會根據setting.py
中的TIME_ZONE
來將一個aware
型別的時間轉換為TIME_ZONE
指定時區的時間。
DateField:
日期型別。在Python
中是datetime.date
型別,可以記錄年月日。在對映到資料庫中也是date
型別。使用這個Field
可以傳遞以下幾個引數:
auto_now
:在每次這個資料儲存的時候,都使用當前的時間。比如作為一個記錄修改日期的欄位,可以將這個屬性設定為True
。auto_now_add
:在每次資料第一次被新增進去的時候,都使用當前的時間。比如作為一個記錄第一次入庫的欄位,可以將這個屬性設定為True
。(只有第一次次新增資料有效)
DateTimeField:
日期時間型別,類似於DateField
。不僅僅可以儲存日期,還可以儲存時間。對映到資料庫中是datetime
型別。這個Field
也可以使用auto_now
和auto_now_add
兩個屬性。
TimeField:
時間型別。在資料庫中是time
型別。在Python
中是datetime.time
型別。
navie和aware介紹以及在django中的用法:
https://docs.djangoproject.com/en/2.1/topics/i18n/timezones/
EmailField:
類似於CharField
。在資料庫底層也是一個varchar
型別。預設的長度為255字元,最大長度是254個字元。我們也可以設定最大長度。雖然是EmailField
欄位,但是並不能對輸入的字元竄進行判斷是否為正確的郵箱格式,隨便輸入字元竄都是可以的。
在資料庫層面不會限制字元竄一定要滿足郵箱格式,只是在以後使用ModelForm等表單相關操作時才會起作用。
FileField:
用來儲存檔案的。
ImageField:
用來儲存圖片檔案的。
FloatField:
浮點型別。對映到資料庫中是float
型別。
IntegerField:
整形。值的區間是-2147483648——2147483647
。
BigIntegerField:
大整形。值的區間是-9223372036854775808——9223372036854775807
。
PositiveIntegerField:
正整形。值的區間是0——2147483647
。
SmallIntegerField:
小整形。值的區間是-32768——32767
。
PositiveSmallIntegerField:
正小整形。值的區間是0——32767
。
TextField:
大量的文字型別。對映到資料庫中是longtext型別。
UUIDField:
只能儲存uuid
格式的字串。uuid
是一個32位的全球唯一的字串,一般用來作為主鍵。(UUID是什麼)
URLField:
類似於CharField
,只不過只能用來儲存url
格式的字串。並且預設的max_length
是200。
欄位常用引數
1.null:
如果設定為True,Django將會在對映表的時候指定是否為空。預設是為False。在使用字串相關的Field(CharField/TextField)的時候,官方推薦儘量不要使用這個引數,也就是保持預設值False。因為Django在處理字串相關的Field的時候,即使這個Field的null=False,如果你沒有給這個Field傳遞任何值,那麼Django也會使用一個空的字串""來作為預設值儲存進去。因此如果再使用null=True,Django會產生兩種空值的情形(NULL或者空字串)。如果想要在表單驗證的時候允許這個字串為空,那麼建議使用blank=True。如果你的Field是BooleanField,那麼對應的可空的欄位則為NullBooleanField。
2. blank:
標識這個欄位在表單驗證的時候是否可以為空。預設是False。
這個和null是有區別的,null是一個純資料庫級別的。而blank是表單驗證級別的。就是和資料庫沒有關係,是在瀏覽器上進行判斷的。
3. db_column:
這個欄位在資料庫中的名字。如果沒有設定這個引數,那麼將會使用模型中屬性的名字。
4. default:
預設值。可以為一個值,或者是一個函式,但是不支援lambda表示式。並且不支援列表/字典/集合等可變的資料結構。
5. primary_key:
是否為主鍵。預設是False。
6. unique:
在表中這個欄位的值是否唯一。一般是設定手機號碼/郵箱等。
更多Field引數請參考官方文件:https://docs.djangoproject.com/zh-hans/2.1/ref/models/fields/
模型中Meta配置
對於一些模型級別的配置。我們可以在模型中定義一個類,叫做Meta。然後在這個類中新增一些類屬性來控制模型的作用。比如我們想要在資料庫對映的時候使用自己指定的表名,而不是使用模型的名稱。那麼我們可以在Meta類中新增一個db_table的屬性。示例程式碼如下:
class Book(models.Model):
name = models.CharField(max_length=20,null=False)
desc = models.CharField(max_length=100,name='description',db_column="description1")
class Meta:
db_table = 'book_model'
1. db_table:
這個模型對映到資料庫中的表名。如果沒有指定這個引數,那麼在對映的時候將會使用模型名來作為預設的表名。
2. ordering:
設定在提取資料的排序方式。
更多的配置可以檢視官方文件:https://docs.djangoproject.com/en/2.1/ref/models/options/