博客文章详情页

首页展示的是所有文章的列表,当用户看到感兴趣的文章时,他点击文章的标题或者继续阅读的按钮,应该跳转到文章的详情页面来阅读文章的详细内容。现在让我们来开发博客的详情页面,有了前面的基础,开发流程都是一样的了:首先配置 URL,即把相关的 URL 和视图函数绑定在一起,然后实现视图函数,编写模板并让视图函数渲染模板。

设计文章详情页的 URL

回顾一下我们首页视图的 URL,在 blog\urls.py 文件里,我们写了:

blog/urls.py

from django.conf.urls import url

from . import views

urlpatterns = [
    url(r'^$', views.index, name='index'),
]

首页视图匹配的 URL 去掉域名后其实就是一个空的字符串。对文章详情视图而言,每篇文章对应着不同的 URL。比如我们可以把文章详情页面对应的视图设计成这个样子:当用户访问 <网站域名>/post/1/ 时,显示的是第一篇文章的内容,而当用户访问 <网站域名>/post/2/ 时,显示的是第二篇文章的内容,这里数字代表了第几篇文章,也就是数据库中 Post 记录的 id 值。下面依照这个规则来绑定 URL 和视图:

blog/urls.py

from django.conf.urls import url

from . import views

app_name = 'blog'
urlpatterns = [
    url(r'^$', views.index, name='index'),
    url(r'^post/(?P<pk>[0-9]+)/$', views.detail, name='detail'),
]

Django 使用正则表达式来匹配用户访问的网址。这里 r'^post/(?P<pk>[0-9]+)/$' 整个正则表达式刚好匹配我们上面定义的 URL 规则。这条正则表达式的含义是,以 post/ 开头,后跟一个至少一位数的数字,并且以 / 符号结尾,如 post/1/、 post/255/ 等都是符合规则的,[0-9]+ 表示一位或者多位数。此外这里 (?P<pk>[0-9]+) 表示命名捕获组,其作用是从用户访问的 URL 里把括号内匹配的字符串捕获并作为关键字参数传给其对应的视图函数 detail。比如当用户访问 post/255/ 时(注意 Django 并不关心域名,而只关心去掉域名后的相对 URL),被括起来的部分 (?P<pk>[0-9]+) 匹配 255,那么这个 255 会在调用视图函数 detail 时被传递进去,实际上视图函数的调用就是这个样子:detail(request, pk=255)。我们这里必须从 URL 里捕获文章的 id,因为只有这样我们才能知道用户访问的究竟是哪篇文章。

可能上述的正则表达式你有点难以理解,关于正则表达式的部分并非 Django 相关的内容,而是 Python 的内容。Django 只是在这里使用了 Python 处理正则表达式的 re 模块。因此如果想更好地理解 Python 中正则表达式的相关知识,请自行查看 Python 官方文档中 re 模块的文档。

此外我们通过 app_name='blog' 告诉 Django 这个 urls.py 模块是属于 blog 应用的,这种技术叫做视图函数命名空间。我们看到 blog\urls.py 目前有两个视图函数,并且通过 name 属性给这些视图函数取了个别名,分别是 index、detail。但是一个复杂的 Django 项目可能不止这些视图函数,例如一些第三方应用中也可能有叫 index、detail 的视图函数,那么怎么把它们区分开来,防止冲突呢?方法就是通过 app_name 来指定命名空间,命名空间具体如何使用将在下面介绍。如果你忘了在 blog\urls.py 中添加这一句,接下来你可能会得到一个 NoMatchReversed 异常。

为了方便地生成上述的 URL,我们在 Post 类里定义一个 get_absolute_url 方法,注意 Post 本身是一个 Python 类,在类中我们是可以定义任何方法的。

blog/models.py

from django.db import models
from django.contrib.auth.models import User
from django.urls import reverse
from django.utils.six import python_2_unicode_compatible

@python_2_unicode_compatible
class Post(models.Model):
    ...

    def __str__(self):
        return self.title

    # 自定义 get_absolute_url 方法
    # 记得从 django.urls 中导入 reverse 函数
    def get_absolute_url(self):
        return reverse('blog:detail', kwargs={'pk': self.pk})

注意到 URL 配置中的 url(r'^post/(?P<pk>[0-9]+)/$', views.detail, name='detail') ,我们设定的 name='detail' 在这里派上了用场。看到这个 reverse 函数,它的第一个参数的值是 'blog:detail',意思是 blog 应用下的 name=detail 的函数,由于我们在上面通过 app_name = 'blog' 告诉了 Django 这个 URL 模块是属于 blog 应用的,因此 Django 能够顺利地找到 blog 应用下 name 为 detail 的视图函数,于是 reverse 函数会去解析这个视图函数对应的 URL,我们这里 detail 对应的规则就是 post/(?P<pk>[0-9]+)/ 这个正则表达式,而正则表达式部分会被后面传入的参数 pk 替换,所以,如果 Post 的 id(或者 pk,这里 pk 和 id 是等价的) 是 255 的话,那么 get_absolute_url 函数返回的就是 /post/255/ ,这样 Post 自己就生成了自己的 URL。

编写 detail 视图函数

接下来就是实现我们的 detail 视图函数了:

blog/views.py

from django.shortcuts import render, get_object_or_404
from .models import Post

def index(request):
    # ...

def detail(request, pk):
    post = get_object_or_404(Post, pk=pk)
    return render(request, 'blog/detail.html', context={'post': post})

视图函数很简单,它根据我们从 URL 捕获的文章 id(也就是 pk,这里 pk 和 id 是等价的)获取数据库中文章 id 为该值的记录,然后传递给模板。注意这里我们用到了从 django.shortcuts 模块导入的 get_object_or_404 方法,其作用就是当传入的 pk 对应的 Post 在数据库存在时,就返回对应的 post,如果不存在,就给用户返回一个 404 错误,表明用户请求的文章不存在。

编写详情页模板

接下来就是书写模板文件,从下载的博客模板(如果你还没有下载,请 点击这里 下载)中把 single.html 拷贝到 templates\blog 目录下(和 index.html 在同一级目录),然后改名为 detail.html。此时你的目录结构应该像这个样子:

blogproject\
    manage.py
    blogproject\
        __init__.py
        settings.py
        ...
    blog/
        __init__.py
        models.py
        ,,,
    templates\
        blog\
            index.html
            detail.html

在 index 页面博客文章列表的标题继续阅读按钮写上超链接跳转的链接,即文章 post 对应的详情页的 URL,让用户点击后可以跳转到 detail 页面:

templates/blog/index.html

<article class="post post-1">
  <header class="entry-header">
    <h1 class="entry-title">
      <a href="{{ post.get_absolute_url }}">{{ post.title }}</a>
    </h1>
    ...
  </header>
  <div class="entry-content clearfix">
    ...
    <div class="read-more cl-effect-14">
      <a href="{{ post.get_absolute_url }}" class="more-link">继续阅读 <span class="meta-nav"></span></a>
    </div>
  </div>
</article>
{% empty %}
  <div class="no-post">暂时还没有发布的文章!</div>
{% endfor %}

这里我们修改两个地方,第一个是文章标题处:

<h1 class="entry-title">
  <a href="{{ post.get_absolute_url }}">{{ post.title }}</a>
</h1>

我们把 a 标签的 href 属性的值改成了 {{ post.get_absolute_url }}。回顾一下模板变量的用法,由于 get_absolute_url 这个方法(我们定义在 Post 类中的)返回的是 post 对应的 URL,因此这里 {{ post.get_absolute_url }} 最终会被替换成该 post 自身的 URL。

同样,第二处修改的是继续阅读按钮的链接:

<a href="{{ post.get_absolute_url }}" class="more-link">继续阅读 <span class="meta-nav"></span>
</a>

这样当我们点击首页文章的标题或者继续阅读按钮后就会跳转到该篇文章对应的详情页面了。然而如果你尝试跳转到详情页后,你会发现样式是乱的。这在 真正的 Django 博客首页 时讲过,由于我们是直接复制的模板,还没有正确地处理静态文件。我们可以按照介绍过的方法修改静态文件的引入路径,但很快你会发现在任何页面都是需要引入这些静态文件,如果每个页面都要修改会很麻烦,而且代码都是重复的。下面就介绍 Django 模板继承的方法来帮我们消除这些重复操作。

模板继承

我们看到 index.html 文件和 detail.html 文件除了 main 标签包裹的部分不同外,其它地方都是相同的,我们可以把相同的部分抽取出来,放到 base.html 里。首先在 templates\ 目录下新建一个 base.html 文件,这时候你的项目目录应该变成了这个样子:

blogproject\
    manage.py
    blogproject\
        __init__.py
        settings.py
        ...
    blog\
        __init__.py
        models.py
        ,,,
    templates\
        base.html
        blog\
            index.html
            detail.html

把 index.html 的内容全部拷贝到 base.html 文件里,然后删掉 main 标签包裹的内容,替换成如下的内容。

templates/base.html

...
<main class="col-md-8">
    {% block main %}
    {% endblock main %}
</main>
<aside class="col-md-4">
  {% block toc %}
  {% endblock toc %}
  ...
</aside>
...

这里 block 也是一个模板标签,其作用是占位。比如这里的 {% block main %}{% endblock main %} 是一个占位框,main 是我们给这个 block 取的名字。下面我们会看到 block 标签的作用。同时我们也在 aside 标签下加了一个 {% block toc %}{% endblock toc %} 占位框,因为 detail.html 中 aside 标签下会多一个目录栏。当 {% block toc %}{% endblock toc %} 中没有任何内容时,{% block toc %}{% endblock toc %} 在模板中不会显示。但当其中有内容是,模板就会显示 block 中的内容。

在 index.html 里,我们在文件最顶部使用 {% extends 'base.html' %} 继承 base.html,这样就把 base.html 里的代码继承了过来,另外在 {% block main %}{% endblock main %} 包裹的地方填上 index 页面应该显示的内容:

templates/blog/index.html

{% extends 'base.html' %}

{% block main %}
    {% for post in post_list %}
        <article class="post post-1">
          ...
        </article>
    {% empty %}
        <div class="no-post">暂时还没有发布的文章!</div>
    {% endfor %}
    <!-- 简单分页效果
    <div class="pagination-simple">
        <a href="#">上一页</a>
        <span class="current">第 6 页 / 共 11 页</span>
        <a href="#">下一页</a>
    </div>
    -->
    <div class="pagination">
      ...
    </div>
{% endblock main %}

这样 base.html 里的代码加上 {% block main %}{% endblock main %} 里的代码就和最开始 index.html 里的代码一样了。这就是模板继承的作用,公共部分的代码放在 base.html 里,而其它页面不同的部分通过替换 {% block main %}{% endblock main %} 占位标签里的内容即可。

如果你对这种模板继承还是有点糊涂,可以把这种继承和 Python 中类的继承类比。base.html 就是父类,index.html 就是子类。index.html 继承了 base.html 中的全部内容,同时它自身还有一些内容,这些内容就通过 “覆写” {% block main %}{% endblock main %}(把 block 看做是父类的属性)的内容添加即可。

