简体   繁体   中英

Django - 'NoneType' object has no attribute 'threads'

Hello guys so I am trying to add a category system for posts by following a tutorial on this website https://djangopy.org/how-to/how-to-implement-categories-in-django/ (I changed my code up a little)

I get this error 'NoneType' object has no attribute 'threads' every time I try and go to the category page to view the posts inside that category.

models.py:

from django.db import models
from django.urls import reverse
from django.utils.text import slugify

class Category(models.Model):
    name = models.CharField(max_length=100)
    short_desc = models.CharField(max_length=160)
    slug = models.SlugField()
    parent = models.ForeignKey('self', blank=True, null=True, related_name='children', on_delete=models.CASCADE)

    class Meta:
        unique_together = ('slug', 'parent',)
        verbose_name_plural = "Categories"

    def __str__(self):
        full_path = [self.name]
        k = self.parent

        while k is not None:
            full_path.append(k.name)
            k = k.parent

        return ' -> '.join(full_path[::-1])

    def save(self, *args, **kwargs):
        value = self.name
        self.slug = slugify(value, allow_unicode=True)
        super().save(*args, **kwargs)


class Thread(models.Model):
    ...
    category = models.ForeignKey('Category', null=True, blank=True, on_delete=models.CASCADE, related_name='threads')
    ...

    def get_cat_list(self):
        k = self.category
        breadcrumb = ["dummy"]
        while k is not None:
            breadcrumb.append(k.slug)
            k = k.parent

        for i in range(len(breadcrumb)-1):
            breadcrumb[i] = '/'.join(breadcrumb[-1:i-1:-1])
        return breadcrumb[-1:0:-1]

    ...

views.py:

from django.shortcuts import render, HttpResponseRedirect
from django.contrib import messages
from .models import Category, Thread
from .forms import SubmitScamNumber

def show_category_view(request, hierarchy=None):
    category_slug = hierarchy.split('/')
    category_queryset = list(Category.objects.all())
    all_slugs = [ x.slug for x in category_queryset ]
    parent = None
    for slug in category_slug:
        if slug in all_slugs:
            #parent = get_object_or_404(Category, slug=slug, parent=parent)
            parent = Category.objects.filter(slug__in=category_slug, parent=None).first()

    context = {
        'category': parent,
        'threads': parent.threads.all(),
        'sub_categories': parent.children.all(),
    }
    return render(request, "categories.html", context)

 ...

categories.html:

{% extends 'base.html' %}
{% block content %}

{% if sub_categories %}
    <h3>Sub Categories</h3>
    {% for i in sub_categories %}
        <a href="{{ i.slug }}"> {{ i.name }} </a>
    {% endfor %}
{% endif %}

{% if threads %}
{% for i in threads %}
    <div class="columns">
        <div class=" card-article-hover card">
          <a href="{{ i.slug }}">
            <img  src="{{ i.cover_photo.url }}">
          </a>
          <div class="card-section">
            <a href="{{ i.slug }}">
              <h6 class="article-title">{{ i.title | truncatechars:30}}</h6>
            </a>
          </div>
          <div class="card-divider flex-container align-middle">
            <a href="" class="author">{{ i.user.get_full_name }}</a>
          </div>
          <div class="hover-border">
          </div>
        </div>
    </div>
{% endfor %}
{% endif %}

{% endblock content %}

In show_category_view you set parent = None . Then, if there are any slugs derived from the hierarchy , you loop over them and re-assign parent to be an instance of Category . However, if there are no slugs in category_slug or the slug is not found in all_slugs or the line Category.objects.filter(slug__in=category_slug, parent=None).first() doesn't return an instance of Category , then the value of parent will remain as None and then when you try to access the threads attribute on an object which is None , as you do here...

parent.threads.all()  # parent = None

...the exception you are seeing will occur.

So, exactly as the error tells you, 'NoneType' object has no attribute 'threads' , because parent is still None as originally defined.

There's another null checking error lurking in this function.

def show_category_view(request, hierarchy=None):
    category_slug = hierarchy.split('/')

You set the default value of the hierarchy argument to be None , then you call split on it, even though that value might be None . If you call split on None it will throw a similar AttributeError .

You also don't need to cast list over category_queryset = list(Category.objects.all()) . Querysets are already list like objects that can be looped over, implement the iterator protocol, etc. (in fact, by doing this you throw away all the benefits that the queryset interface provides for you.) Here you can just do:

all_slugs = [x.slug for x in Category.objects.all()]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM