0

0

Django NoneType 错误深度解析与分类文章展示教程

DDD

DDD

发布时间:2025-11-24 11:37:00

|

943人浏览过

|

来源于php中文网

原创

Django NoneType 错误深度解析与分类文章展示教程

本教程深入探讨 django 中 `attributeerror: 'nonetype' object has no attribute 'views'` 错误的成因,特别是在 url 路由配置不当和数据查询为空时。文章将提供详细的视图逻辑修正、url 模式优化以及模板渲染的最佳实践,确保分类文章能正确显示,并避免因对象不存在而引发的运行时错误。

在 Django 开发中,AttributeError: 'NoneType' object has no attribute '...' 是一个非常常见的错误,它通常意味着你正在尝试访问一个 None 对象的属性。当数据库查询没有返回任何结果,或者在其他逻辑中变量被赋值为 None 时,就可能发生这种情况。本教程将以一个实际案例为例,详细讲解如何诊断、理解并解决这类问题,特别是在处理 URL 路由和分类文章展示功能时。

1. 理解 NoneType 错误根源

在提供的代码中,错误信息 AttributeError at /blog/cat1 'NoneType' object has no attribute 'views' 明确指出,在尝试访问一个 None 对象的 views 属性时发生了错误。结合堆信息,这通常发生在 blogPost 视图中:

# views.py
def blogPost(request, slug):
    post = Post.objects.filter(slug=slug).first() # 关键行
    post.views = post.views+1 # 错误发生在这里
    post.save()
    # ...

这里的关键在于 Post.objects.filter(slug=slug).first()。当数据库中不存在与给定 slug 匹配的 Post 对象时,first() 方法会返回 None。紧接着,下一行代码 post.views = post.views + 1 就会尝试访问 None 对象的 views 属性,从而引发 AttributeError: 'NoneType' object has no attribute 'views'。

2. 诊断与修正 URL 路由问题

导致上述错误的一个重要原因是 URL 路由配置不当,使得用户意图访问分类文章的请求被错误地匹配到了文章详情页视图。

观察 urls.py 文件:

# urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('postComment', views.postComment, name='postComment'),
    path('', views.blogHome, name='blogHome'),
    path('', views.blogPost, name='blogPost'), # 文章详情页
    path('category//', views.blogCategory, name='blogCategory'), # 分类页
]

当用户访问 /blog/cat1 时,Django 的 URL 解析器会从上到下匹配 urlpatterns。 path('', views.blogPost, name='blogPost') 是一个非常通用的模式,它会匹配任何形如 /blog/something 的路径,并将 something 作为 slug 参数传递给 blogPost 视图。 因此,当请求 /blog/cat1 到来时,它会优先匹配到 blogPost 视图,并将 cat1 作为 slug。然而,cat1 是一个分类名称,很可能在 Post 模型的 slug 字段中不存在对应的文章,导致 Post.objects.filter(slug='cat1').first() 返回 None,进而引发错误。

修正策略:

  1. URL 模式顺序调整: 将更具体的 URL 模式放在更通用的模式之前。分类 URL path('category//', ...) 比文章详情页 URL path('', ...) 更具体,因为它包含了一个额外的路径段 category/。

  2. 参数类型和名称统一: 确保 URL 模式中捕获的参数类型和视图函数接收的参数类型一致。blogCategory 视图期望接收一个 pk (主键),但 URL 模式 path('category//', ...) 捕获的是一个字符串 category。为了清晰和一致,我们应将其改为更具描述性的 category_name 或 category_slug。

修改后的 urls.py 示例:

# urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('postComment', views.postComment, name='postComment'),
    path('', views.blogHome, name='blogHome'),
    # 将分类URL放在文章详情URL之前,且使用更明确的参数名
    path('category//', views.blogCategory, name='blogCategory'),
    path('/', views.blogPost, name='blogPost'), # 建议在slug后也加斜杠保持一致性
]

注意: 在 path('/', ...) 中添加斜杠 / 是一种常见的最佳实践,有助于保持 URL 结构的一致性,避免重复内容,并简化 SEO。

酷表ChatExcel
酷表ChatExcel

北大团队开发的通过聊天来操作Excel表格的AI工具

下载

3. 完善视图逻辑与数据获取

在修正了 URL 路由之后,我们还需要优化视图函数,以确保它们能够健壮地处理数据查询结果,并正确地传递数据给模板。