detail 页面处理起来就简单了,同样继承 base.html ,在 {% block main %}{% endblock main %} 里填充 detail.html 页面应该显示的内容,以及在 {% block toc %}{% endblock toc %} 中填写 base.html 中没有的目录部分的内容。不过目前的目录只是占位数据,我们在以后会实现如何从文章中自动摘取目录。

templates/blog/detail.html

{% extends 'base.html' %}

{% block main %}
    <article class="post post-1">
      ...
    </article>
    <section class="comment-area">
      ...
    </section>
{% endblock main %}
{% block toc %}
    <div class="widget widget-content">
        <h3 class="widget-title">文章目录</h3>
        <ul>
            <li>
                <a href="#">教程特点</a>
            </li>
            <li>
                <a href="#">谁适合这个教程</a>
            </li>
            <li>
                <a href="#">在线预览</a>
            </li>
            <li>
                <a href="#">资源列表</a>
            </li>
            <li>
                <a href="#">获取帮助</a>
            </li>
        </ul>
    </div>
{% endblock toc %}

修改 article 标签下的一些内容,让其显示文章的实际数据:

<article class="post post-{{ post.pk }}">
  <header class="entry-header">
    <h1 class="entry-title">{{ post.title }}</h1>
    <div class="entry-meta">
      <span class="post-category"><a href="#">{{ post.category.name }}</a></span>
      <span class="post-date"><a href="#"><time class="entry-date"
                                                datetime="{{ post.created_time }}">{{ post.created_time }}</time></a></span>
      <span class="post-author"><a href="#">{{ post.author }}</a></span>
      <span class="comments-link"><a href="#">4 评论</a></span>
      <span class="views-count"><a href="#">588 阅读</a></span>
    </div>
  </header>
  <div class="entry-content clearfix">
    {{ post.body }}
  </div>
</article>

再次从首页点击一篇文章的标题或者继续阅读按钮跳转到详情页面,可以看到预期效果了!

博客文章详情页

总结

本章节的代码位于:Step8: blog detail view

如果遇到问题,请通过下面的方式寻求帮助。

  • 在下方评论区留言。
  • 将问题的详细描述通过邮件发送到 djangostudyteam@163.com,一般会在 24 小时内回复。
  • Pythonzhcn 社区的新手问答版块 发布帖子。

-- EOF --


150 条评论 / 69 人参与
JackSu

还有几个问题哈,如果我删除了一个post,继续增加post,会发现删除的那个post的pk好像会占用一个位置,也就是说一共只有三个post但是第四个post还是pk=4。

如果我要查看sqlite3数据库里面的东西该如何查看呢,在学校一直用的mysql,有可视化工具,可是这个我都不知道数据库在哪里


追梦人物 [博主] JackSu

用数据库可视化软件打开 db.sqlite3 就可以了,和mysql管理是一样的。pk主键是递增的,一旦设定就无法更改,所以数字只会递增,删除掉一些记录后主键就会不连续,这是主键的特点。


JackSu

博主你好,看了你这个教程受益颇多,但是这里还是有一个问题,在生成post列表显示index里面时,post.pk并没有值啊


追梦人物 [博主] JackSu

确保模板中正确地引用:{{ post.pk }},确保post变量的确是文章记录,比如post.title 有值。


xusanpang

test


聴憩_dandelion

你好,我想请问下点击继续为什么不是跳转到此文章具体内容(后台添加的文章),而是我们自己写的detail.html,还有看不懂那个reverse是怎么能够展示文章内容,该怎么查找相关知识呢?


Chen xianmin 聴憩_dandelion

我是初学者,试着回答一下。

1. 在index页面,点击继续后,实现代码应该是下面这段

<div class="read-more cl-effect-14"> <a href="{{post.get_absolute_url}}" class="more-link">继续阅读 <span class="meta-nav">→</span></a></div>

那么会发送请求request,跳到相应的博客url。

2. 上面的request请求会被视图函数detail(views.py里面的)处理,如下:

def detail(request, pk): 

 post = get_object_or_404(Post, pk=pk) 

 return render(request, 'blog/detail.html', context={'post': post})

接着就跳到detail页面,并把相应的post对象当做模板变量传到render函数里面,就把detail页面渲染出来了。


聴憩_dandelion Chen xianmin

太棒了你,谢谢,解决了我的疑问


聴憩_dandelion Chen xianmin

还有个问题,那这样要怎么能够获取到后台的数据页面展示出来


Chen xianmin 聴憩_dandelion

def detail(request, pk): 

 post = get_object_or_404(Post, pk=pk)   #这句已经能够从数据库里面把Post类的对象取出来了,即博客数据

 post.body = markdown.markdown(post.body, extensions=['markdown.extensions.extra', 'markdown.extensions.codehilite', 'markdown.extensions.toc', ]) 

 return render(request, 'blog/detail.html', context={'post': post}) #这句就把之前的对象传到detail.html页面去显示了


vbcpascal

你好,请问出现
url(r'post/(?P<pk>[0-9]+)/', views.detail, name='detail'),

SyntaxError: invalid syntax

是什么问题?谢谢。


vbcpascal vbcpascal

修正一下:

url(r'^post/(?P<pk>[0-9]+)/$', views.detail, name='detail'),

但还是错误


Chen xianmin vbcpascal

url(r'^post/(?P<pk>[0-9]+)/$', views.detail, name='detail'),

看看是不是一样


追梦人物 [博主] vbcpascal

没看出来有错误,可能不是这里的原因。


風与飄颻

index file miss this line ({% load static %}) and then cause some error.

Thanks so much. Really!


luohua13950

假设用户点击一个网页:/post/255/,这里的255是怎么生成的,点这个网页的时候这255就生成了吗?用户在打开一个网页的时候肯定要生成一个URL给django匹配,比较纳闷这个用户点击的URL是怎么来的(我知道流程:点击一个网页,django传到URL里匹配,匹配到调用相应视图函数,视图函数再渲染模板)


