本渣渣不专注技术,只专注使用技术,不是一个资深的coder,是一个不折不扣的copier |
1、博客详情页面
博客的详情页同样继承自 base.html 模板,侧边栏就不需要了,这里老规矩,不一块一块的讲解,这个自己要
blog -> storm -> view.py
class DetailView(generic.DetailView): """ Django有基于类的视图DetailView,用于显示一个对象的详情页,我们继承它 """ # 获取数据库中的文章列表 model = Article # template_name属性用于指定使用哪个模板进行渲染 template_name = 'article.html' # context_object_name属性用于给上下文变量取名(在模板中使用该名字) context_object_name = 'article' def get_object(self): obj = super(DetailView, self).get_object() # 设置浏览量增加时间判断,同一篇文章两次浏览超过半小时才重新统计阅览量,作者浏览忽略 u = self.request.user ses = self.request.session the_key = 'is_read_{}'.format(obj.id) is_read_time = ses.get(the_key) if u != obj.author: if not is_read_time: obj.update_views() ses[the_key] = time.time() else: now_time = time.time() t = now_time - is_read_time if t > 60 * 30: obj.update_views() ses[the_key] = time.time() # 文章可以使用markdown书写,带格式的文章,像csdn写markdown文章一样 md = markdown.Markdown(extensions=[ 'markdown.extensions.extra', 'markdown.extensions.codehilite', TocExtension(slugify=slugify), ]) obj.body = md.convert(obj.body) obj.toc = md.toc return obj def get_context_data(self, **kwargs): context = super(DetailView, self).get_context_data(**kwargs) context['category'] = self.object.id return context
blog -> storm -> urls.py
# --------------------------- __author__='stormsha' __date__='2019/3/10 21:03' # --------------------------- from django.conf.urls import url from .views import IndexView, DetailView urlpatterns = [ # 首页 url(r'^$', IndexView.as_view(template_name='index.html'), name='index'), # 主页,自然排序 # 给我留言 url(r'^category/message/$', MessageView, name='message'), # 关于自己 url(r'^category/about/$', AboutView, name='about'), # 赞助作者 url(r'^category/donate/$', DonateView, name='donate'), # 技术交流 url(r'^category/exchange/$', ExchangeView, name='exchange'), # 项目合作 url(r'^category/project/$', ProjectView, name='project'), # 提问交流 url(r'^category/question/$', QuestionView, name='question'), # 分类页面 url(r'^category/(?P<bigslug>.*?)/(?P<slug>.*?)', IndexView.as_view(template_name='content.html'), name='category'), # 归档页面 url(r'^date/(?P<year>\d+)/(?P<month>\d+)/$', IndexView.as_view(template_name='archive.html'), name='date'), # 标签页面 url(r'^tag/(?P<tag>.*?)/$', IndexView.as_view(template_name='content.html'), name='tag'), # 文章详情页面 url(r'^article/(?P<slug>.*?)/$', DetailView.as_view(), name='article'), ]
新添加的路由
url(r'^article/(?P<slug>.*?)/$', DetailView.as_view(), name='article'),
首页模板文章列表部分代码
<!--文章导航条--> <header> <!--lower 统一英文字符为小写--> <a class="label label-important" href="/category/{{ article.category.bigcategory.name }}/{{ article.category.name|lower }}">{{ article.category.name }}<i class="label-arrow"></i> </a> <!--文章标题--> <h2> <a target="_blank" href="/article/{{ article.slug }}" title="{{ article.title }}">{{ article.title }} </a> </h2> </header> <!--文章缩略图--> <div class="focus"> <a target="_blank" href="/article/{{ article.slug }}"><img class="thumb" width="200" height="123" src="{{ article.img_link }}" alt="{{ article.title }}"/> </a> </div>
前端请求的路径,带一个文章slug参数、slug是文章的唯一标识符,也称作文章别名,别问有啥用,就是提升逼格
/article/{{ article.slug }}
详情页模板
这里只展示部分代码做说明,源码参照Github
blog -> templates -> article.html
<article class="article-content"> # 文章内容,safe过滤器,把文章中带的标签转换成浏览器可解析的编码 {{ article.body|safe }} <p>转载请注明: <a href="{% url 'blog:article' object.slug %}">StormSha</a> » <a href="{% url 'blog:article' object.slug %}">{{ object.title }}</a> </p> </article>
object: 是通用基类方法get_object返回的文章详情对象
推荐阅读:通用基类
2、实现博客的上一篇与下一篇功能
为了实现这个功能,我们需要在templatetags中获取该篇博客的上一篇博客以及下一篇博客。为了实现这个功能,我们需要考虑到以下2种情况:
博客的id号有可能是不连续的,当我们删除一篇博客时,连带着该id号一块删除了,如果通过blog_id - 1和blog_id+1来获取博客的上一篇与下一篇,则有可能会出错。如果是第一篇博客,它是没有上一篇的;同样,最后一篇博客也是没有下一篇的 因此,我们可以使用状态码has_prev和has_next来判断博客是否有上一篇和下一篇,并通过对id进行循环来判断id号码的连续性,实现代码如下:
blog -> templatetags -> blog_tags.py
# 获取前一篇文章,参数当前文章 ID @register.simple_tag def get_article_previous(article_id): has_previous = False id_previous = int(article_id) while not has_previous and id_previous >= 1: article_previous = Article.objects.filter(id=id_previous - 1).first() if not article_previous: id_previous -= 1 else: has_previous = True if has_previous: article = Article.objects.filter(id=id_previous).first() return article else: return # 获取下一篇文章,参数当前文章 ID @register.simple_tag def get_article_next(article_id): has_next = False id_next = int(article_id) article_id_max = Article.objects.all().order_by('-id').first() id_max = article_id_max.id while not has_next and id_next <= id_max: article_next = Article.objects.filter(id=id_next + 1).first() if not article_next: id_next += 1 else: has_next = True if has_next: article = Article.objects.filter(id=id_next).first() return article else: return
blog -> templates -> article.html
<nav class="article-nav"> <span class="article-nav-prev"> <i class="fa fa-angle-double-left"></i> {% get_article_previous object.id as article_previous %} <a href="{% url 'blog:article' article_previous.slug %}" rel="prev">{{ article_previous.title }}</a> </span> <span class="article-nav-next"> {% get_article_next object.id as article_next %} <a href="{% url 'blog:article' article_next.slug %}" rel="prev">{{ article_next.title }}</a> <i class="fa fa-angle-double-right"></i> </span> </nav>
如此,即可实现博客的上一篇与下一篇功能。
{% include 'comment_list.html' %}
源码详情页中的评论功能,再用户注册、登录、修改个人中心后讲解,重要的留在最后
【友情提示】——如果发现有表达错误,或者知识点错误,或者搞不懂的地方,请及时留言,可以在评论区互相帮助,让后来者少走弯路是我的初衷。我也是一步步摸着石头走过来的,深知网络上只言片语的图文教程,给初学者带来的深深困扰。
【建议】——在对项目结构不太熟悉时,参照完整源码少走弯路
转载请注明: StormSha » Django个人博客开发十二 | 博客详情页面
url(r'^article/(?P<slug>.*?)/$', DetailView.as_view(), name='article')
这一段里的.*?
问号是不是多余了?我也是在学习中,说错莫怪不多于,
url(r'^article/(?P<slug>.*?)/$', DetailView.as_view(), name='article')
.*?
可以改为[-\w+]
、.*
、.
都可以,如果直接把.*?
去掉是不可以,这里的原理是正则匹配,<slug>
是把匹配到的内容赋值给slug