前言
承接《Django入門》,本文參照慕課網(wǎng)《django入門與實(shí)踐》課程惩琉,開發(fā)一個(gè)簡單的博客系統(tǒng)。按照國際慣例夺荒,我們先學(xué)習(xí)一下django的基礎(chǔ)知識瞒渠。
模板引擎
Django默認(rèn)使用DTL(Django Template Language)作為模板引擎,如果想要修改為其他模板引擎技扼,直接在djsite/djsite/settings.py中修改TEMPLATES即可伍玖。詳情可以參考The Django template language: for Python programmers。
first template
1剿吻、在blog目錄下創(chuàng)建templates目錄窍箍。
2、在templates目錄中創(chuàng)建index.html文件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Index</title>
</head>
<body>
first template!
</body>
</html>
3椰棘、修改blog/urls.py為:
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^$', views.index, name='index'),
url(r'helloworld', views.hello, name='hello')
]
4纺棺、在views.py中添加方法:
def index(request):
return render(request, 'index.html')
5、測試訪問
啟動(dòng)django邪狞,訪問 http://localhost:8000/blog/ 祷蝌,即可看到渲染好的頁面。
DTL
1帆卓、修改index方法為:
def index(request):
return render(request, 'index.html',{'title': 'DTL'})
2巨朦、修改index.html為:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Index</title>
</head>
<body>
<h2>{{title}}</h2>
<p>first template!</p>
</body>
</html>
3、測試訪問
啟動(dòng)django剑令,訪問 http://localhost:8000/blog/ 糊啡,即可看到渲染好的頁面。
不同應(yīng)用下的templates目錄會發(fā)生沖突吁津,django按照INSTALLED_APP中的順序查找templates棚蓄。為了解決這個(gè)問題,我們需要在templates目錄中加一層目錄腺毫,以應(yīng)用名為名癣疟。而模板,都放到這一層目錄中潮酒。
4睛挚、在templates目錄下,新建blog文件夾急黎,把index.html移動(dòng)到blog文件夾中扎狱。同時(shí),修改index函數(shù)為:
def index(request):
return render(request, 'blog/index.html',{'title': 'DTL'})
增刪查改
django默認(rèn)使用db.sqlite3數(shù)據(jù)庫勃教,我們暫時(shí)不進(jìn)行修改淤击。
Model
1、在blog/models.py中添加一個(gè)類Article:
class Article(models.Model):
title = models.CharField(max_length=32, default='Title')
content = models.TextField(null=True)
# 參數(shù) auto_now=True 表示自動(dòng)添加隱藏的時(shí)間
pub_time = models.DateTimeField(null=True, auto_now=True)
def __str__(self):
return self.title
關(guān)于屬性的配置故源,參考Model field reference污抬。
2、生成數(shù)據(jù)表
python manage.py makemigrations blog
绳军,創(chuàng)建model印机,生成的文件在blog/migrations目錄下
python manage.py migrate
,根據(jù)model生成數(shù)據(jù)庫表
3门驾、查看sql語句
python manage.py sqlmigrate blog 0001
4射赛、下載安裝SQLite Expert Personal,雙擊db.sqlite3文件即可查看編輯數(shù)據(jù)庫奶是。
5楣责、使用SQLiteExpert竣灌,在blog_article表中添加數(shù)據(jù)。
查找數(shù)據(jù)
1秆麸、在blog/views.py中添加方法:
from . import models
def list(request):
articles = models.Article.objects.all()
article = models.Article.objects.get(pk=1)
return render(request, 'blog/list.html',{'articles':articles,'article':article})
2初嘹、在migrations/blog中添加list.html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>List</title>
</head>
<body>
<h2>第一篇文章</h2>
<h3>標(biāo)題:{{article.title}}</h3>
<p>內(nèi)容{{article.content}}</p>
<hr>
<h2>文章列表</h2>
<table>
<thead>
<th>標(biāo)題</th>
<th>內(nèi)容</th>
</thead>
<tbody>
{% for article in articles %}
<tr>
<td>{{article.title}}</td>
<td>{{article.content}}</td>
</tr>
{% endfor %}
</tbody>
</table>
</body>
</html>
3、修改blog/urls.py為:
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^$', views.index, name='index'),
url(r'^index$', views.index, name='index'),
url(r'^helloworld$', views.hello, name='hello'),
url(r'^list$',views.list, name='list')
]
4蛔屹、測試訪問
訪問地址 http://localhost:8000/blog/list 削樊,即可看到渲染后的效果。
增加數(shù)據(jù)
1兔毒、在blog/urls.py中添加:
url(r'^add$',views.add, name='add'),
2漫贞、在blog/views.py中添加方法:
import json
def add(request):
title = request.GET.get('title', 'defaultTitle')
content = request.GET.get('content', 'defaultContent')
article = models.Article.objects.create(title=title, content=content)
result = {'code': 0, 'ext': 'success', 'article_id': article.id}
return HttpResponse(json.dumps(result,ensure_ascii=False))
3、測試訪問
訪問地址 http://localhost:8000/blog/add?title=test&content=test 育叁,即可看到添加成功的提示迅脐。
修改數(shù)據(jù)
1、在blog/urls.py中添加:
url(r'^edit$',views.edit, name='edit'),
2豪嗽、在blog/views.py中添加方法:
def edit(request):
article_id = request.GET.get('id', 0)
title = request.GET.get('title', 'defaultTitle')
content = request.GET.get('content', 'defaultContent')
article = models.Article.objects.get(pk=article_id)
article.title = title
article.content = content
article.save()
result = {'code': 0, 'ext': 'success', 'article_id': article.id}
return HttpResponse(json.dumps(result,ensure_ascii=False))
3谴蔑、測試訪問
訪問地址 http://localhost:8000/blog/edit?id=1&title=test&content=test222 ,即可看到修改成功的提示龟梦。
PS:修改數(shù)據(jù)和增加數(shù)據(jù)可以合成為一個(gè)接口隐锭,例如:
def edit(request):
article_id = request.GET.get('id', '0')
title = request.GET.get('title', 'defaultTitle')
content = request.GET.get('content', 'defaultContent')
if article_id == '0':
article = models.Article.objects.create(title=title, content=content)
result = {'code': 0, 'ext': 'success', 'article_id': article.id}
return HttpResponse(json.dumps(result,ensure_ascii=False))
article = models.Article.objects.get(pk=article_id)
article.title = title
article.content = content
article.save()
result = {'code': 0, 'ext': 'success', 'article_id': article.id}
return HttpResponse(json.dumps(result,ensure_ascii=False))
刪除數(shù)據(jù)
1、在blog/urls.py中添加:
url(r'^delete$',views.delete, name='delete'),
2计贰、在blog/views.py中添加方法:
def delete(request):
article_id = request.GET.get('id', 0)
models.Article.objects.get(pk=article_id).delete()
result = {'code': 0, 'ext': 'success'}
return HttpResponse(json.dumps(result,ensure_ascii=False))
3钦睡、測試訪問
訪問地址 http://localhost:8000/blog/delete?id=1 ,即可看到刪除成功的提示躁倒。
Model轉(zhuǎn)JSON
要想最終得到一個(gè)json數(shù)據(jù)荞怒,前提是我們要擁有一個(gè)dict,所以Model轉(zhuǎn)JSON問題就歸結(jié)為怎樣組裝出一個(gè)dict秧秉。
示例一:在add方法中褐桌,我們返回的結(jié)果是json格式。如果想要把a(bǔ)rticle(Model)也放進(jìn)結(jié)果中象迎,該怎么處理荧嵌?參考Python JSON和django的model對象轉(zhuǎn)化成dict,修改代碼如下:
from django.forms.models import model_to_dict
def add(request):
title = request.GET.get('title', 'defaultTitle')
content = request.GET.get('content', 'defaultContent')
article = models.Article.objects.create(title=title, content=content)
article = model_to_dict(article)
result = {'code': 0, 'ext': 'success','article': article}
return HttpResponse(json.dumps(result,ensure_ascii=False))
示例二:如果想要把a(bǔ)rticles(Models)也放進(jìn)結(jié)果中砾淌,該怎么處理完丽?參考django 返回json數(shù)據(jù)。
首先拇舀,把Models序列化為json格式數(shù)據(jù);然后蜻底,使用json.loads轉(zhuǎn)換為dict格式數(shù)據(jù)骄崩;最后聘鳞,把轉(zhuǎn)換后的dict和其他dict格式數(shù)據(jù)組裝到一起。
from django.core import serializers
def add(request):
title = request.GET.get('title', 'defaultTitle')
content = request.GET.get('content', 'defaultContent')
article = models.Article.objects.create(title=title, content=content)
article = model_to_dict(article)
articles = models.Article.objects.all()
json_data = serializers.serialize("json", articles)
dict_data = json.loads(json_data)
result = {
'code': 0,
'ext': 'success',
'article': article,
'articles': dict_data}
return HttpResponse(json.dumps(result, ensure_ascii=False))
sqlite清空表命令
delete from 'blog_article';
update sqlite_sequence set seq = 0 where name = 'blog_article';
POST問題
修改add方法為:
def add(request):
title = request.POST.get('title', 'defaultTitle')
content = request.POST.get('content', 'defaultContent')
article = models.Article.objects.create(title=title, content=content)
article = model_to_dict(article)
articles = models.Article.objects.all()
json_data = serializers.serialize("json", articles)
dict_data = json.loads(json_data)
result = {
'code': 0,
'ext': 'success',
'article': article,
'articles': dict_data}
return HttpResponse(json.dumps(result, ensure_ascii=False))
使用postman發(fā)送post請求時(shí)遇到如下錯(cuò)誤:
CSRF verification failed. Request aborted.
解決辦法要拂,使用csrf_exempt裝飾器:
from django.views.decorators.csrf import csrf_exempt
@csrf_exempt
def add(request):
title = request.POST.get('title', 'defaultTitle')
content = request.POST.get('content', 'defaultContent')
article = models.Article.objects.create(title=title, content=content)
article = model_to_dict(article)
articles = models.Article.objects.all()
json_data = serializers.serialize("json", articles)
dict_data = json.loads(json_data)
result = {
'code': 0,
'ext': 'success',
'article': article,
'articles': dict_data}
return HttpResponse(json.dumps(result, ensure_ascii=False))
時(shí)間處理
修改時(shí)區(qū)
查看db.sqlite3數(shù)據(jù)庫抠璃,可以看到通過接口添加的數(shù)據(jù)時(shí)間不對。
參考django時(shí)間的時(shí)區(qū)問題脱惰,修改settings.py:
USE_TZ = True
TIME_ZONE = 'Asia/Shanghai'
設(shè)置了USE_TZ=True搏嗡,則存儲到數(shù)據(jù)庫中的時(shí)間永遠(yuǎn)是UTC時(shí)間。
設(shè)置了TIME_ZONE = 'Asia/Shanghai'拉一,能保證證模板時(shí)間的正確顯示采盒。
這時(shí)如果TIME_ZONE = 'UTC',用datetime.datetime.now()獲取時(shí)間蔚润,django會把這個(gè)時(shí)間當(dāng)成UTC時(shí)間存儲到數(shù)據(jù)庫中去磅氨。
如果修改設(shè)置為TIME_ZONE = 'Asia/Shanghai',用datetime.datetime.now()獲取時(shí)間嫡纠,django會把這個(gè)時(shí)間當(dāng)成Asia/Shanghai時(shí)間烦租,即東八區(qū)時(shí)間,然后django會把這個(gè)時(shí)間轉(zhuǎn)成帶時(shí)區(qū)UTC時(shí)間存儲到數(shù)據(jù)庫中去除盏,而讀的時(shí)候直接按UTC時(shí)間讀出來叉橱,這就是很多人遇到的存儲到數(shù)據(jù)庫中的時(shí)間比本地時(shí)間會小8個(gè)小時(shí)的原因。
如果要獲取當(dāng)前時(shí)區(qū)時(shí)間者蠕,則使用django.utils.timezone.now()窃祝。
Model
參考django:DateTimeField如何自動(dòng)設(shè)置為當(dāng)前時(shí)間并且能被修改,我們來修改一下blog/models.py蠢棱。
創(chuàng)建django的model時(shí)锌杀,有DateTimeField、DateField和TimeField三種類型可以用來創(chuàng)建日期字段泻仙,其值分別對應(yīng)著datetime()糕再、date()、time()三中對象玉转。這三個(gè)field有著相同的參數(shù)auto_now和auto_now_add突想,表面上看起來很easy,但實(shí)際使用中很容易出錯(cuò)究抓,下面是一些注意點(diǎn)猾担。
DateTimeField.auto_now
這個(gè)參數(shù)的默認(rèn)值為false,設(shè)置為true時(shí)刺下,能夠在保存該字段時(shí)绑嘹,將其值設(shè)置為當(dāng)前時(shí)間,并且每次修改model橘茉,都會自動(dòng)更新工腋。因此這個(gè)參數(shù)在需要存儲“最后修改時(shí)間”的場景下姨丈,十分方便。需要注意的是擅腰,設(shè)置該參數(shù)為true時(shí)蟋恬,并不簡單地意味著字段的默認(rèn)值為當(dāng)前時(shí)間,而是指字段會被“強(qiáng)制”更新到當(dāng)前時(shí)間趁冈,你無法程序中手動(dòng)為字段賦值歼争;如果使用django再帶的admin管理器,那么該字段在admin中是只讀的渗勘。
DateTimeField.auto_now_add
這個(gè)參數(shù)的默認(rèn)值也為False沐绒,設(shè)置為True時(shí),會在model對象第一次被創(chuàng)建時(shí)呀邢,將字段的值設(shè)置為創(chuàng)建時(shí)的時(shí)間洒沦,以后修改對象時(shí),字段的值不會再更新价淌。該屬性通常被用在存儲“創(chuàng)建時(shí)間”的場景下申眼。與auto_now類似,auto_now_add也具有強(qiáng)制性蝉衣,一旦被設(shè)置為True括尸,就無法在程序中手動(dòng)為字段賦值,在admin中字段也會成為只讀的病毡。
如何將創(chuàng)建時(shí)間設(shè)置為“默認(rèn)當(dāng)前”并且可修改
那么問題來了濒翻。實(shí)際場景中,往往既希望在對象的創(chuàng)建時(shí)間默認(rèn)被設(shè)置為當(dāng)前值啦膜,又希望能在日后修改它有送。怎么實(shí)現(xiàn)這種需求呢?
django中所有的model字段都擁有一個(gè)default參數(shù)僧家,用來給字段設(shè)置默認(rèn)值雀摘。可以用default=timezone.now來替換auto_now=True或auto_now_add=True八拱。timezone.now對應(yīng)著django.utils.timezone.now()阵赠,因此需要寫成類似下面的形式:
from django.db import models
import django.utils.timezone as timezone
class Article(models.Model):
title = models.CharField(max_length=32, default='Title')
content = models.TextField(null=True)
pub_time = models.DateTimeField('發(fā)布日期', default=timezone.now)
def __str__(self):
return self.title
DateEncoder
經(jīng)過上面的修改,時(shí)間是可以修改了肌稻,但是同時(shí)引入了另外一個(gè)問題清蚀,在add接口中,json.dumps()函數(shù)會報(bào)錯(cuò):
TypeError: Object of type 'datetime' is not JSON serializable
這是因?yàn)閖son.dumps()函數(shù)無法解析datetime格式的數(shù)據(jù)爹谭。
問題來了枷邪,auto_now=True時(shí),json.dumps()卻可以解析诺凡,莫非此時(shí)不是datetime格式齿风?
且不管它药薯,我們先解決datetime轉(zhuǎn)json問題。
import json
import datetime
class DateEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime.datetime):
return obj.strftime('%Y-%m-%d %H:%M:%S')
elif isinstance(obj, date):
return obj.strftime('%Y-%m-%d')
else:
return json.JSONEncoder.default(self, obj)
在使用json.dumps()函數(shù)時(shí)救斑,添加cls參數(shù):
json.dumps(result, cls=DateEncoder, ensure_ascii=False)
頁面渲染
設(shè)置好時(shí)區(qū)后,在頁面渲染時(shí)真屯,會自動(dòng)轉(zhuǎn)化成當(dāng)前時(shí)區(qū)時(shí)間脸候,例如Nov. 29, 2017, 1:49 p.m.
但是,這種格式不符合我們的閱讀習(xí)慣绑蔫,我們可以在渲染時(shí)改成自己喜歡的格式:
{{article.pub_time|date:"Y-m-d H:i:s"}}
此時(shí)运沦,輸出到頁面的格式就變成了2017-11-29 13:49:44
json UTC處理
以add接口為例,從數(shù)據(jù)庫中查詢出的數(shù)據(jù)時(shí)間是UTC格式的配深,例如2017-11-29T05:49:44.092Z
思路一:
直接返回UTC格式數(shù)據(jù)給前端携添,前端來完成格式化,參考js格式化json傳來的UTC格式的時(shí)間篓叶,或者使用支持UTC格式化的模板引擎烈掠。
思路二:
參考遍歷QuerySet,給每一個(gè)pub_time轉(zhuǎn)換格式:
import datetime
import time
def utc2local(utc_st):
# UTC時(shí)間轉(zhuǎn)本地時(shí)間(+8:00)
now_stamp = time.time()
local_time = datetime.datetime.fromtimestamp(now_stamp)
utc_time = datetime.datetime.utcfromtimestamp(now_stamp)
offset = local_time - utc_time
local_st = utc_st + offset
return local_st
for item in articles:
# print(item.pub_time)
local_time = utc2local(item.pub_time)
# UTC_FORMAT = "%Y-%m-%dT%H:%M:%S.%fZ"
LOCAL_FORMAT = "%Y-%m-%d %H:%M:%S"
# print(local_time.strftime(LOCAL_FORMAT))
local_time_str = local_time.strftime(LOCAL_FORMAT)
item.pub_time = datetime.datetime.strptime(local_time_str, LOCAL_FORMAT)
這種方法返回的時(shí)間缸托,格式為2017-11-29T13:49:44
左敌,還是有問題,多了個(gè)T俐镐。
我們?yōu)槭裁床话裭ocal_time_str賦值給item.pub_time呢矫限?因?yàn)閕tem.put_time限制數(shù)據(jù)類型為datetime。
思路三:
存儲時(shí)佩抹,直接存儲字符串格式的時(shí)間叼风。修改blog/models.py如下:
from django.db import models
# Create your models here.
class Article(models.Model):
title = models.CharField(max_length=32, default='Title')
content = models.TextField(null=True)
# pub_time = models.DateTimeField('發(fā)布日期', default=timezone.now)
pub_time = models.CharField(max_length=64, default='')
def __str__(self):
return self.title
修改add和edit接口為:
import json
from django.forms.models import model_to_dict
from django.views.decorators.csrf import csrf_exempt
import datetime
import time
from django.utils import timezone
@csrf_exempt
def add(request):
title = request.POST.get('title', 'defaultTitle')
content = request.POST.get('content', 'defaultContent')
pub_time = utc2local(timezone.now())
LOCAL_FORMAT = "%Y-%m-%d %H:%M:%S"
pub_time = pub_time.strftime(LOCAL_FORMAT)
article = models.Article.objects.create(title=title, content=content, pub_time=pub_time)
article = model_to_dict(article)
result = {
'code': 0,
'ext': 'success',
'article': article}
return HttpResponse(json.dumps(result, ensure_ascii=False))
@csrf_exempt
def edit(request):
article_id = request.POST.get('id', 0)
title = request.POST.get('title', 'defaultTitle')
content = request.POST.get('content', 'defaultContent')
pub_time = utc2local(timezone.now())
LOCAL_FORMAT = "%Y-%m-%d %H:%M:%S"
pub_time = pub_time.strftime(LOCAL_FORMAT)
article = models.Article.objects.get(pk=article_id)
article.title = title
article.content = content
article.pub_time = pub_time
article.save()
article = model_to_dict(article)
result = {
'code': 0,
'ext': 'success',
'article': article}
return HttpResponse(json.dumps(result, ensure_ascii=False))
def utc2local(utc_st):
# UTC時(shí)間轉(zhuǎn)本地時(shí)間(+8:00)
now_stamp = time.time()
local_time = datetime.datetime.fromtimestamp(now_stamp)
utc_time = datetime.datetime.utcfromtimestamp(now_stamp)
offset = local_time - utc_time
local_st = utc_st + offset
return local_st
源碼分享
https://github.com/voidking/djsite/releases/tag/v0.1.0
小結(jié)
至此,涉獵了django開發(fā)blog所需要的基本知識棍苹。下文中无宿,將會在實(shí)戰(zhàn)中學(xué)習(xí)django更高級的用法。