3.1 修正 blogPost 视图

为了避免 NoneType 错误,我们需要在访问 post 对象的属性之前,检查 post 是否为 None。

# views.py
from django.shortcuts import render, redirect, get_object_or_404
from blog.models import Post, BlogComment, Category
from django.contrib import messages
from blog.templatetags import extras
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
import socket

def blogPost(request, slug):
    post = Post.objects.filter(slug=slug).first()

    if post is None:
        # 如果文章不存在,可以返回404错误,或者重定向到博客首页,或者显示一个消息
        # 这里我们使用 get_object_or_404 来简化处理
        # post = get_object_or_404(Post, slug=slug) # 这种方式更简洁,如果未找到会自动抛出404
        messages.error(request, '请求的文章不存在。')
        return redirect('blogHome') # 重定向到博客首页

    post.views = post.views + 1
    post.save()

    # comments
    comments = BlogComment.objects.filter(post=post, parent=None)
    replies = BlogComment.objects.filter(post=post).exclude(parent=None)
    replyDict = {}
    for reply in replies:
        if reply.parent.sno not in replyDict.keys():
            replyDict[reply.parent.sno] = [reply]
        else:
            replyDict[reply.parent.sno].append(reply)

    context = {'post': post, 'comments': comments, 'user': request.user, 'replyDict': replyDict}
    return render(request, 'blog/blogPost.html', context)

推荐使用 get_object_or_404: 实际上,对于查询单个对象且期望它必须存在的情况,get_object_or_404 函数是更简洁和符合 Django 惯例的选择。它会在对象不存在时自动抛出 Http404 异常。

# views.py (使用 get_object_or_404 优化)
def blogPost(request, slug):
    post = get_object_or_404(Post, slug=slug) # 如果找不到文章,会自动返回404页面

    post.views = post.views + 1
    post.save()

    # ... 后续评论逻辑不变 ...
    comments = BlogComment.objects.filter(post=post, parent=None)
    replies = BlogComment.objects.filter(post=post).exclude(parent=None)
    replyDict = {}
    for reply in replies:
        if reply.parent.sno not in replyDict.keys():
            replyDict[reply.parent.sno] = [reply]
        else:
            replyDict[reply.parent.sno].append(reply)

    context = {'post': post, 'comments': comments, 'user': request.user, 'replyDict': replyDict}
    return render(request, 'blog/blogPost.html', context)

3.2 修正 blogCategory 视图

原始的 blogCategory 视图期望接收一个 pk (主键),但我们修改后的 URL 模式传递的是 category_name。此外,视图需要获取该分类下的所有文章,而不是仅仅传递分类对象。

# views.py
def blogCategory(request, category_name): # 接收 category_name 参数
    # 根据名称获取分类对象,如果不存在则返回404
    category = get_object_or_404(Category, name=category_name)

    # 获取该分类下的所有文章
    # 由于 Category 模型中 Post 有 ManyToManyField('Category', related_name='posts')
    # 可以通过 category.posts.all() 来获取所有关联文章
    allPosts = category.posts.all().order_by("-timeStamp") # 按照时间倒序

    # 可以选择对分类文章进行分页
    paginator = Paginator(allPosts, 5) # 每页显示5篇文章
    page = request.GET.get('page')
    try:
        posts_paginated = paginator.page(page)
    except PageNotAnInteger:
        posts_paginated = paginator.page(1)
    except EmptyPage:
        posts_paginated = paginator.page(paginator.num_pages)

    context = {
        'category': category,
        'allPosts': posts_paginated, # 将分页后的文章列表传递给模板
        'page': 'pages' # 保持原有的分页上下文
    }
    return render(request, "blog/blogCategory.html", context)

4. 优化模板渲染与 URL 生成

blogCategory.html 模板也需要进行大幅度修改,以正确显示分类下的文章,并使用 Django 的 {% url %} 标签来生成正确的链接。

4.1 blogCategory.html 模板优化

原模板中 {% for category in post.categories.all %} 和 {% if posts == posts %} 存在逻辑错误。我们应该遍历从视图中传递过来的 allPosts (或 posts_paginated)。

{% extends "base.html" %}

{% block title %} 分类: {{ category.name }} {% endblock title %}
{% block blogactive %} active {% endblock blogactive %}
{% block body %}

分类文章: {{ category.name }}

{% if allPosts %} {# 检查是否有文章 #} {% for post in allPosts %} {# 遍历从视图传递过来的文章列表 #}
{% if post.thumbnail %} {# 检查是否有缩略图 #} @@##@@ {% else %} @@##@@ {% endif %}

{# 使用 {% url %} 标签生成文章详情页链接 #} {{ post.title }}

发布于 {{ post.timeStamp|date:"Y年m月d日 H:i" }} 作者: {{ post.author }}

{{ post.content|safe|striptags|truncatechars:300 }}

{# 使用 {% url %} 标签生成文章详情页链接 #} 阅读更多 | 分类: {% for cat in post.categories.all %} {# 遍历当前文章的分类 #} {# 使用 {% url %} 标签生成分类页链接 #} {{ cat.name }} {% if not forloop.last %}, {% endif %} {% endfor %}
{% endfor %} {# 分页导航 #} {% if allPosts.has_other_pages %} {% endif %} {% else %}

该分类下暂无文章。

您的搜索未匹配到任何文章。


建议:
  • 确保所有单词拼写正确。
  • 尝试使用不同的关键词。
  • 尝试使用更通用的关键词。
  • 尝试使用更少的关键词。
{% endif %}
{% endblock body %}

4.2 blogPost.html 模板中的 URL 修正

虽然 blogPost.html 没有直接导致本教程的 NoneType 错误,但为了保持一致性和最佳实践,也应将其中硬编码的 URL 替换为 {% url %} 标签。


{# 正确使用 {% url %} 标签生成文章详情页链接 #} {{ post.title }}

{# 正确使用 {% url %} 标签生成文章详情页链接 #} No Image

相关专题

更多
html版权符号
html版权符号

html版权符号是“©”,可以在html源文件中直接输入或者从word中复制粘贴过来,php中文网还为大家带来html的相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

611

2023.06.14

html在线编辑器
html在线编辑器

html在线编辑器是用于在线编辑的工具,编辑的内容是基于HTML的文档。它经常被应用于留言板留言、论坛发贴、Blog编写日志或等需要用户输入普通HTML的地方,是Web应用的常用模块之一。php中文网为大家带来了html在线编辑器的相关教程、以及相关文章等内容,供大家免费下载使用。

648

2023.06.21

html网页制作
html网页制作

html网页制作是指使用超文本标记语言来设计和创建网页的过程,html是一种标记语言,它使用标记来描述文档结构和语义,并定义了网页中的各种元素和内容的呈现方式。本专题为大家提供html网页制作的相关的文章、下载、课程内容,供大家免费下载体验。

467

2023.07.31

html空格
html空格

html空格是一种用于在网页中添加间隔和对齐文本的特殊字符,被用于在网页中插入额外的空间,以改变元素之间的排列和对齐方式。本专题为大家提供html空格的相关的文章、下载、课程内容,供大家免费下载体验。

245

2023.08.01

html是什么
html是什么

HTML是一种标准标记语言,用于创建和呈现网页的结构和内容,是互联网发展的基石,为网页开发提供了丰富的功能和灵活性。本专题为大家提供html相关的各种文章、以及下载和课程。

2891

2023.08.11

html字体大小怎么设置
html字体大小怎么设置

在网页设计中,字体大小的选择是至关重要的。合理的字体大小不仅可以提升网页的可读性,还能够影响用户对网页整体布局的感知。php中文网将介绍一些常用的方法和技巧,帮助您在HTML中设置合适的字体大小。

505

2023.08.11

html转txt
html转txt

html转txt的方法有使用文本编辑器、使用在线转换工具和使用Python编程。本专题为大家提供html转txt相关的文章、下载、课程内容,供大家免费下载体验。

311

2023.08.31

html文本框代码怎么写
html文本框代码怎么写

html文本框代码:1、单行文本框【<input type="text" style="height:..;width:..;" />】;2、多行文本框【textarea style=";height:;"></textare】。

423

2023.09.01

高德地图升级方法汇总
高德地图升级方法汇总

本专题整合了高德地图升级相关教程,阅读专题下面的文章了解更多详细内容。

9

2026.01.16

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Bootstrap 5教程
Bootstrap 5教程

共46课时 | 2.9万人学习

AngularJS教程
AngularJS教程

共24课时 | 2.6万人学习

CSS教程
CSS教程

共754课时 | 19.3万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号