解决了以下问题
首页实现了分页功能
把视图修改成类的通用视图ListView和DetailView
通用视图ListView 博客首页、分类、归档、标签等和首页类似的页面,都是同理的,都是一样的格式,无非是需要显示的文章的列表不一样。
关于类的通用视图的介绍,可以参考基于类的通用视图:ListView 和 DetailView
首先是针对首页的ListView 1 2 3 4 5 6 7 8 9 blog/views.py ... from django.views.generic import ListView... class IndexView (ListView ): model = Post template_name = 'blog/index.html' context_object_name = 'post_list' ...
同时,URL需要这样配置:
1 2 3 4 5 6 7 blog/urls.py app_name = 'blog' urlpatterns = [ url(r'^$' , views.IndexView.as_view(), name='index' ), ... ]
如果没有特殊需求,这样就足够了,就会显示所有的文章。
然后是分类页面Category的ListView 1 2 3 4 5 6 ... class CategoryView (IndexView ): def get_queryset (self ): cate = get_object_or_404(Category, pk=self .kwargs.get('pk' )) return super (CategoryView, self ).get_queryset().filter (category=cate) ...
CategoryView
可以直接继承IndexView
,因为它们有一些代码是相同的,可以减少代码的重复量。
URL配置如下:
1 2 3 4 5 6 7 blog/urls.py app_name = 'blog' urlpatterns = [ ... url(r'^category/(?P<pk>[0-9]+)/$' , views.CategoryView.as_view(), name='category' ), ]
再然后是归档Archives的ListView 1 2 3 4 5 6 7 8 9 10 ... class ArchivesView (IndexView ): def get_queryset (self ): year = self .kwargs.get('year' ) month = self .kwargs.get('month' ) return super (ArchivesView, self ).get_queryset().filter ( created_time__year=year, created_time__month=month, ) ...
这个和CategoryView是类似的。 同理,URL配置需要这样修改:
1 2 3 4 5 6 7 8 blog/urls.py app_name = 'blog' urlpatterns = [ ... url(r'^archives/(?P<year>[0-9]{4})/(?P<month>[0-9]{1,2})/$' , views.ArchivesView.as_view(), name='archives' ), ]
标签TagView不再重复 道理是一样的。
还有内容页的视图DetailView 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 blog/views.py from django.views.generic import ListView, DetailViewclass PostDetailView (DetailView ): model = Post template_name = 'blog/detail.html' context_object_name = 'post' def get (self, request, *args, **kwargs ): response = super (PostDetailView, self ).get(request, *args, **kwargs) self .object .increase_views() return response def get_object (self, queryset=None ): post = super (PostDetailView, self ).get_object(queryset=None ) renderer = HighlightRenderer() markdown = mistune.Markdown(renderer=renderer, hard_wrap=True ) blog.text = markdown(blog.text) return post
然后URL配置需要修改一下:
1 2 3 4 5 6 7 8 blog/urls.py app_name = 'blog' urlpatterns = [ ... url(r'^detail/(?P<pk>[0-9]+)/$' , views.BlogView.as_view(), name='detail' ), ... ]
这样设置以后,就可以正常使用了。
但是我遇到了以下的坑
我的首页,不只是有blog_list
这一个字典,还有网页的标题。
分页功能还没有加入进来。
内容页面,我还需要上一篇和下一篇这样的功能。
于是做以下修改 ListView
里面复写get_context_data
函数,就可以传递额外的参数,我这样加入网页标题和分页功能。
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 60 61 62 63 64 65 66 67 68 69 70 71 72 73 ... class IndexView (ListView ): template_name = 'blog/index.html' model = Post context_object_name = 'post_list' paginate_by = 10 def get_context_data (self, **kwargs ): context = super (IndexView, self ).get_context_data(**kwargs) context.update({ 'PageTitle' : self .get_page_title() + ' - 唐僧肉片' , }) paginator = context.get('paginator' ) page = context.get('page_obj' ) is_pagianted = context.get('is_paginated' ) page_data = self .pagination_data(paginator, page, is_pagianted) context.update(page_data) return context def pagination_data (self, paginator, page, is_pagenated ): if not is_pagenated: return {} left = [] right = [] left_has_more = False right_has_more = False first = False last = False page_number = page.number total_pages = paginator.num_pages page_range = list (paginator.page_range) if page_number == 1 : right = page_range[page_number:page_number + 2 ] if right[-1 ] < total_pages - 1 : right_has_more = True if right[-1 ] < total_pages: last = True elif page_number == total_pages: left = page_range[(page_number - 3 ) if (page_number - 3 ) > 0 else 0 :page_number - 1 ] if left[0 ] > 2 : left_has_more = True if left[0 ] > 1 : first = True else : left = page_range[(page_number - 3 ) if (page_number - 3 ) > 0 else 0 :page_number - 1 ] right = page_range[page_number:page_number + 2 ] if right[-1 ] < total_pages - 1 : right_has_more = True if right[-1 ] < total_pages: last = True if left[0 ] > 2 : left_has_more = True if left[0 ] > 1 : first = True data = { 'left' : left, 'right' : right, 'left_has_more' : left_has_more, 'right_has_more' : right_has_more, 'first' : first, 'last' : last, } return data def get_page_title (self ): return '博客首页' ...
这里的坑有:
如果是自己输入代码,一定要注意拼写,拼写错误真的很难发现。
get_page_title
函数是后来加的,在我看到这个函数是必不可少的。因为后面的CategoryView
,ArchivesView
,TagView
都是继承的IndexView
,IndexView
设置网页标题的函数在get_context_data
部分,如果不用get_page_title
函数,那么IndexView
派生的类,都需要重写这个get_context_data
函数来更改网页标题,这个函数还包含着分页功能部分,需要全部复制过来。而使用get_page_title
只是为了设置文章标题,派生类只需要重写get_page_title
这个函数即可。
分页部分基本上是复制的Django Pagination 完善分页 ,没有什么变化。
分页部分还要在index.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 29 ... {% if is_paginated %} <div class ="pagination" > {% if first %} <a href ="?page=1" > 1</a > {% endif %} {% if left %} {% if left_has_more %} <span > ...</span > {% endif %} {% for i in left %} <a href ="?page={{ i }}" > {{ i }}</a > {% endfor %} {% endif %} <a href ="?page={{ page_obj.number }}" style ="color: red" > {{ page_obj.number }}</a > {% if right %} {% for i in right %} <a href ="?page={{ i }}" > {{ i }}</a > {% endfor %} {% if right_has_more %} <span > ...</span > {% endif %} {% endif %} {% if last %} <a href ="?page={{ paginator.num_pages }}" > {{ paginator.num_pages }}</a > {% endif %} </div > {% endif %} ...
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 ... .pagination {margin-top : 25px ; text-align : center; width : 100% ;}.pagination a { display : inline-block; line-height : 38px ; padding : 0 15px ; margin-right : 4px ; text-align : center; background-color : #fff ; user-select : none; cursor : pointer; font-size : 14px ; border : 1px solid #d7dde4 ; border-radius : 4px ; transition : all .2s ease-in-out; } .pagination a :hover { color : #d2691e ; border : 1px solid #d2691e ; } .pagination .current-page { display : inline-block; color : #d2691e ; font-size : 14px ; } .pagination span { display : inline-block; font-size : 20px ; line-height : 38px ; padding : 0 8px ; margin-right : 4px ; }
所以派生类变成了这样 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 ... class ArchivesView (IndexView ): def get_queryset (self ): year = self .kwargs.get('year' ) month = self .kwargs.get('month' ) return super (ArchivesView, self ).get_queryset().filter ( created_time__year=year, created_time__month=month, ) def get_page_title (self ): year = self .kwargs.get('year' ) month = self .kwargs.get('month' ) return year + '年' + month + '月博客' class CategoryView (IndexView ): def get_queryset (self ): cate = get_object_or_404(Category, pk=self .kwargs.get('pk' )) return super (CategoryView, self ).get_queryset().filter (category=cate) def get_page_title (self ): cate = get_object_or_404(Category, pk=self .kwargs.get('pk' )) return '分类:' + cate.name class TagView (IndexView ): def get_queryset (self ): tag = get_object_or_404(Tag, pk=self .kwargs.get('pk' )) return super (TagView, self ).get_queryset().filter (tags=tag) def get_page_title (self ): tag = get_object_or_404(Tag, pk=self .kwargs.get('pk' )) return '标签:' + tag.name ...
每个派生类都重写了get_queryset
来获取文章列表,重写了get_page_title
更新页面标题。
博客详情页面也要修改 复制过来的代码,是没有上一篇和下一篇这样的功能的,于是需要自己写进去。无非就是把以前的代码更新一下写法就可以了。
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 ... def get_context_data (self, **kwargs ): pk = self .kwargs.get('pk' ) blog = get_object_or_404(Post, pk=pk) context = super (BlogView, self ).get_context_data(**kwargs) prev = super (BlogView, self ).get_queryset().filter ( pk__gt=pk, category=blog.category, ).order_by('pk' ) if prev.count() > 0 : prev = prev[0 ] else : prev = None next_blog = super (BlogView, self ).get_queryset().filter ( pk__lt=pk, category=blog.category, ).order_by('-pk' ) if next_blog.count() > 0 : next_blog = next_blog[0 ] else : next_blog = None context.update({ 'prev' : prev, 'next' : next_blog, }) return context ...
大功告成。
PS:写的有点乱,将就着看吧。