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


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.s*e()
    # ...

这里的关键在于 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('<str:slug>', views.blogPost, name='blogPost'), # 文章详情页
    path('category/<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/<str:category_name>/', views.blogCategory, name='blogCategory'),
    path('<str:slug>/', views.blogPost, name='blogPost'), # 建议在slug后也加斜杠保持一致性
]

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

NoCode NoCode

美团推出的零代码应用生成平台

NoCode 180 查看详情 NoCode

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.s*e()

    # 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.s*e()

    # ... 后续评论逻辑不变 ...
    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 %}
<div class="container my-4">
    <h2>分类文章: {{ category.name }}</h2>

    {% if allPosts %} {# 检查是否有文章 #}
        {% for post in allPosts %} {# 遍历从视图传递过来的文章列表 #}
            <div class="card mb-3">
                <div class="card-body">
                    <div class="row g-0">
                        <div class="col-md-4">
                            <div class="ratio ratio-16x9">
                            <!-- featured image --> 
                                {% if post.thumbnail %} {# 检查是否有缩略图 #}
                                    @@##@@
                                {% else %}
                                    @@##@@
                                {% endif %}
                            </div>
                        </div>
                        <div class="col-md-7 ps-md-3 pt-3 pt-md-0 d-flex flex-column">
                                <h2 class="card-title h3">
                                    {# 使用 {% url %} 标签生成文章详情页链接 #}
                                    <a href="{% url 'blogPost' slug=post.slug %}">{{ post.title }}</a>
                                </h2>
                                <div class="text-muted">
                                    <small>
                                        发布于 {{ post.timeStamp|date:"Y年m月d日 H:i" }} 作者: <strong>{{ post.author }}</strong>
                                    </small>
                                </div>
                                <p class="card-text mb-auto py-2">{{ post.content|safe|striptags|truncatechars:300 }}</p>
                                <div>
                                    {# 使用 {% url %} 标签生成文章详情页链接 #}
                                    <a href="{% url 'blogPost' slug=post.slug %}" class="btn btn-primary">阅读更多</a> | 分类:
                                    {% for cat in post.categories.all %} {# 遍历当前文章的分类 #}
                                        {# 使用 {% url %} 标签生成分类页链接 #}
                                        <a href="{% url 'blogCategory' category_name=cat.name %}">
                                            {{ cat.name }}
                                        </a>{% if not forloop.last %}, {% endif %}
                                    {% endfor %}
                                </div>
                        </div>
                    </div>
                </div>
            </div>
        {% endfor %}

        {# 分页导航 #}
        {% if allPosts.has_other_pages %}
        <n* aria-label="Page n*igation example">
            <ul class="pagination justify-content-center">
                {% if allPosts.has_previous %}
                    <li class="page-item"><a class="page-link" href="?page={{ allPosts.previous_page_number }}">上一页</a></li>
                {% else %}
                    <li class="page-item disabled"><span class="page-link">上一页</span></li>
                {% endif %}

                {% for i in allPosts.paginator.page_range %}
                    {% if allPosts.number == i %}
                        <li class="page-item active"><span class="page-link">{{ i }}</span></li>
                    {% else %}
                        <li class="page-item"><a class="page-link" href="?page={{ i }}">{{ i }}</a></li>
                    {% endif %}
                {% endfor %}

                {% if allPosts.has_next %}
                    <li class="page-item"><a class="page-link" href="?page={{ allPosts.next_page_number }}">下一页</a></li>
                {% else %}
                    <li class="page-item disabled"><span class="page-link">下一页</span></li>
                {% endif %}
            </ul>
        </n*>
        {% endif %}

    {% else %}
        <p>该分类下暂无文章。</p>
        <p>您的搜索未匹配到任何文章。</p>
        <br>
        建议: <br>
        <ul>
            <li>确保所有单词拼写正确。</li>
            <li>尝试使用不同的关键词。</li>
            <li>尝试使用更通用的关键词。</li>
            <li>尝试使用更少的关键词。</li>
        </ul>
    {% endif %}

</div>
{% endblock body %}

4.2 blogPost.html 模板中的 URL 修正

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

<!-- 示例:在 blogPost.html 中 -->
<h2 class="card-title h3">
    {# 正确使用 {% url %} 标签生成文章详情页链接 #}
    <a href="{% url 'blogPost' slug=post.slug %}">{{ post.title }}</a>
</h2>

<!-- ... -->

<div>
    {# 正确使用 {% url %} 标签生成文章详情页链接 #}
    <a href="{% url 'blogPost
{{post.title}}No Image

以上就是Django NoneType 错误深度解析与分类文章展示教程的详细内容,更多请关注其它相关文章!


# go  # seo  # 编码  # app  # html  # 放在  # dba营销推广计划  # 下一页  # 丰镇门户网站建设  # 网站建设合伙合同  # 北京推广营销策划总结  # 北京seo公司威星hfqjwl  # 家居营销推广价格怎么算  # 手机百度 关键词排名  # 网站推广责任几何引争议  # seo和程序员  # 渌口区网站建设  # 是在  # 它会  # 的是  # 遍历  # 分页  # 详情页  # 不存在  # 关键词  # red  # django  # 路由  # ai  # 栈 


相关栏目: 【 Google疑问12 】 【 Facebook疑问10 】 【 优化推广96088 】 【 技术知识133117 】 【 IDC资讯59369 】 【 网络运营7196 】 【 IT资讯61894 】


相关推荐: 优化2xN网格最大路径和的动态规划算法实践  pubmed数据库官方主页_pubmed学术论文查找官网直达  动漫之家观看全集库 动漫之家免费资源网地址  不吃碳水化合物是健康减肥的好办法吗  Golang如何测试结构体方法_Golang reflect方法测试与调用技巧  word文档行距怎么调?word文档调行距的操作步骤  抖音小程序怎么开通?小程序开通条件是什么?  解决CSS background 属性中 cover 关键字的常见误用  铁路12306官网入口 铁路12306中国铁路官网登录首页  iPhone14无法连接蓝牙设备如何解决  泰拉瑞亚水晶无法放置问题  在Flask应用中安全高效地更新SQLAlchemy用户数据  优化 React onClick 事件处理:函数引用与箭头函数的对比  中通快递官网指定查询 中通快递单号查询平台入口  WooCommerce 新客户订单自动添加管理员备注教程  自定义你的VS Code状态栏,监控关键信息  德邦快递收费标准详解  163邮箱在线登录 163邮箱网页版在线入口  告别阻塞等待:如何使用GuzzlePromises优雅处理PHP异步操作,提升应用响应速度  抖音号升级成企业资质怎么弄?有什么好处?  苹果手机聊天记录删除了如何恢复  CSS动画如何实现图标旋转并放大_transform rotate scale @keyframes实现  《全民k歌》音乐怎么下载到本地2025  向往的生活小游戏启动处_向往的生活小游戏立即启动  FullCalendar自定义按钮样式定制指南  顺丰快递收费标准查询_如何查看顺丰最新收费价格  解决CSS容器溢出问题:使用calc()实现精确布局与边距控制  《三国:谋定天下》平民全阶段通用阵容  鸣潮历史学家灯塔位置一览  《随手记》备份数据方法  QQ邮箱PC端登录页面_QQ邮箱网页版登录界面  Symfony路由参数转换器:实体存在性验证与错误处理策略  Python csv 模块处理非字符串数据:列表写入 CSV 文件的机制解析  cad怎么隐藏指定的图层_cad隐藏或冻结图层方法  《大周列国志》皇帝律令功能介绍  荣耀盒子应用管理技巧  微信朋友圈怎么设置三天可见 微信朋友圈设置指定天数可见步骤【教程】  泰拉瑞亚网页版在线登录入口 泰拉瑞亚官方正版入口  Python测试中模块导入路径解析的最佳实践  使用Selenium在无头Chrome中交互动态菜单和复选框的策略  Selenium自动化:利用键盘模拟解决复杂日期输入框输入问题  以下哪一项是古代兵书三十六计中的计谋  多闪电脑版下载_多闪PC端模拟器使用  猫眼电影app如何参与官方的抽奖活动_猫眼电影官方抽奖参与方法  抄漫画官网防走失地址_抄漫画最新漫画完整版阅读入口  折叠屏手机充不进电是什么问题? 特殊结构带来的维修难点  mysql如何限制远程访问_mysql远程访问限制方法  抖音网页版官方链接 抖音网页版官网链接入口  qq音乐官方网站入口_qq音乐在线听歌网页版链接  Highcharts雷达图轴线交点数值标注指南 

 2025-11-24

了解您产品搜索量及市场趋势,制定营销计划

同行竞争及网站分析保障您的广告效果

点击免费数据支持

提交您的需求,1小时内享受我们的专业解答。

运城市盐湖区信雨科技有限公司


运城市盐湖区信雨科技有限公司

运城市盐湖区信雨科技有限公司是一家深耕海外推广领域十年的专业服务商,作为谷歌推广与Facebook广告全球合作伙伴,聚焦外贸企业出海痛点,以数字化营销为核心,提供一站式海外营销解决方案。公司凭借十年行业沉淀与平台官方资源加持,打破传统外贸获客壁垒,助力企业高效开拓全球市场,成为中小企业出海的可靠合作伙伴。

 8156699

 13765294890

 8156699@qq.com

Notice

We and selected third parties use cookies or similar technologies for technical purposes and, with your consent, for other purposes as specified in the cookie policy.
You can consent to the use of such technologies by closing this notice, by interacting with any link or button outside of this notice or by continuing to browse otherwise.