Django开发简单Blog系统——上

文章目录
  1. 1. 前言
  2. 2. 模板引擎
    1. 2.1. first template
    2. 2.2. DTL
  3. 3. 增删查改
    1. 3.1. Model
    2. 3.2. 查找数据
    3. 3.3. 增加数据
    4. 3.4. 修改数据
    5. 3.5. 删除数据
    6. 3.6. Model转JSON
    7. 3.7. sqlite清空表命令
  4. 4. POST问题
  5. 5. 时间处理
    1. 5.1. 修改时区
    2. 5.2. Model
    3. 5.3. DateEncoder
    4. 5.4. 页面渲染
    5. 5.5. json UTC处理
  6. 6. 源码分享
  7. 7. 小结
  8. 8. 书签

前言

承接《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文件。

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Index</title>
</head>
<body>
first template!
</body>
</html>

3、修改blog/urls.py为:

1
2
3
4
5
6
7
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中添加方法:

1
2
def index(request):
return render(request, 'index.html')

5、测试访问
启动django,访问 http://localhost:8000/blog/ ,即可看到渲染好的页面。

DTL

1、修改index方法为:

1
2
def index(request):
return render(request, 'index.html',{'title': 'DTL'})

2、修改index.html为:

1
2
3
4
5
6
7
8
9
10
11
<!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、测试访问
启动django,访问 http://localhost:8000/blog/ ,即可看到渲染好的页面。

不同应用下的templates目录会发生冲突,django按照INSTALLED_APP中的顺序查找templates。为了解决这个问题,我们需要在templates目录中加一层目录,以应用名为名。而模板,都放到这一层目录中。

4、在templates目录下,新建blog文件夹,把index.html移动到blog文件夹中。同时,修改index函数为:

1
2
def index(request):
return render(request, 'blog/index.html',{'title': 'DTL'})

增删查改

django默认使用db.sqlite3数据库,我们暂时不进行修改。

Model

1、在blog/models.py中添加一个类Article:

1
2
3
4
5
6
7
8
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中添加方法:

1
2
3
4
5
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文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>List</title>
</head>
<body>
<h2>第一篇文章</h2>
<h3>标题:{{article.title}}</h3>
<p>内容{{article.content}}</p>
<hr>
<h2>文章列表</h2>
<table>
<thead>
<th>标题</th>
<th>内容</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为:

1
2
3
4
5
6
7
8
9
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中添加:

1
url(r'^add$',views.add, name='add'),

2、在blog/views.py中添加方法:

1
2
3
4
5
6
7
8
9
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中添加:

1
url(r'^edit$',views.edit, name='edit'),

2、在blog/views.py中添加方法:

1
2
3
4
5
6
7
8
9
10
11
12
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:修改数据和增加数据可以合成为一个接口,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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中添加:

1
url(r'^delete$',views.delete, name='delete'),

2、在blog/views.py中添加方法:

1
2
3
4
5
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 JSONdjango的model对象转化成dict,修改代码如下:

1
2
3
4
5
6
7
8
9
10
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格式数据组装到一起。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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方法为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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请求时遇到如下错误:

1
CSRF verification failed. Request aborted.

解决办法,使用csrf_exempt装饰器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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:

1
2
3
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(),因此需要写成类似下面的形式:

1
2
3
4
5
6
7
8
9
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()函数会报错:

1
TypeError: Object of type 'datetime' is not JSON serializable

这是因为json.dumps()函数无法解析datetime格式的数据。
问题来了,auto_now=True时,json.dumps()却可以解析,莫非此时不是datetime格式?
且不管它,我们先解决datetime转json问题。

1
2
3
4
5
6
7
8
9
10
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参数:

1
json.dumps(result, cls=DateEncoder, ensure_ascii=False)

页面渲染

设置好时区后,在页面渲染时,会自动转化成当前时区时间,例如Nov. 29, 2017, 1:49 p.m.

但是,这种格式不符合我们的阅读习惯,我们可以在渲染时改成自己喜欢的格式:

1
{{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转换格式:

1
2
3
4
5
6
7
8
9
10
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

1
2
3
4
5
6
7
8
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如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
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接口为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
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入门与实践