追梦人物 [博主] luohua13950

我们通常在 a 标签的 href 属性设置用户要访问的 URL。教程中就有示例,例如点击标题跳转。


Viman

博主你好, 有个疑问想请帮忙解答下。

按照你博客写的我在 blog/urls.py 下配置了 url(r'^post/(?P<pk>[0-9]+)/$', views.detail, name='detail'), 

然后在Model 里面的Post类下面get_absolute_url 函数reverse方法 return reverse('blog:detail', kwargs={'pk': self.pk})    是无法成功匹配到正则的。 

改成如下,却又可以:

blogproject/urls.py 下配置 url(r'^post/(?P<pk>[0-9]+)/$', views.detail, name='detail'),  然后reverse方法 return reverse('detail', kwargs={'pk': self.pk})   内除去appname匹配正则正常了。


拖延症晚期患者SOS Viman

我也是 ,按你的这样改就可以了,不明所以


Joker丶ST

博主你好,,我在详情页加了两个标签上一篇和下一篇文章,,,请问怎么获取前一篇和下一篇的url以及标题呢


xinyuebaiyun

测试


hsrowl

博主您好

我现在不想只是显示个id,想url后面部分自定义成文章标题的拼音:

/post/255/ →/post/wenzhangxiangqing/ 

我看了下django-uuslug

这是要动数据库模型吗?有没有简单一点的方法呢?


追梦人物 [博主] hsrowl

你要在model中添加slug字段,并且修改相应的 url patern


hsrowl 追梦人物 [博主]

好了 感谢!


nine好 hsrowl

你在urls里调用了相应数据表的title吗?


nine好 追梦人物 [博主]

博主你好!

我想把url后面部分自定义成中文编码!

/post/255/ →/post/%E6%88%91%E7%9A%84%E5%8D%9A%E5%AE%A2/ 

一定要动数据库才能行吗,有没有简单点的方法


nine好 追梦人物 [博主]

还有请问博主(?P<pk>[0-9]+)为什么不是[0-9]+而一定要在前面写上?P<pk>这是为什么?


追梦人物 [博主] nine好

这是浏览器编码,django无能为力的,有些浏览器会显示中文,有些则是显示这种编码。


追梦人物 [博主] nine好

看看 python re 正则模块的命名捕获组。


墨磨墨磨叽墨迹河H

请问一下,这个错误到底哪里解决啊,代码也对了半天实在是无解了。

TypeError at /_reverse_with_prefix() argument after ** must be a mapping, not setRequest Method:GETRequest URL:http://127.0.0.1:8000/Django Version:2.0.3Exception Type:TypeErrorException Value:_reverse_with_prefix() argument after ** must be a mapping, not setException Location:/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/django/urls/base.py in reverse, line 88Python Executable:/Library/Frameworks/Python.framework/Versions/3.6/bin/python3Python Version:3.6.4Python Path:['/Users/Evenhb/Documents/python_test/Django/myblog', '/Library/Frameworks/Python.framework/Versions/3.6/lib/python36.zip', '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6', '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/lib-dynload', '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages']Server time:星期六, 7 四月 2018 17:58:35 +0800Error during template renderingIn template /Users/Evenhb/Documents/python_test/Django/myblog/templates/base.html, error at line 0


追梦人物 [博主] 墨磨墨磨叽墨迹河H

请帖出相关的错误代码。


孤云飘飘zhao

我后来在其他地方发现一个更好解决获取url路径的方法.老师教的这种在models中添加一个方法来获取也是一种思想,但是我感觉可以直接在模板中就获取了.用url 'blog:detail' 这种方法也可以直接获取到对应应用中的视图


奇楠之后

哈哈哈,看了好几遍终于看懂了。博主有好几句话很重要却很容易让我给忽略了,建议认真看逐句分析理解,虽然有一段确实绕,对新手的我,收获很多。感谢博主的分享。


MarioBai

我在运行这个到这里的时候发生

AttributeError at /blog/post/3/'list' object has no attribute 'body'

 Request Method:GET 

Request URL:http://127.0.0.1:8000/blog/post/3/ 

Django Version:1.11

 Exception Type:AttributeErrorException Value:'list' object has no attribute 'body'Exception Location:/Users/bai/pyproject/pdjango/myblogs/blog/views.py in detail, line 20Python Executable:/Users/bai/anaconda3/bin/python 

Python Version:3.6.0Python 

Path:['/Users/bai/pyproject/pdjango/myblogs', '/Users/bai/anaconda3/lib/python36.zip', '/Users/bai/anaconda3/lib/python3.6', '/Users/bai/anaconda3/lib/python3.6/lib-dynload', '/Users/bai/anaconda3/lib/python3.6/site-packages', '/Users/bai/anaconda3/lib/python3.6/site-packages/Sphinx-1.5.1-py3.6.egg', '/Users/bai/anaconda3/lib/python3.6/site-packages/aeosa', '/Users/bai/anaconda3/lib/python3.6/site-packages/setuptools-27.2.0-py3.6.egg'] 

Server time:星期五, 26 一月 2018 19:34:56 +0800
这个是什么原因啊?


追梦人物 [博主] MarioBai

'list' object has no attribute 'body'

说明你返回的 post 是一个 list,请检查返回的值确保正确。


Linux-2017

没有url,可以用path实现url(r'^post/(?P<pk>[0-9]+)/$', views.detail, name='detail'),这个吗


陈少侠不是江湖人 Linux-2017

可以,path('post/<int:pk>/', views.detail, name='detail'),


erwwwwwwww

