python django -2
阿新 • • 發佈:2017-07-24
進行 txt meta html模板 password ack 創建索引 list fault
ORM簡介
- MVC框架中包括一個重要的部分,就是ORM,它實現了數據模型與數據庫的解耦,即數據模型的設計不需要依賴於特定的數據庫,通過簡單的配置就可以輕松更換數據庫
- ORM是“對象-關系-映射”的簡稱,主要任務是:
- 根據對象的類型生成表結構
- 將對象、列表的操作,轉換為sql語句
- 將sql查詢到的結果轉換為對象、列表
- 這極大的減輕了開發人員的工作量,不需要面對因數據庫變更而導致的無效勞動
- Django中的模型包含存儲數據的字段和約束,對應著數據庫中唯一的表
使用MySql數據庫
- 在虛擬環境中安裝mysql包
pip install mysql-python
- 在mysql中創建數據庫
create databases test2 charset=utf8
- 打開settings.py文件,修改DATABASES項
DATABASES = { ‘default‘: { ‘ENGINE‘: ‘django.db.backends.mysql‘, ‘NAME‘: ‘test2‘, ‘USER‘: ‘用戶名‘, ‘PASSWORD‘: ‘密碼‘, ‘HOST‘: ‘數據庫服務器ip,本地可以使用localhost‘, ‘PORT‘: ‘端口,默認為3306‘, } }
開發流程
- 在models.py中定義模型類,要求繼承自models.Model
- 把應用加入settings.py文件的installed_app項
- 生成遷移文件
- 執行遷移生成表
- 使用模型類進行crud操作
使用數據庫生成模型類
python manage.py inspectdb > booktest/models.py
定義模型
- 在模型中定義屬性,會生成表中的字段
- django根據屬性的類型確定以下信息:
- 當前選擇的數據庫支持字段的類型
- 渲染管理表單時使用的默認html控件
- 在管理站點最低限度的驗證
- django會為表增加自動增長的主鍵列,每個模型只能有一個主鍵列,如果使用選項設置某屬性為主鍵列後,則django不會再生成默認的主鍵列
- 屬性命名限制
- 不能是python的保留關鍵字
- 由於django的查詢方式,不允許使用連續的下劃線
定義屬性
- 定義屬性時,需要字段類型
- 字段類型被定義在django.db.models.fields目錄下,為了方便使用,被導入到django.db.models中
- 使用方式
- 導入from django.db import models
- 通過models.Field創建字段類型的對象,賦值給屬性
- 對於重要數據都做邏輯刪除,不做物理刪除,實現方法是定義isDelete屬性,類型為BooleanField,默認值為False
字段類型
- 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, and default 這些設置是相互排斥的,他們之間的任何組合將會發生錯誤的結果
- TimeField:使用Python的datetime.time實例表示的時間,參數同DateField
- DateTimeField:使用Python的datetime.datetime實例表示的日期和時間,參數同DateField
- FileField:一個上傳文件的字段
- ImageField:繼承了FileField的所有屬性和方法,但對上傳的對象進行校驗,確保它是個有效的image
字段選項
- 通過字段選項,可以實現對字段的約束
- 在字段對象時通過關鍵字參數指定
- null:如果為True,Django 將空值以NULL 存儲到數據庫中,默認值是 False
- blank:如果為True,則該字段允許為空白,默認值是 False
- 對比:null是數據庫範疇的概念,blank是表單驗證證範疇的
- db_column:字段的名稱,如果未指定,則使用屬性的名稱
- db_index:若值為 True, 則在表中會為此字段創建索引
- default:默認值
- primary_key:若為 True, 則該字段會成為模型的主鍵字段
- unique:如果為 True, 這個字段在表中必須有唯一值
關系
- 關系的類型包括
- ForeignKey:一對多,將字段定義在多的端中
- ManyToManyField:多對多,將字段定義在兩端中
- OneToOneField:一對一,將字段定義在任意一端中
- 可以維護遞歸的關聯關系,使用‘self‘指定,詳見“自關聯”
- 用一訪問多:對象.模型類小寫_set
bookinfo.heroinfo_set
- 用一訪問一:對象.模型類小寫
heroinfo.bookinfo
- 訪問id:對象.屬性_id
heroinfo.book_id
元選項
- 在模型類中定義類Meta,用於設置元信息
- 元信息db_table:定義數據表名稱,推薦使用小寫字母,數據表的默認名稱
<app_name>_<model_name>
- 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)
類的屬性
- objects:是Manager類型的對象,用於與數據庫進行交互
- 當定義模型類時沒有指定管理器,則Django會為模型類提供一個名為objects的管理器
- 支持明確指定模型類的管理器
class BookInfo(models.Model):
...
books = models.Manager()
- 當為模型類指定管理器後,django不再為模型類生成名為objects的默認管理器
管理器Manager
- 管理器是Django的模型進行數據庫的查詢操作的接口,Django應用的每個模型都擁有至少一個管理器
- 自定義管理器類主要用於兩種情況
- 情況一:向管理器類中添加額外的方法:見下面“創建對象”中的方式二
- 情況二:修改管理器返回的原始查詢集:重寫get_queryset()方法
class BookInfoManager(models.Manager):
def get_queryset(self):
return super(BookInfoManager, self).get_queryset().filter(isDelete=False)
class BookInfo(models.Model):
...
books = BookInfoManager()
創建對象
- 當創建對象時,django不會對數據庫進行讀寫操作
- 調用save()方法才與數據庫交互,將對象保存到數據庫中
- 使用關鍵字參數構造模型對象很麻煩,推薦使用下面的兩種之式
- 說明: _init _方法已經在基類models.Model中使用,在自定義模型中無法使用,
- 方式一:在模型類中增加一個類方法
class BookInfo(models.Model):
...
@classmethod
def create(cls, title, pub_date):
book = cls(btitle=title, bpub_date=pub_date)
book.bread=0
book.bcommet=0
book.isDelete = False
return book
引入時間包:from datetime import *
調用:book=BookInfo.create("hello",datetime(1980,10,11));
保存:book.save()
- 方式二:在自定義管理器中添加一個方法
- 在管理器的方法中,可以通過self.model來得到它所屬的模型類
class BookInfoManager(models.Manager):
def create_book(self, title, pub_date):
book = self.model()
book.btitle = title
book.bpub_date = pub_date
book.bread=0
book.bcommet=0
book.isDelete = False
return book
class BookInfo(models.Model):
...
books = BookInfoManager()
調用:book=BookInfo.books.create_book("abc",datetime(1980,1,1))
保存:book.save()
- 在方式二中,可以調用self.create()創建並保存對象,不需要再手動save()
class BookInfoManager(models.Manager):
def create_book(self, title, pub_date):
book = self.create(btitle = title,bpub_date = pub_date,bread=0,bcommet=0,isDelete = False)
return book
class BookInfo(models.Model):
...
books = BookInfoManager()
調用:book=Book.books.create_book("abc",datetime(1980,1,1))
查看:book.pk
實例的屬性
- DoesNotExist:在進行單個查詢時,模型的對象不存在時會引發此異常,結合try/except使用
實例的方法
- str (self):重寫object方法,此方法在將對象轉換成字符串時會被調用
- save():將模型對象保存到數據表中
- delete():將模型對象從數據表中刪除
簡介
- 查詢集表示從數據庫中獲取的對象集合
- 查詢集可以含有零個、一個或多個過濾器
- 過濾器基於所給的參數限制查詢的結果
- 從Sql的角度,查詢集和select語句等價,過濾器像where和limit子句
- 接下來主要討論如下知識點
- 查詢集
- 字段查詢:比較運算符,F對象,Q對象
查詢集
- 在管理器上調用過濾器方法會返回查詢集
- 查詢集經過過濾器篩選後返回新的查詢集,因此可以寫成鏈式過濾
- 惰性執行:創建查詢集不會帶來任何數據庫的訪問,直到調用數據時,才會訪問數據庫
- 何時對查詢集求值:叠代,序列化,與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
- get():返回單個滿足條件的對象
限制查詢集
- 查詢集返回列表,可以使用下標的方式進行限制,等同於sql中的limit和offset子句
- 註意:不支持負數索引
- 使用下標後返回一個新的查詢集,不會立即執行查詢
- 如果獲取一個對象,直接使用[0],等同於[0:1].get(),但是如果沒有數據,[0]引發IndexError異常,[0:1].get()引發DoesNotExist異常
查詢集的緩存
- 每個查詢集都包含一個緩存來最小化對數據庫的訪問
- 在新建的查詢集中,緩存為空,首次對查詢集求值時,會發生數據庫查詢,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])
- 何時查詢集不會被緩存:當只對查詢集的部分進行求值時會檢查緩存,但是如果這部分不在緩存中,那麽接下來查詢返回的記錄將不會被緩存,這意味著使用索引來限制查詢集將不會填充緩存,如果這部分數據已經被緩存,則直接使用緩存中的數據
字段查詢
- 實現where子名,作為方法filter()、exclude()、get()的參數
- 語法:屬性名稱__比較運算符=值
- 表示兩個下劃線,左側是屬性名稱,右側是比較類型
- 對於外鍵,使用“屬性名_id”表示外鍵的原始值
- 轉義:like語句中使用了%與,匹配數據中的%與,在過濾器中直接寫,例如:filter(title__contains="%")=>where title like ‘%\%%‘,表示查找標題中包含%的
比較運算符
- exact:表示判等,大小寫敏感;如果沒有寫“ 比較運算符”,表示判等
filter(isDelete=False)
- contains:是否包含,大小寫敏感
exclude(btitle__contains=‘傳‘)
- startswith、endswith:以value開頭或結尾,大小寫敏感
exclude(btitle__endswith=‘傳‘)
- isnull、isnotnull:是否為null
filter(btitle__isnull=False)
- 在前面加個i表示不區分大小寫,如iexact、icontains、istarswith、iendswith
- in:是否包含在範圍內
filter(pk__in=[1, 2, 3, 4, 5])
- gt、gte、lt、lte:大於、大於等於、小於、小於等於
filter(id__gt=3)
- year、month、day、week_day、hour、minute、second:對日期間類型的屬性進行運算
filter(bpub_date__year=1980)
filter(bpub_date__gt=date(1980, 12, 31))
- 跨關聯關系的查詢:處理join查詢
- 語法:模型類名 <屬性名> <比較>
- 註:可以沒有__<比較>部分,表示等於,結果同inner join
- 可返向使用,即在關聯的兩個模型中都可以使用
filter(heroinfo_ _hcontent_ _contains=‘八‘)
- 查詢的快捷方式:pk,pk表示primary key,默認的主鍵是id
filter(pk__lt=6)
聚合函數
- 使用aggregate()函數返回聚合函數的值
- 函數:Avg,Count,Max,Min,Sum
from django.db.models import Max
maxDate = list.aggregate(Max(‘bpub_date‘))
- count的一般用法:
count = list.count()
F對象
- 可以使用模型的字段A與字段B進行比較,如果A寫在了等號的左邊,則B出現在等號的右邊,需要通過F對象構造
list.filter(bread__gte=F(‘bcommet‘))
- django支持對F()對象使用算數運算
list.filter(bread__gte=F(‘bcommet‘) * 2)
- F()對象中還可以寫作“模型類__列名”進行關聯查詢
list.filter(isDelete=F(‘heroinfo__isDelete‘))
- 對於date/time字段,可與timedelta()進行運算
list.filter(bpub_date__lt=F(‘bpub_date‘) + timedelta(days=1))
Q對象
- 過濾器的方法中關鍵字參數查詢,會合並為And進行
- 需要進行or查詢,使用Q()對象
- Q對象(django.db.models.Q)用於封裝一組關鍵字參數,這些關鍵字參數與“比較運算符”中的相同
from django.db.models import Q
list.filter(Q(pk_ _lt=6))
- Q對象可以使用&(and)、|(or)操作符組合起來
- 當操作符應用在兩個Q對象時,會產生一個新的Q對象
list.filter(pk_ _lt=6).filter(bcommet_ _gt=10)
list.filter(Q(pk_ _lt=6) | Q(bcommet_ _gt=10))
- 使用~(not)操作符在Q對象前表示取反
list.filter(~Q(pk__lt=6))
- 可以使用&|~結合括號進行分組,構造做生意復雜的Q對象
- 過濾器函數可以傳遞一個或多個Q對象作為位置參數,如果有多個Q對象,這些參數的邏輯為and
- 過濾器函數可以混合使用Q對象和關鍵字參數,所有參數都將and在一起,Q對象必須位於關鍵字參數的前面
自連接
- 對於地區信息,屬於一對多關系,使用一張表,存儲所有的信息
- 類似的表結構還應用於分類信息,可以實現無限級分類
- 新建模型AreaInfo,生成遷移
class AreaInfo(models.Model):
atitle = models.CharField(max_length=20)
aParent = models.ForeignKey(‘self‘, null=True, blank=True)
- 訪問關聯對象
上級對象:area.aParent
下級對象:area.areainfo_set.all()
- 加入測試數據(在workbench中,參見“省市區mysql.txt”)
- 在booktest/views.py中定義視圖area
from models import AreaInfo
def area(request):
area = AreaInfo.objects.get(pk=130100)
return render(request, ‘booktest/area.html‘, {‘area‘: area})
- 定義模板area.html
<!DOCTYPE html>
<html>
<head>
<title>地區</title>
</head>
<body>
當前地區:{{area.atitle}}
<hr/>
上級地區:{{area.aParent.atitle}}
<hr/>
下級地區:
<ul>
{ %for a in area.areainfo_set.all%}
<li>{{a.atitle}}</li>
{ %endfor%}
</ul>
</body>
</html>
- 在booktest/urls.py中配置一個新的urlconf
urlpatterns = [
url(r‘^area/$‘, views.area, name=‘area‘)
]
python django -2