1

I've tried to create a simple app that has a list of Categories and each Category has a list of Posts.

When I delete a Category object in Django Admin it disappears in Admin but it remains rendered in the template. Here is Admin showing only two Categories

Admin shows only two Categories

However, on the home.html it shows 3.. home.html shows 3

the base.html serving the Navbar also shows 3

navbar shows 3

I have cleared cache and hard reload. Here is my code:

Views.py

from django.shortcuts import render, get_object_or_404
from .models import Post, Category
from django.views.generic import ListView, DetailView

category_list = Category.objects.all()

def privacy(request):
    return render (request, 'privacy.html', {'category_list':category_list})

def home(request):    
    return render(request,'home.html',{'category_list':category_list})

def categoryDetail(request, slug):
    category = get_object_or_404(Category, slug=slug)
    post = Post.objects.filter(category=category)
    return render(request,'category_detail.html',{'category':category, 'post':post, 'category_list':category_list})

and template, home.html

{% if category_list %}

    <!-- Content Row -->
    <div class="row">
        {% for cat in category_list %}
      <div class="col-md-4 mb-5">
        <div class="card h-100">
          <div class="card-body">
            <h2 class="card-title"><a href="{% url 'category_detail' cat.slug %}">{{ cat.name }}</a></h2>
            <p class="card-text">{{ cat.summary|truncatechars:100 }}</p>
          </div>
          <div class="card-footer">
            <a href="{% url 'category_detail' cat.slug %}" class="btn btn-primary btn-sm">More Info</a>
          </div>
        </div>
      </div>
      {% endfor %}
    </div>
    <!-- /.row -->

{% else %}

<h3>COMING SOON ...</h3>

{% endif %}

an similarly in base.html

        <ul class="navbar-nav ml-auto">
{% for cat in category_list %}
          <li class="nav-item">
            <a class="nav-link" href="{% url 'category_detail' cat.slug %}">| {{cat.name}}</a>
          </li>
{% endfor %}
        </ul>

models.py

from django.db import models

class Category(models.Model):
    name = models.CharField(max_length=255)
    summary = models.TextField()
    slug = models.SlugField(max_length=255, unique=True)

    def __str__(self):
        return self.name

class Post(models.Model):
    title= models.CharField(max_length=255)
    category = models.ForeignKey('Category', on_delete=models.CASCADE)

    def __str__(self):
        return self.title

i followed a you tube video on creating these functions in the Views.py because I wanted to be able to display detail of a Category and the list of posts in it on the same page... I have a horrible feeling my error is due to defining category_list outside of a function...or worse!

Also, do I need to write a function to base.html to dynamically update the menu options?

the_end
  • 67
  • 9

1 Answers1

1

That's not your problem, and you do not need to add a custom function to make everything reload by itself. It's the fact that Django's bulk delete method doesn't actually call the delete() method.

This only suits if you are deleting through the admin site; if this happens in your custom template, something else is going on. But you're fine here.

From the docs:

The “delete selected objects” action uses QuerySet.delete() for efficiency reasons, which has an important caveat: your model’s delete() method will not be called.

If you wish to override this behavior, simply write a custom action which accomplishes deletion in your preferred manner – for example, by calling Model.delete() for each of the selected items.

So add this to your admin:

class CategoryAdmin(admin.ModelAdmin):
    actions=['really_delete_selected']

    def get_actions(self, request):
        actions = super(CategoryAdmin, self).get_actions(request)
        del actions['delete_selected']
        return actions

    def really_delete_selected(self, request, queryset):
        for obj in queryset:
            obj.delete()

        if queryset.count() == 1:
            message_bit = "1 Category entry was"
        else:
            message_bit = "%s category entries were" % queryset.count()
        self.message_user(request, "%s successfully deleted." % message_bit)
    really_delete_selected.short_description = "Delete selected entries"

admin.site.register(Entry, CategoryAdmin)

Reference

UPDATE

After reviewing your code again, I saw another vulnerability.

category_list = Category.objects.all()

def home(request):    
    return render(request,'home.html',{'category_list':category_list})

You must use category_list as a local variable -- which is, inside the home function. If you declared it globally (outside), it won't respond to the changes as it only refreshes once per run. Just to be clear, it will be

def home(request):    
    category_list = Category.objects.all()
    return render(request,'home.html',{'category_list':category_list})

If this works, feel free to remove the changes made to the admin dashboard. Seems like the delete thing is an actual issue in some cases.

crimsonpython24
  • 2,223
  • 2
  • 11
  • 27
  • I added your code to Admin changing" Entry" to "Category" in admin.site.register(Entry, CategoryAdmin)...the delete bit works....but now when I add a new Category that will not render!! – the_end Jul 29 '20 at 09:52
  • The admin dashboard or in general? – crimsonpython24 Jul 29 '20 at 10:11
  • when I log into Admin and add a Category it is visible in the Admin dashboard but it is not appearing on the webpage – the_end Jul 29 '20 at 10:39
  • thanks so much, I understand that now and it works as expected. My frustration is having to write the "category_list = Category.objects.all()" inside every function as otherwise the base.html will not show the categories in the Navbar on each webpage. that is the next problem to solve! – the_end Jul 29 '20 at 11:09