--URL 命名空间还是有点搞不清

url(r'^$', views.IndexView.as_view(), name='index'),

url(r'^author-polls/', include('polls.urls', namespace='author-polls', app_name='polls'))

网上很多是 {% url 'author:index' %}    ,这样使用但是 app_name到底有什么用呢


damonfly

在这里  https://segmentfault.com/q/1010000002802975  你在外层的 urlpatterns 用了 include 吧?用 include() 的时候,请不要加 $


请问什么 使用 include()时 不要加 $                ?


damonfly

本人环境Mac py3.6.3 Django1.11.6  按照教程里面代码,如下 

在blogproject/urls.py  里面 urlpatterns = [    url(r'^admin/', admin.site.urls),    url(r'^$', include('blog.urls')),]在blog/urls.py里面 urlpatterns = [    url(r'^$', views.index, name='index'),    url(r'^post/(?P<pk>[0-9]+)/$', views.detail, name='detail'),]  会报错:Error during template renderingIn template /Users/dev/temp_python/temp02-django/blogproject/templates/blog/index.html, error at line 74Reverse for 'detail' with keyword arguments '{'pk': 1}' not found. 1 pattern(s) tried: ['$post/(?P<pk>[0-9]+)/$']64<div class="copyrights">Modified by <a href="http://zmrenwu.com/">追梦人物的博客</a></div>6566<div class="content-body">67 <div class="container">68 <div class="row">69 <main class="col-md-8">70 {% for post in post_list %}71 <article class="post post-{{ post.pk }}">72 <header class="entry-header">73 <h1 class="entry-title">74 <a href="{{ post.get_absolute_url }}">{{ post.title }}</a> 

解决方法是 ,在blogproject/urls.py  里面改为 urlpatterns = [     url(r'^admin/', admin.site.urls),     url(r'', include('blog.urls')), ]在blogproject/urls.py  去掉对 ^$ 对空字符串的匹配直接使用空字符串 '' (双单引号内不含空格) 则程序正常运行。。。 


damonfly

本人环境Mac py3.6.3 Django1.11.6  按照教程里面代码,如下

在blogproject/urls.py  里面 urlpatterns = [    url(r'^admin/', admin.site.urls),    url(r'^$', include('blog.urls')),]在blog/urls.py里面 urlpatterns = [    url(r'^$', views.index, name='index'),    url(r'^post/(?P<pk>[0-9]+)/$', views.detail, name='detail'),]  

会报错:

Error during template renderingIn template /Users/dev/temp_python/temp02-django/blogproject/templates/blog/index.html, error at line 74Reverse for 'detail' with keyword arguments '{'pk': 1}' not found. 1 pattern(s) tried: ['$post/(?P<pk>[0-9]+)/$']64<div class="copyrights">Modified by <a href="http://zmrenwu.com/">追梦人物的博客</a></div>6566<div class="content-body">67 <div class="container">68 <div class="row">69 <main class="col-md-8">70 {% for post in post_list %}71 <article class="post post-{{ post.pk }}">72 <header class="entry-header">73 <h1 class="entry-title">74 <a href="{{ post.get_absolute_url }}">{{ post.title }}</a>

解决方法是 ,在blogproject/urls.py  里面改为 urlpatterns = [     url(r'^admin/', admin.site.urls),     url(r'', include('blog.urls')), ]在blog/urls.py   去掉对 ^$ 对空字符串的匹配直接使用空字符串 '' (双单引号内不含空格) 则程序正常运行。。。 


damonfly damonfly

damonfly本人环境Mac py3.6.3 Django1.11.6  按照教程里面代码,如下在blogproject/urls.py  里面 urlpatterns = [    url(r'^admin/', admin.site.urls),    url(r'^$', include('blog.urls')),]在blog/urls.py里面 urlpatterns = [    url(r'^$', views.index, name='index'),    url(r'^post/(?P<pk>[0-9]+)/$', views.detail, name='detail'),]  会报错:Error during template renderingIn template /Users/dev/temp_python/temp02-django/blogproject/templates/blog/index.html, error at line 74Reverse for 'detail' with keyword arguments '{'pk': 1}' not found. 1 pattern(s) tried: ['$post/(?P<pk>[0-9]+)/$']64<div class="copyrights">Modified by <a href="http://zmrenwu.com/">追梦人物的博客</a></div>6566<div class="content-body">67 <div class="container">68 <div class="row">69 <main class="col-md-8">70 {% for post in post_list %}71 <article class="post post-{{ post.pk }}">72 <header class="entry-header">73 <h1 class="entry-title">74 <a href="{{ post.get_absolute_url }}">{{ post.title }}</a>解决方法是 ,在blogproject/urls.py  里面改为 urlpatterns = [     url(r'^admin/', admin.site.urls),     url(r'', include('blog.urls')), ]在blog/urls.py (这里我手快有误应该是blogproject/urls.py)  去掉对 ^$ 对空字符串的匹配直接使用空字符串 '' (双单引号内不含空格) 则程序正常运行。。。 


malone6

"{{ post.get_absolute_url }}"无法获得详情页的url时,要将blogproject/urls改为 url(r'',include('blog.urls',namespace = 'blog')),

