參考博客
Django 文檔 | Django 文檔 | Django
the5fire
表關(guān)系中的常用字段:on_delete=models.CASCADE窘俺,F(xiàn)oreignKey,db_constraint对途,related_name
執(zhí)行python manage.py migrate 沒(méi)有創(chuàng)建表的解決方案
QuerySet API 參考 | Django 文檔 | Django
翻譯了Django1.4數(shù)據(jù)庫(kù)訪問(wèn)優(yōu)化部分 | the5fire
創(chuàng)建項(xiàng)目及配置
創(chuàng)建項(xiàng)目結(jié)構(gòu)
cd D:\github\python-django\2.typeidea
django-admin startproject typeidea
python manage.py runserver
拆分settings以適應(yīng)不同運(yùn)行環(huán)境
mkdir settings && cd settings && copy nul __init__.py
cd ..
move settings.py settings/base.py #將base.py抽象成settings的基類(lèi)
cd settings && copy nul develop.py
- 修改時(shí)區(qū)語(yǔ)言
# base.py文件中需要修改的部分,其他部分省略膳犹,
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
- 開(kāi)發(fā)環(huán)境使用指定數(shù)據(jù)庫(kù)
#將base中的數(shù)據(jù)庫(kù)配置粘貼到develop.py,并引入base.py的其他配置
# coding:utf-8
from .base import * # NOQA
DEBUG = True #從base.py剪貼過(guò)來(lái)
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR,'db.sqlite3'),
}
}
- 修改manage.py和typeidea/wsgi.py侨颈。指定settings路徑
替換:manage.py和typeidea/wsgi.py中
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'typeidea.settings')
為:
profile = os.environ.get('TYPEIDEA_PROFILE', 'develop') #如果環(huán)境變量有值就用環(huán)境的哈垢,沒(méi)有就用develop
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "typeidea.settings.%s" % profile)
git倉(cāng)庫(kù)
配置.gitignore
- 將不需要上傳的git文件配置到.gitignore
*.pyc
*.swp
*.sqlite3
提交命令
git add .
git commit -m "初始提交"
git remote add origin <倉(cāng)庫(kù)url>
git push -u origin master
# 安全提交方式
git add -p 交互提交方式
git commit 進(jìn)入編輯模式
編寫(xiě)Model層代碼
- model分為
創(chuàng)建app/blog
## 創(chuàng)建一個(gè)新分支
git checkout -b add-blog-app-model
cd typeidea/typeidea
python manage.py startapp blog
編寫(xiě)blog/model.py
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class Category(models.Model):
STATUS_NORMAL = 1
STATUS_DELETE = 0
STATUS_ITEMS = (
(STATUS_NORMAL, '正常'),
(STATUS_DELETE, '刪除'),
)
name = models.CharField(max_length=50, verbose_name="名稱(chēng)")
status = models.PositiveBigIntegerField(default=STATUS_ITEMS,
choices=STATUS_ITEMS, verbose_name="狀態(tài)")
is_nav = models.BooleanField(default=False, verbose_name="是否為導(dǎo)航")
created_time = models.DateTimeField(auto_now_add=True, verbose_name="創(chuàng)建時(shí)間")
class Meta:
verbose_name = verbose_name_plural = '分類(lèi)'
class Tag(models.Model):
STATUS_NORMAL = 1
STATUS_DELETE = 0
STATUS_ITEMS = (
(STATUS_NORMAL, '正常'),
(STATUS_DELETE, '刪除'),
)
name = models.CharField(max_length=255,verbose_name="標(biāo)題")
status = models.CharField(max_length=1024,blank=True,verbose_name="摘要")
owner = models.ForeignKey(User,verbose_name="作者", on_delete=models.CASCADE)
create_time = models.DateTimeField(auto_now_add=True,verbose_name="創(chuàng)建時(shí)間")
class Meta:
verbose_name = verbose_name_plural = "標(biāo)簽"
class Post(models.Model):
STATUS_NORMAL = 1
STATUS_DELETE = 0
STATUS_DRAFT = 2
STATUS_ITEMS = (
(STATUS_NORMAL,'正常'),
(STATUS_DELETE,'刪除'),
(STATUS_DRAFT,'草稿'),
)
title = models.CharField(max_length=255,verbose_name="標(biāo)題")
desc = models.CharField(max_length=1024,blank=True,verbose_name="摘要")
content = models.TextField(verbose_name="正文",help_text="正文必須為MarkDown格式")
status = models.PositiveIntegerField(default=STATUS_NORMAL,
choices=STATUS_ITEMS,verbose_name="狀態(tài)")
category = models.ForeignKey(Category,verbose_name="分類(lèi)", on_delete=models.CASCADE)
tag = models.ManyToManyField(Tag,verbose_name="標(biāo)簽")
owner = models.ForeignKey(User,verbose_name="作者", on_delete=models.CASCADE)
created_time = models.DateTimeField(auto_now_add=True,verbose_name="創(chuàng)建時(shí)間")
class Meta:
verbose_name = verbose_name_plural = "文章"
ordering = ['-id'] # 根據(jù)id進(jìn)行降序排列
創(chuàng)建app/config,編寫(xiě)model.py
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class Link(models.Model):
STATUS_NORMAL = 1
STATUS_DELETE = 0
STATUS_ITEMS = (
(STATUS_NORMAL,'正常'),
(STATUS_DELETE,'刪除'),
)
title = models.CharField(max_length=50,verbose_name="標(biāo)題")
href = models.URLField(verbose_name="鏈接") #默認(rèn)長(zhǎng)度為200
status = models.PositiveIntegerField(default=1,choices=STATUS_ITEMS,
verbose_name="狀態(tài)")
weight = models.PositiveIntegerField(default=1,choices=zip(range(1,6),range(1,6)),
verbose_name="權(quán)重",
help_text="權(quán)重靠前展示順序靠前")
owner = models.ForeignKey(User,verbose_name="作者", on_delete=models.CASCADE)
created_time = models.DateTimeField(auto_now_add=True,verbose_name="創(chuàng)建時(shí)間")
class Meta:
verbose_name = verbose_name_plural = "友鏈"
# 側(cè)邊欄
class SideBar():
STATUS_SHOW = 1
STATUS_HIDE = 0
STATUS_ITEMS = (
(STATUS_SHOW,'展示'),
(STATUS_HIDE,'隱藏'),
)
SIDE_TYPE =(
(1,'HTML'),
(2,'最新文章'),
(3,'最熱文章'),
(4,'最近評(píng)論'),
)
title = models.CharField(max_length=50,verbose_name="標(biāo)題")
display_type = models.PositiveIntegerField(default=1,choices=SIDE_TYPE,
verbose_name="展示類(lèi)型")
content = models.CharField(max_length=500,blank=True,verbose_name="內(nèi)容",
help_text="如果設(shè)置的不是HTML類(lèi)型,可為空")
status = models.PositiveIntegerField(default=STATUS_SHOW,choices=STATUS_ITEMS,
verbose_name="狀態(tài)")
created_time = models.DateTimeField(auto_now_add=True,verbose_name="創(chuàng)建時(shí)間")
owner = models.ForeignKey(User,verbose_name="作者", on_delete=models.CASCADE)
created_time = models.DateTimeField(auto_now_add=True,verbose_name="創(chuàng)建時(shí)間")
class Meta:
verbose_name = verbose_name_plural = "側(cè)邊欄"
創(chuàng)建評(píng)論app/comment卜朗,編寫(xiě)model.py
from django.db import models
from blog.models import Post
# Create your models here.
class Comment(models.Model):
STATUS_NORMAL = 1
STATUS_DELETE = 0
STATUS_ITEMS = (
(STATUS_NORMAL, '正常'),
(STATUS_DELETE, '刪除'),
)
target = models.ForeignKey(Post, verbose_name="評(píng)論", on_delete=models.CASCADE)
content = models.CharField(max_length=2000, verbose_name="內(nèi)容")
nickname = models.CharField(max_length=50, verbose_name="昵稱(chēng)")
website = models.URLField(verbose_name="網(wǎng)站")
email = models.EmailField(verbose_name="郵箱")
status = models.PositiveIntegerField(default=STATUS_NORMAL, choices=STATUS_ITEMS, verbose_name="狀態(tài)")
created_time = models.DateTimeField(auto_now_add=True, verbose_name="創(chuàng)建時(shí)間")
class Meta:
verbose_name = verbose_name_plural = "評(píng)論"
配置INSTALLED_APPS
- 編寫(xiě)完model之后,將創(chuàng)建的app配置到settings逛万,使django可以識(shí)別到
- 注意INSTALLED_APPS列表的順序,static和templates模塊尋找app資源時(shí)当纱,按從上到下的app順序查找
-
開(kāi)發(fā)中可以寫(xiě)一份路徑和名稱(chēng)與admin(app)一樣的來(lái)覆蓋自帶的坡氯,django可以重載
創(chuàng)建數(shù)據(jù)庫(kù)
- 創(chuàng)建遷移文件,就是把model定義的數(shù)據(jù)庫(kù)信息遷移到數(shù)據(jù)庫(kù)中
python manage.py makemigrations
- 執(zhí)行遷移操作
- 數(shù)據(jù)庫(kù)的名稱(chēng)是在settings/develop.py中配置的DATABASES里的default中的name
- 使用其他類(lèi)型數(shù)據(jù)庫(kù)如mysql需要先建好數(shù)據(jù)庫(kù)洋腮,通過(guò)這2個(gè)命令來(lái)建表
python manage.py migrate
- 查看數(shù)據(jù)庫(kù),進(jìn)入交互模式
python manage.py dbshell
-
注意數(shù)據(jù)庫(kù)庫(kù)生成的位置
ORM基本概念
常用字段類(lèi)型
數(shù)值型
- AutoField int(11):自增主鍵箫柳,django model默認(rèn)提供,可以被重寫(xiě)
id = models.AutoField(primary_key=True)
- BooleanField tinyint(1):布爾類(lèi)型字段啥供,一般用于記錄狀態(tài)
- DecimalField decimal : 對(duì)數(shù)據(jù)精度要求高悯恍,支付金融相關(guān)
cash = models.DecimalField(max_digits=8,decimal_places=2,default=0,verbose_name="消費(fèi)金額")
定義長(zhǎng)度為8位,精度為2的數(shù)字伙狐。比如保存666.66這樣的數(shù)字瞬欧,max_digits=5,decimal_places=2
- IntegerField int(11):同AutoField一樣罢防,就是不自增
- PostivieIntegerField:只包含正整數(shù)
- SmallIntegerField smallint:小整數(shù)用到
字符型
CharField varchar 基礎(chǔ)的varchar類(lèi)型
URLField 繼承自CharFiled艘虎,實(shí)現(xiàn)了對(duì)url的特殊處理
UUIDField char(22) 在postgreSQL中使用的是uuid類(lèi)型,其他存放生成的唯一id用char(32)
EmailField 繼承CharFiled咒吐,多了對(duì)email的特殊處理
FileField 繼承自CharField野建,多了對(duì)文件的處理,admin展示部分會(huì)生成一個(gè)可上傳文件的按鈕
TextField longtext 存放大容量文本恬叹,博客正文
ImageField 繼承自FileField候生,處理圖片相關(guān)數(shù)據(jù)
日期類(lèi)型
ForeginKey
OneToOneField
MangToManyField
外鍵和一對(duì)一其實(shí)是一種,只是一對(duì)一在外鍵字段上加了unique绽昼,多對(duì)多會(huì)創(chuàng)建一個(gè)中間表
參數(shù)
null null唯鸭,在數(shù)據(jù)庫(kù)層面是否允許為空
blank 針對(duì)業(yè)務(wù)層面,該值是否允許為空
choices 配置字段choices绪励,在admin頁(yè)面上就可以看到對(duì)應(yīng)的可選項(xiàng)展示
db_column 指定Field對(duì)應(yīng)數(shù)據(jù)庫(kù)哪個(gè)字段肿孵,一般是name對(duì)應(yīng)數(shù)據(jù)庫(kù)字段
db_index 索引配置,業(yè)務(wù)上需要經(jīng)常作為查詢(xún)條件的字段
default 默認(rèn)值配置
editable 是否可編輯疏魏,默認(rèn)True停做,如果不想將字段展示到頁(yè)面上,可以配置為False
error_messages 用來(lái)自定義字段值校驗(yàn)失敗時(shí)的異常提示大莫,他是字典格式蛉腌,key的選項(xiàng)可以為null、blank只厘、invalid烙丛、invalid_choice、unique羔味、unique_for_date
unique 唯一約束
unique_for_date 針對(duì)date(日期)的聯(lián)合約束河咽,比如我們一天只能有一篇django文章配置字段時(shí)配置參數(shù):unique_for_date="created_time"
querySet對(duì)象
- 數(shù)據(jù)庫(kù)查詢(xún)與更新,使用queryset
- 通過(guò)給Model增加一個(gè)objects屬性來(lái)提供數(shù)據(jù)操作的接口
- queryset是懶加載的赋元,當(dāng)用到時(shí)才會(huì)執(zhí)行數(shù)據(jù)庫(kù)查詢(xún)語(yǔ)句
- 支持鏈?zhǔn)讲樵?xún)
下面2句不會(huì)執(zhí)行數(shù)據(jù)庫(kù)查詢(xún)忘蟹,只是拿到了queryset對(duì)象
- 當(dāng)執(zhí)行print(availabled_posts)時(shí),才會(huì)執(zhí)行數(shù)據(jù)庫(kù)查詢(xún)
posts = Post.objects.all() #拿到quertset對(duì)象搁凸,這個(gè)對(duì)象中包含了我們需要的數(shù)據(jù)媚值,用到他時(shí)會(huì)去db中獲取數(shù)據(jù)
availabled_posts= posts.filter(status=1)
# 鏈?zhǔn)秸{(diào)用:每個(gè)函數(shù)或者方法的執(zhí)行結(jié)果返回對(duì)象本身,這樣還能繼續(xù)調(diào)用它的方法
常用的queryset接口
支持鏈?zhǔn)秸{(diào)用的接口
all 接口:相當(dāng)于select * from table_name 語(yǔ)句
filter 接口:根據(jù)條件過(guò)濾护糖,常用等于褥芒,不等于,大于嫡良,小于锰扶。比如能改成like查詢(xún)
Model.objects.filter(content_contains="條件")
exclude 接口:同filter献酗,只是相反的邏輯
reverse 接口:把queryset中的結(jié)果顛倒
distinct 接口:用來(lái)進(jìn)行去重查詢(xún),產(chǎn)生select distint這樣的查詢(xún)
none 接口:返回空的queryset
不支持鏈?zhǔn)秸{(diào)用的接口
get 接口:Post.objects.get(id=1)用于查詢(xún)id為1的文章少辣,如果存在則直接返回對(duì)應(yīng)的Post實(shí)例凌摄;如果不存在羡蛾,拋出DoesNotExit異常
try:
post =Post.objects.get(id=1)
except Post.DoesNotExist:
# 做異常處理
create 接口:用來(lái)創(chuàng)建一個(gè)Model對(duì)象漓帅,比如post = Post.objects.create(title="一起學(xué)習(xí)")
get_or_create接口:根據(jù)條件查找,如果沒(méi)有痴怨,就調(diào)用create創(chuàng)建
update_or_create接口:同get_or_create忙干,只是用來(lái)更新
count接口:用于返回queryset有多少條記錄,相當(dāng)于select count(*) from table_name
lastest 接口:返回最新的一條記錄浪藻,但需要在Model的Meta中定義:get_latest_by = <用來(lái)排序的字段>
earliest接口:同上捐迫,返回最早的一條記錄
first接口:從當(dāng)前QuerySet記錄中獲取第一條
last接口:同上,獲取最后一條
exists接口:返回True爱葵,或者False施戴,在數(shù)據(jù)庫(kù)層面執(zhí)行select(1)As "a" FROM table_name LIMIT 1的查詢(xún),用來(lái)判斷queryset是否有數(shù)據(jù)最合適萌丈。不要用count
bulk_create接口:同create赞哗,用來(lái)批量創(chuàng)建記錄
in_bulk接口:批量查詢(xún),接受2個(gè)參數(shù)id_list和 filed_name辆雾,可以通過(guò)Post.objects.in_bulk([1,2,3])查詢(xún)出id為1肪笋、2、3的數(shù)據(jù)度迂。返回結(jié)果是字典類(lèi)型藤乙,字典類(lèi)型的key為查詢(xún)條件。
update接口:根據(jù)條件批量更新記錄惭墓。比如Post.objects.filter(owner__name='the5fire').update(title='測(cè)試更新')
delete接口:同update坛梁,這個(gè)接口是用來(lái)根據(jù)條件批量刪除記錄。update和delete會(huì)觸發(fā)django的signal
values接口:只需要返回某個(gè)字段的值腊凶,不需要model實(shí)例划咐,可以使用
title_list = Post.objects.filter(category_id=1).values('title')
返回的結(jié)果包含dict的queryset,<queryset[{'title':xxx},]>
values_list接口:同values吭狡,但是直接返回的是包含元組的queryset
titles_list = Post.objects.filter(category=1).values_list('title')
返回的結(jié)果類(lèi)似<queryset[('標(biāo)題'),]>
如果只有一個(gè)字段尖殃,可以通過(guò)增加flat=True參數(shù),便于后續(xù)處理
title_list = Post.objects.filter(category=1).values_list('title',flat=True)
for title in title_list:
print(title)
進(jìn)階接口
defer接口:把不需要的字段做延遲加載,比如說(shuō)獲取到文章中除正文之外的其他字段划煮。通過(guò)posts = Post.object.all().defer('content'),這樣拿到的記錄就不包含content部分
posts = Post.object.all().defer('content')
for post in posts:
print(post.content) #此時(shí)會(huì)執(zhí)行數(shù)據(jù)查詢(xún)送丰,獲取到content
N+1 問(wèn)題:一條查詢(xún)請(qǐng)求返回n條數(shù)據(jù),一般由外鍵查詢(xún)產(chǎn)生的比較多
only接口:同defer相反弛秋,比如指向獲取title記錄器躏,就可以使用only
select_related接口:用來(lái)解決外鍵產(chǎn)生的N+1問(wèn)題的方案俐载。(只能解決一對(duì)多的關(guān)聯(lián)關(guān)系)
posts = Post.objects.all()
for post in posts: #產(chǎn)生數(shù)據(jù)庫(kù)查詢(xún)
print(post.owner) #產(chǎn)生額外的數(shù)據(jù)庫(kù)查詢(xún)
代碼同上面類(lèi)似,只是用的是owner
他的解決辦法
posts = Post.objects.all().select_related('category')
for post in posts: #產(chǎn)生數(shù)據(jù)庫(kù)查詢(xún)登失,category數(shù)據(jù)也會(huì)一次性查詢(xún)出來(lái)
print(post.category)
prefetch_related 接口:準(zhǔn)備多對(duì)多關(guān)系的數(shù)據(jù)遏佣,可以通過(guò)這個(gè)接口來(lái)避免N+1查詢(xún)
比如,post和tag的關(guān)系可以通過(guò)這種方式來(lái)避免:
posts = Posts.object.all().prefetch_related('tag')
for post in posts: #產(chǎn)生兩條查詢(xún)語(yǔ)句揽浙,分別查詢(xún)post和tag
print(post)
常用的字段查詢(xún)
Post.objects.filter(content__contains='查詢(xún)條件')中的contains就屬于字段查詢(xún)状婶。
- contians:包含,用來(lái)進(jìn)行l(wèi)ike查詢(xún)
- icontains:同contains馅巷,只是忽略大小寫(xiě)
- exact:精確匹配
- iexact:同exact膛虫,忽略小寫(xiě)
- in:指定某個(gè)集合,比如Post.objects.filter(id__in=[1,2,3])相當(dāng)于select * from blog_post where in (1,2,3);
- gt:大于某個(gè)值
- gte:大于某個(gè)值
- lt:小于某個(gè)值
- lte:小于等于每個(gè)值
- startswith:以某個(gè)字符串開(kāi)頭钓猬,與contains類(lèi)似稍刀,只會(huì)產(chǎn)生LIKE'<關(guān)鍵字>%'這樣的sql
- istartwith:同startswith,忽略大小寫(xiě)
- endswith:以某個(gè)字符串結(jié)尾
- iendswith:忽略大小寫(xiě)
- range:范圍查詢(xún)敞曹,多用于時(shí)間范圍账月,如Post.objects.filter(created_time__range=('2018-05-01','2018-06-01'))會(huì)產(chǎn)生這樣的查詢(xún):select 。澳迫。局齿。where created_time BETWEEN '2018-05-01' AND '2018-06-01';
關(guān)于日期類(lèi)的查詢(xún)還有很多,比如date纲刀、year和month等项炼,具體等需要時(shí)查文檔即可。
進(jìn)階查詢(xún)
滿(mǎn)足復(fù)雜查詢(xún)示绊,比如select ... where id = 1 or id = 2 這樣的查詢(xún)锭部,就需要進(jìn)階查詢(xún)
- F表達(dá)式
保證數(shù)據(jù)庫(kù)的原子性
post = Post.objects.get(id=1)
post.pv = post.pv + 1
pos.save()
from django.db.models import F
post = Post.objects.get(id=1)
post.pv = F('pv') + 1 #保證是原子性操作
pos.save
- Q表達(dá)式
解決or查詢(xún)
from django.db.models import Q
Post.objects.filter(Q(id=1) | Q(id=2))
解決and查詢(xún)
Post.objects.filter(Q(id=1) & Q(id=2))
- Count聚合查詢(xún)(annotate)
得到某個(gè)分類(lèi)下有多少篇文章
category = Category.objects.get(id=1)
posts_count = category.post_set.count()
增加屬性:annotate
from django.db.models import Count
categories = Category.objects.annotate(posts_count=Count('post'))
print(categories[0].posts_count)
這相當(dāng)category動(dòng)態(tài)增加了屬性posts_count,而這個(gè)屬性的值來(lái)源于Count('post')
- Sum聚合查詢(xún)(aggregate)
同count類(lèi)似,只是他是用來(lái)做合計(jì)的
from django.db.models import Sum
Post.objects.aggregate(all_pv=Sum('pv'))
# 輸出類(lèi)似結(jié)果:{'all_pv'L487}
還有Avg面褐、Mix拌禾、Max等表達(dá)式,軍用來(lái)滿(mǎn)足sql查詢(xún)