前言
承接《Django入门》,本文参照慕课网《django入门与实践》课程,开发一个简单的博客系统。按照国际惯例,我们先学习一下django的基础知识。
模板引擎
Django默认使用DTL(Django Template Language)作为模板引擎,如果想要修改为其他模板引擎,直接在djsite/djsite/settings.py中修改TEMPLATES即可。详情可以参考The Django template language: for Python programmers。
first template
1、在blog目录下创建templates目录。
2、在templates目录中创建index.html文件。
Index
first template!
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、测试访问
启动django,访问 http://localhost:8000/blog/ ,即可看到渲染好的页面。
DTL
1、修改index方法为:
def index(request):
return render(request, 'index.html',{'title': 'DTL'})
2、修改index.html为:
Index
{{title}}
first template!
3、测试访问
启动django,访问 http://localhost:8000/blog/ ,即可看到渲染好的页面。
不同应用下的templates目录会发生冲突,django按照INSTALLED_APP中的顺序查找templates。为了解决这个问题,我们需要在templates目录中加一层目录,以应用名为名。而模板,都放到这一层目录中。
4、在templates目录下,新建blog文件夹,把index.html移动到blog文件夹中。同时,修改index函数为:
def index(request):
return render(request, 'blog/index.html',{'title': 'DTL'})
增删查改
django默认使用db.sqlite3数据库,我们暂时不进行修改。
Model
1、在blog/models.py中添加一个类Article:
class Article(models.Model):
title = models.CharField(max_length=32, default='Title')
content = models.TextField(null=True)
# 参数 auto_now=True 表示自动添加隐藏的时间
pub_time = models.DateTimeField(null=True, auto_now=True)
def __str__(self):
return self.title
关于属性的配置,参考Model field reference。
2、生成数据表
python manage.py makemigrations blog
,创建model,生成的文件在blog/migrations目录下
python manage.py migrate
,根据model生成数据库表
3、查看sql语句
python manage.py sqlmigrate blog 0001
4、下载安装SQLite Expert Personal,双击db.sqlite3文件即可查看编辑数据库。
5、使用SQLiteExpert,在blog_article表中添加数据。
查找数据
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文件
List
第一篇文章
标题:{{article.title}}
内容{{article.content}}
文章列表
标题
内容
{% for article in articles %}
{{article.title}}
{{article.content}}
{% endfor %}
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 ,即可看到渲染后的效果。
增加数据
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 ,即可看到添加成功的提示。
修改数据
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:修改数据和增加数据可以合成为一个接口,例如:
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))
删除数据
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转JSON
要想最终得到一个json数据,前提是我们要拥有一个dict,所以Model转JSON问题就归结为怎样组装出一个dict。
示例一:在add方法中,我们返回的结果是json格式。如果想要把article(Model)也放进结果中,该怎么处理?参考Python JSON和django的model对象转化成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))
示例二:如果想要把articles(Models)也放进结果中,该怎么处理?参考django 返回json数据。
首先,把Models序列化为json格式数据;然后,使用json.loads转换为dict格式数据;最后,把转换后的dict和其他dict格式数据组装到一起。
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发送post请求时遇到如下错误:
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))
时间处理
修改时区
查看db.sqlite3数据库,可以看到通过接口添加的数据时间不对。
参考django时间的时区问题,修改settings.py:
USE_TZ = True
TIME_ZONE = 'Asia/Shanghai'
设置了USE_TZ=True,则存储到数据库中的时间永远是UTC时间。
设置了TIME_ZONE = 'Asia/Shanghai',能保证证模板时间的正确显示。
这时如果TIME_ZONE = 'UTC',用datetime.datetime.now()获取时间,django会把这个时间当成UTC时间存储到数据库中去。
如果修改设置为TIME_ZONE = 'Asia/Shanghai',用datetime.datetime.now()获取时间,django会把这个时间当成Asia/Shanghai时间,即东八区时间,然后django会把这个时间转成带时区UTC时间存储到数据库中去,而读的时候直接按UTC时间读出来,这就是很多人遇到的存储到数据库中的时间比本地时间会小8个小时的原因。
如果要获取当前时区时间,则使用django.utils.timezone.now()。
Model
参考django:DateTimeField如何自动设置为当前时间并且能被修改,我们来修改一下blog/models.py。
创建django的model时,有DateTimeField、DateField和TimeField三种类型可以用来创建日期字段,其值分别对应着datetime()、date()、time()三中对象。这三个field有着相同的参数auto_now和auto_now_add,表面上看起来很easy,但实际使用中很容易出错,下面是一些注意点。
DateTimeField.auto_now
这个参数的默认值为false,设置为true时,能够在保存该字段时,将其值设置为当前时间,并且每次修改model,都会自动更新。因此这个参数在需要存储“最后修改时间”的场景下,十分方便。需要注意的是,设置该参数为true时,并不简单地意味着字段的默认值为当前时间,而是指字段会被“强制”更新到当前时间,你无法程序中手动为字段赋值;如果使用django再带的admin管理器,那么该字段在admin中是只读的。
DateTimeField.auto_now_add
这个参数的默认值也为False,设置为True时,会在model对象第一次被创建时,将字段的值设置为创建时的时间,以后修改对象时,字段的值不会再更新。该属性通常被用在存储“创建时间”的场景下。与auto_now类似,auto_now_add也具有强制性,一旦被设置为True,就无法在程序中手动为字段赋值,在admin中字段也会成为只读的。
如何将创建时间设置为“默认当前”并且可修改
那么问题来了。实际场景中,往往既希望在对象的创建时间默认被设置为当前值,又希望能在日后修改它。怎么实现这种需求呢?
django中所有的model字段都拥有一个default参数,用来给字段设置默认值。可以用default=timezone.now来替换auto_now=True或auto_now_add=True。timezone.now对应着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('发布日期', default=timezone.now)
def __str__(self):
return self.title
DateEncoder
经过上面的修改,时间是可以修改了,但是同时引入了另外一个问题,在add接口中,json.dumps()函数会报错:
TypeError: Object of type 'datetime' is not JSON serializable
这是因为json.dumps()函数无法解析datetime格式的数据。
问题来了,auto_now=True时,json.dumps()却可以解析,莫非此时不是datetime格式?
且不管它,我们先解决datetime转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()函数时,添加cls参数:
json.dumps(result, cls=DateEncoder, ensure_ascii=False)
页面渲染
设置好时区后,在页面渲染时,会自动转化成当前时区时间,例如Nov. 29, 2017, 1:49 p.m.
但是,这种格式不符合我们的阅读习惯,我们可以在渲染时改成自己喜欢的格式:
{{article.pub_time|date:"Y-m-d H:i:s"}}
此时,输出到页面的格式就变成了2017-11-29 13:49:44
json UTC处理
以add接口为例,从数据库中查询出的数据时间是UTC格式的,例如2017-11-29T05:49:44.092Z
思路一:
直接返回UTC格式数据给前端,前端来完成格式化,参考js格式化json传来的UTC格式的时间,或者使用支持UTC格式化的模板引擎。
思路二:
参考遍历QuerySet,给每一个pub_time转换格式:
import datetime
import time
def utc2local(utc_st):
# UTC时间转本地时间(+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)
这种方法返回的时间,格式为2017-11-29T13:49:44
,还是有问题,多了个T。
我们为什么不把local_time_str赋值给item.pub_time呢?因为item.put_time限制数据类型为datetime。
思路三:
存储时,直接存储字符串格式的时间。修改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('发布日期', 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时间转本地时间(+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
小结
至此,涉猎了django开发blog所需要的基本知识。下文中,将会在实战中学习django更高级的用法。
书签
django入门与实践