Skip to content

Commit a85e2b9

Browse files
author
yangxg
committed
Step21: class based view
1 parent 1a0a7a3 commit a85e2b9

File tree

3 files changed

+58
-37
lines changed

3 files changed

+58
-37
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ tutorial 分支为项目的主分支,每一篇教程的代码都和历史提
141141
18. [使用 Certbot 向 Let's Encrypt 免费申请 HTTPS 证书](https://www.zmrenwu.com/courses/hellodjango-blog-tutorial/materials/76/)
142142
19. [使用 Docker 让部署 Django 项目更加轻松](https://www.zmrenwu.com/courses/hellodjango-blog-tutorial/materials/77/)
143143
20. [开发博客文章阅读量统计功能](https://www.zmrenwu.com/courses/hellodjango-blog-tutorial/materials/78/)
144+
21. [Django 官方推荐的姿势:类视图](https://www.zmrenwu.com/courses/hellodjango-blog-tutorial/materials/79/)
144145

145146
## 公众号
146147
<p align="center">

blog/urls.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44

55
app_name = 'blog'
66
urlpatterns = [
7-
path('', views.index, name='index'),
8-
path('posts/<int:pk>/', views.detail, name='detail'),
9-
path('archives/<int:year>/<int:month>/', views.archive, name='archive'),
10-
path('categories/<int:pk>/', views.category, name='category'),
11-
path('tags/<int:pk>/', views.tag, name='tag'),
7+
path('', views.IndexView.as_view(), name='index'),
8+
path('posts/<int:pk>/', views.PostDetailView.as_view(), name='detail'),
9+
path('archives/<int:year>/<int:month>/', views.ArchiveView.as_view(), name='archive'),
10+
path('categories/<int:pk>/', views.CategoryView.as_view(), name='category'),
11+
path('tags/<int:pk>/', views.TagView.as_view(), name='tag'),
1212
]

blog/views.py

+52-32
Original file line numberDiff line numberDiff line change
@@ -3,52 +3,72 @@
33
import markdown
44
from django.shortcuts import get_object_or_404, render
55
from django.utils.text import slugify
6+
from django.views.generic import DetailView, ListView
67
from markdown.extensions.toc import TocExtension
78

89
from .models import Category, Post, Tag
910

1011

11-
def index(request):
12-
post_list = Post.objects.all()
13-
return render(request, 'blog/index.html', context={'post_list': post_list})
12+
class IndexView(ListView):
13+
model = Post
14+
template_name = 'blog/index.html'
15+
context_object_name = 'post_list'
1416

1517

16-
def archive(request, year, month):
17-
post_list = Post.objects.filter(created_time__year=year,
18-
created_time__month=month
19-
)
20-
return render(request, 'blog/index.html', context={'post_list': post_list})
18+
class CategoryView(IndexView):
2119

20+
def get_queryset(self):
21+
cate = get_object_or_404(Category, pk=self.kwargs.get('pk'))
22+
return super().get_queryset().filter(category=cate)
2223

23-
def category(request, pk):
24-
# 记得在开始部分导入 Category 类
25-
cate = get_object_or_404(Category, pk=pk)
26-
post_list = Post.objects.filter(category=cate)
27-
return render(request, 'blog/index.html', context={'post_list': post_list})
2824

25+
class ArchiveView(IndexView):
26+
def get_queryset(self):
27+
year = self.kwargs.get('year')
28+
month = self.kwargs.get('month')
29+
return super().get_queryset().filter(created_time__year=year,
30+
created_time__month=month)
2931

30-
def tag(request, pk):
31-
# 记得在开始部分导入 Tag 类
32-
t = get_object_or_404(Tag, pk=pk)
33-
post_list = Post.objects.filter(tags=t)
34-
return render(request, 'blog/index.html', context={'post_list': post_list})
3532

33+
class TagView(IndexView):
34+
def get_queryset(self):
35+
t = get_object_or_404(Tag, pk=self.kwargs.get('pk'))
36+
return super().get_queryset().filter(tags=t)
3637

37-
def detail(request, pk):
38-
post = get_object_or_404(Post, pk=pk)
3938

40-
# 阅读量 +1
41-
post.increase_views()
39+
# 记得在顶部导入 DetailView
40+
class PostDetailView(DetailView):
41+
# 这些属性的含义和 ListView 是一样的
42+
model = Post
43+
template_name = 'blog/detail.html'
44+
context_object_name = 'post'
4245

43-
md = markdown.Markdown(extensions=[
44-
'markdown.extensions.extra',
45-
'markdown.extensions.codehilite',
46-
# 记得在顶部引入 TocExtension 和 slugify
47-
TocExtension(slugify=slugify),
48-
])
49-
post.body = md.convert(post.body)
46+
def get(self, request, *args, **kwargs):
47+
# 覆写 get 方法的目的是因为每当文章被访问一次,就得将文章阅读量 +1
48+
# get 方法返回的是一个 HttpResponse 实例
49+
# 之所以需要先调用父类的 get 方法,是因为只有当 get 方法被调用后,
50+
# 才有 self.object 属性,其值为 Post 模型实例,即被访问的文章 post
51+
response = super().get(request, *args, **kwargs)
5052

51-
m = re.search(r'<div class="toc">\s*<ul>(.*)</ul>\s*</div>', md.toc, re.S)
52-
post.toc = m.group(1) if m is not None else ''
53+
# 将文章阅读量 +1
54+
# 注意 self.object 的值就是被访问的文章 post
55+
self.object.increase_views()
5356

54-
return render(request, 'blog/detail.html', context={'post': post})
57+
# 视图必须返回一个 HttpResponse 对象
58+
return response
59+
60+
def get_object(self, queryset=None):
61+
# 覆写 get_object 方法的目的是因为需要对 post 的 body 值进行渲染
62+
post = super().get_object(queryset=None)
63+
md = markdown.Markdown(extensions=[
64+
'markdown.extensions.extra',
65+
'markdown.extensions.codehilite',
66+
# 记得在顶部引入 TocExtension 和 slugify
67+
TocExtension(slugify=slugify),
68+
])
69+
post.body = md.convert(post.body)
70+
71+
m = re.search(r'<div class="toc">\s*<ul>(.*)</ul>\s*</div>', md.toc, re.S)
72+
post.toc = m.group(1) if m is not None else ''
73+
74+
return post

0 commit comments

Comments
 (0)