![](http://upload-images.jianshu.io/upload_images/2046288-e9a217810acab27c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


malone6 malone6

加图片好麻烦。建议楼主加入评论图片功能


dayday malone6

赞一个,解决了我的问题


生而赴死欸

Unknown command: 'runserve'

 Type 'manage.py help' for usage.

博主,这是个什么情况,manage.py替换了还是这样


生而赴死欸 生而赴死欸

有点尴尬,解决了


赵Sir

楼主,请问个问题,这个从主页跳转到详情页面,可以用Js来做并传值吗?具体应该怎么做呢?


追梦人物 [博主] 赵Sir

请参考js相关功能的文档,我对此没有过研究。


宋良超XZ 赵Sir

/blog/post/{{ post.pk }}  我觉得这样写就行了


hyangzz

ERRORS:blog.Post_tags: (fields.E336) The model is used as an intermediate model by 'blog.Post.tags', but it does not have a foreign key to 'Post' or 'Tag'.System check identified 1 issue (0 silenced). 

请问这是哪里出错了


huguishun

    from django.urls import reverse 

ModuleNotFoundError: No module named 'django.urls'

按照博主教程一路走下来,开启服务,提示没有Django.urls这个模块。不知道什么原因


huguishun huguishun

解决了,from django.core.urlresolvers import reverse


Rayutn

博主好,本章节之前的内容都能实现成功,项目环境和教程中完全的一样。但是之后添加detail函数后为什么会出现这样的编码错误

UnicodeDecodeError: 'utf-8' codec can't decode byte 0xd0 in position 222: invalid continuation byte

应该不存在编码的改变或者问题啊

每个文件为了避免编码错误,在文件头都添加了# -*-coding:utf-8 -*-


Rayutn Rayutn

一部分的错误显示:File "C:\Python3\lib\traceback.py", line 520, in _load_lines    frame.line  File "C:\Python3\lib\traceback.py", line 282, in line    self._line = linecache.getline(self.filename, self.lineno).strip()  File "C:\Python3\lib\linecache.py", line 16, in getline    lines = getlines(filename, module_globals)  File "C:\Python3\lib\linecache.py", line 47, in getlines    return updatecache(filename, module_globals)  File "C:\Python3\lib\linecache.py", line 137, in updatecache    lines = fp.readlines()  File "C:\Python3\lib\codecs.py", line 321, in decode    (result, consumed) = self._buffer_decode(data, self.errors, final)UnicodeDecodeError: 'utf-8' codec can't decode byte 0xd0 in position 222: invalid continuation byte


追梦人物 [博主] Rayutn

编码问题可能是你不小心键入了特殊字符,以及如果你用 python,最好在字符串前加前缀u,例如 u'some string'


IHaoMing

博主,请问一下。当前文章的页面如何获取上一篇和下一篇的url呢


追梦人物 [博主] IHaoMing

思路是获取全部文章 id,获取这一篇文章 id,然后定位这一篇文章的位置,从而找到上一篇和下一篇文章的 id,再 get 获取


白羊木鱼水星

django.urls.exceptions.NoReverseMatch: Reverse for 'detail' with keyword arguments '{'pk': 2}' not found. 1 pattern(s) tried: ['$post/(?P<pk>[0-9]+)/$']

这个错是什么原因啊?


追梦人物 [博主] 白羊木鱼水星

通常来说就是你的 url 写错了,仔细对比一下设计到 url 的地方,看看哪个字符漏了


chengsl 白羊木鱼水星

$post/(?P<pk>[0-9]+)/$

这个写错了,正确的

^post/(?P<pk>[0-9]+)/$


"$":正则中是结束位置的标识


苏柠紫

  File "<frozen importlib._bootstrap_external>", line 674, in exec_module  File "<frozen importlib._bootstrap_external>", line 781, in get_code  File "<frozen importlib._bootstrap_external>", line 741, in source_to_code  File "<frozen importlib._bootstrap>", line 205, in _call_with_frames_removed  File "D:\blogproject\blog\models.py", line 48    def __str__(self):      ^SyntaxError: invalid syntax


苏柠紫 苏柠紫

一加上def__str__(self):就报错,其他和老师的都一样,这是为什么啊


追梦人物 [博主] 苏柠紫

def __str__(self)

def 后面要加一个空格,否则报语法错误。


唯J王道
post-{{ post.pk }这里拼接这个class有何用意呢?看到这里还没看出来

追梦人物 [博主] 唯J王道
没有意义,作为一个 hook,便于你以后可能为其写 js,css

唯J王道 追梦人物 [博主]
受教了!

雷司机_49359
在这里分享一下我遇到的问题吧, Django version是 1.11.3, 遇到 NoReverseMatch , u"'blog" is not a registered namespace 的错误, 在urls.py加入app_name='blog' 可以解决

尘缘_5717 雷司机_49359

我的1.11.4,加了app_name还是不行,不清楚为什么


追梦人物 [博主] 尘缘_5717

看看你添加的位置是否正确


尘缘_5717 追梦人物 [博主]

最后发现是setting里的ROOT_URLCONF定义有问题,解决了

非常感谢!


lxjmaster 尘缘_5717

请问你的ROOT_URLCONF是如何定义的?我的Django版本和你的一样,并且遇到和你一样的问题..


木二Lin
我的显示变成这样了,转换成功了,但是网页怎么不显示成对应的


一级标题

二级标题

三级标题

  • 列表项1
  • 列表项2
  • 列表项3

这是一段引用

python def detail(request, pk): post = get_object_or_404(Post, pk=pk) post.body = markdown.markdown(post.body, extensions=[ 'markdown.extensions.extra', 'markdown.extensions.codehilite', 'markdown.extensions.toc', ]) return render(request, 'blog/detail.html', context={'post': post}) ​


追梦人物 [博主] 木二Lin
网页中的引用方式是否正确?{{ post.toc|safe }}

leonfrank
博主你好,我发现我用了这段代码以后我点开首页文章标题的链接,打开的网页仍然是index模板渲染的网页,这是什么原因

追梦人物 [博主] leonfrank
在浏览器里检查一下你的超链接是否指向了文章详情页呢?

leonfrank 追梦人物 [博主]
是的,浏览器里的地址是http://127.0.0.1:8000/post/2/,我的get_absolute_url定义是

def get_absolute_url(self):
return reverse('myblog:detail',args=[self.pk])

views.py里定义的detail函数是
def detail(request, pk):
post = get_object_or_404(Post, pk=pk)
post.body = markdown.markdown(post.body,extensions=['markdown.extensions.extra',
'markdown.extensions.codehilite',
'markdown.extensions.toc',])
return render(request, 'myblog/detail.html', context={'post': post})

detail.html放在templates/myblog下面 这里面有什么错误吗

leonfrank leonfrank
已经解决了

leonfrank 追梦人物 [博主]
博主,我发现我的博客详情页body部分前后各有一个

,像这样:

这是一个测试



我的detail.html body部分代码和上面是完全一样的,这个是什么问题

追梦人物 [博主] leonfrank
使用 safe 模板标签,教程中有提到,例如:{{post.body|safe}}

观测者 leonfrank
请问你是如何解决的?

poet 观测者

urls.py 利用正则判断 页面路由

使 index 的视图 优先匹配了 post/ 路径下的页面

所以 需要将 detail 视图url 放在 index 上面

url(r'^post/(?P<pk>[0-9]+)/$', views.detail, name='detail'),
url(r'$', views.index, name='index'),

2018年4月1日01点05分

windows 10 + python 3.6 + Django version 2.0.3


ipandasbomb poet

谢谢,不然老是无法跳转


poet ipandasbomb

后面 评论 模块 也有这个坑


ipandasbomb poet

请问能详细解释下为什么detail路由要在index路由前面吗?以前看别的案例好像都不需要强调顺序的。谢谢了


poet ipandasbomb

因为正则 本身的原因,至于为啥,我就不知道了。不过还是挺佩服自己能想到这的


ipandasbomb poet

我也佩服,,,,,昨天一直调试,完全没有错误出现,,,,,都不知道怎么修改。详情页面就是无法跳转。哈哈哈


Jasmine7Lx leonfrank

我也遇到了这个问题,请问你是怎么解决的?


世事可否有转机
你好,我遇到的是name 'get_object_or_404' is not defined……

传唱信仰 世事可否有转机
【编写 detail 视图函数】下有一行导入模块里加上get_object_or_404,遇到这种问题,可以ctrl+f当前网页搜索一下。

lllong33
detail.html中并没有{% for post in post_list %} ,且base.html中也没有定义。他是如何获取的post呢?

盖子是个熊孩子 lllong33
detail.html只需要一个post的内容,所以是通过views.py代码中的传递PK,也就是数据库默认id进入到detail.html中,然后通过
这一行选择某个PK对应的文章的。
index主页需要显示全部的post->title,所以要从列表中轮询{% for post in post_list %}。
个人理解,如果有错误请多指教。

B.C.Wang lllong33
views.detail获取网址中的pk,然后传递到函数中获得post中的对象渲染给detail.html,index.html则是渲染了post_list过去

恠往逺方
请问楼主,我的在index.html中增加了{{ post.get_absolute_url }}但是却没有生成url的链接地址呀,但是用路径 http://127.0.0.1:8000/post/1/ 却是可以访问的这是什么原因啊?

追梦人物 [博主] 恠往逺方
怀疑你的 get_absolute_url 方法写错了?

Li Zhenhan 恠往逺方

<a href="{{post.get_absolute_url}}"> 是不是忘加大括号了


JBKylin
File "/home/jay/projects/blogproject/blog/models.py", line 5, in
from django.urls import reverse
ImportError: No module named 'django.urls'

JBKylin JBKylin
导入django.urls出错,请问是什么原因以及怎样解决

JBKylin JBKylin
解决了 from django.core.urlresolvers import reverse

追梦人物 [博主] JBKylin
你可能 django 版本与教程不一致。

美图公子
请问博主。正则表达式我还是有些基础的,但这个'^post/(?P[0-9]+)/$'
怎样跟post/255匹配上的,我还真难以理解了。
post不用说了。?P[0-9]+ ,其中?表示0或一,P这个怎样能匹配成数字呢?P是不是表示“参数pk”的意思,不是标准的表达式,是告诉django此处表示参数?谢谢

追梦人物 [博主] 美图公子
?P 是 python 正则表达式的一个占位格式,表示其后匹配的内容将被存入键为 pk 的字典中。

wenmingxing1
博主你好,出现了这个问题:
UnicodeDecodeError at /
'utf-8' codec can't decode byte 0xca in position 1814: invalid continuation byte
Request Method: GET
Request URL: http://127.0.0.1:8000/
Django Version: 1.10.6
Exception Type: UnicodeDecodeError
Exception Value:
'utf-8' codec can't decode byte 0xca in position 1814: invalid continuation byte
Exception Location: F:\ligang\Envs\blogproject_env\lib\codecs.py in decode, line 319

rror during template rendering

In template F:\ligang\Workspace\blogproject\templates\blog\index.html, error at line 1
utf-8
1 {% extends 'base.html' %}

追梦人物 [博主] wenmingxing1
这种情况可能是你的模板文件中含有奇怪的编码字符或者文件本身不是 utf-8 编码,另外如果使用的 py2,有很大概率会遇到这个问题。推荐使用 py3.

wenmingxing1 追梦人物 [博主]
我用的是py3,您说的模板文件具体是指的哪个呢?

追梦人物 [博主] wenmingxing1
index.html,base.html 都有可能。

发情的兔
博主,首页href="index.html",点击会直接在原有的地址加上index.html,改成href="/blog/index.html"这样就能返回首页了。django本身就这样,还是版本的问题?我用的django-1.11

追梦人物 [博主] 发情的兔
需要把链接改成首页路由,后续教程会有相关介绍的。

erwwwwwwww
reverse导入的路径不一样,,难道是django版本不一样的原因?
from django.core.urlresolvers import reverse

追梦人物 [博主] erwwwwwwww
最新版 django 推荐从 urls 中导入 reverse,rom django.core.urlresolvers import reverse 是老式导入,django 可能在未来的某个版本移除这个导入路径。

陌上花开迟迟
写的一篇长博客中为什么首行不缩进,段落也不换行

逐殇小彬
TemplateDoesNotExist at /
base.html
这个是少了什么?

追梦人物 [博主] 逐殇小彬
这可能是你的模板路径不对,或者没有在 settings.py 中设置模板根目录

sakura1357
楼主,{% extends 'base.html' %}路径报错,应该是{% extends 'blog/base.html' %}才行,我的环境是Windows 10+Python3.6.1 (64位) + Django 1.10.6

追梦人物 [博主] sakura1357
请对照示例项目查看模板路径和你的有什么不同。

Koto2693601524
樓主,想問代碼:return reverse('blog:detail', kwargs={'pk': self.pk}) 中的self.pk是從什麼時候傳入的?

追梦人物 [博主] Koto2693601524
self.pk 引用实例自身的 pk 属性,这个 pk 属性是来自 Post 的父类 models.Model

westboycome
楼主问题解决了,在logproject\urls里加namespace就可以了,
url(r'', include('blog.urls', namespace='blog'))

小文心剑飞 westboycome


westboycome
{{ post.get_absolute_url }}楼主这个方法好像不能用,报错信息时这样:
NoReverseMatch at /'blog' is not a registered namespace
然后指向这个位置
{{ post.title }}

黑色的忧虑2011
没报错,点文章标题和继续阅读就是没反应

黑色的忧虑2011 黑色的忧虑2011
用的是mysql数据库

追梦人物 [博主] 黑色的忧虑2011
可否把相关的代码发到 pythonzh.cn ?使用 markdown 排版。博客的评论不支持粘贴代码。

黑色的忧虑2011 追梦人物 [博主]
感谢博主,发现是django1.8版本问题,需要在namespace设置才行,用app_name连接不过去,感谢楼主的回复!

DD_mmy 黑色的忧虑2011
命名空间怎么定义的namespace='block',错误的?

追梦人物 [博主] DD_mmy
在 include 函数里加入 namespace 参数即可。

DD_mmy 追梦人物 [博主]
博主,麻烦把章的代码更新一下,把app_name = blog,换成namespace='blog'

追梦人物 [博主] DD_mmy
文章中代码没错呀?

DD_mmy 追梦人物 [博主]
点击文章标题怎么不跳转

追梦人物 [博主] DD_mmy
可能没有设置超链接

DD_mmy 追梦人物 [博主]

DD_mmy 追梦人物 [博主]
urlpatterns = [
url(r'^blog', include('blog.urls', namespace='blog')),
url(r'^post/(?P[0-9]+)/$', views.detail, name='detail'),
]

追梦人物 [博主] DD_mmy
url(r'^post/(?P[0-9]+)/$', views.detail, name='detail'), 这一条应该放在 blog.urls.py 里。你用浏览器看一下标题的 a 标签的 href 是不是文章的 url?

赖伟华咯
NoReverseMatch at /
Reverse for 'detail' with arguments '()' and keyword arguments '{u'pk': }' not found. 1 pattern(s) tried: ['post/(?P[0-9]+)/$']



WHY????

追梦人物 [博主] 赖伟华咯
Please paste the related codes and more imformation. We can not find out it only with a exception message!

赖伟华咯 追梦人物 [博主]
图片复制不了...

追梦人物 [博主] 赖伟华咯
直接粘贴一下代码吧,或者加群后小窗口我。

赖伟华咯 追梦人物 [博主]
In template D:\Django\workspace\blogproject\templates\blog\index.html, error at line 7






1 {% extends 'base.html' %}
2 {% block main %}
3 {% for post in post_list %}
4

追梦人物 [博主] 赖伟华咯
没什么问题,github 上有所有相应分支的代码,你可以把你的代码和项目代码仔细对比一下,看是哪里的问题。重点对对 view 和 url 的代码。

CaptainNebula 赖伟华咯
我的问题跟你一样,请问你解决了么?

gun_hap
请教一下,为何一路跟着做到此处,我的出来的效果和你的不一样呢?
我在数据库中添加了2篇文章,但是在主页中看不到。
另外,“继续阅读”按钮也找不到。
但是其它没有什么问题。

追梦人物 [博主] gun_hap
是不是删了某些模板代码?仔细检查一下模板继承有没有问题。github 有每一步对应的代码,仔细对比一下哪里不同。

Xiang99
'utf-8' codec can't decode byte 0xcd in position 2605: invalid continuation byte这个怎么解决

追梦人物 [博主] Xiang99
很多人遇到这个问题,目前没有发现导致这个问题的原因,可能是你的文件编码问题或者含有特殊的字符导致的。也可能和操作系统有关,并非所有人遇到这个问题。

zengzhengrong Xiang99
文件配置编码改成utf-8
右键文件最后一个选项 properties把GBK编码换掉

zengzhengrong Xiang99
换了utf-8之后要把的中文字重新写一遍

letsens
不能使用Post.tag.name

追梦人物 [博主] letsens
因为 Post 和 Tag 是多对多的关系,所以要获取 Post 下的全部 tag 需使用 post .tags.all(),post.tags 将返回一个类似于 objects 的模型管理器。

letsens 追梦人物 [博主]
嗯嗯,返回对象在循环一次就可以了,谢谢。

letsens
怎么显示每个文章的标签呢?