2

I'm super stuck with NoReverseMatch at /posts/ error. Maybe I am overlooking something small or maybe this issue is much bigger than where I am digging.

https://i.stack.imgur.com/jB0jB.jpg Error message when models.py is like this:

def get_absolute_url(self):
        #return reverse("detail", kwargs={ "slug": self.slug })
        return reverse("posts:detail", kwargs={ "slug": self.slug })

https://i.stack.imgur.com/T56YC.jpg Error message when models.py is like this:

def get_absolute_url(self):
        return reverse("detail", kwargs={ "slug": self.slug })
        #return reverse("posts:detail", kwargs={ "slug": self.slug })

models.py

#from _future_ import __unicode__literals
from django.db import models
from django.core.urlresolvers import reverse
from django.db.models.signals import pre_save

from django.utils.text import slugify

# Create your models here.
# MVC Models Views Controller

def upload_location(instance, filename):
    # filebase, extension = filename.spilt(".")
    # return "%s/%s.%s" %(instance.id, instance.id, filename)
    return "%s/%s" %(instance.id, filename)

class Post(models.Model):
    title = models.CharField(max_length=120)
    slug = models.SlugField(unique=True)
    image = models.ImageField(upload_to=upload_location, 
        null=True, 
        blank=True, 
        width_field="width_field", 
        height_field="height_field")
    height_field = models.IntegerField(default=0)
    width_field = models.IntegerField(default=0)
    content = models.TextField()
    updated = models.DateTimeField(auto_now=True, auto_now_add=False)
    timestamp = models.DateTimeField(auto_now=False, auto_now_add=True)

    def __unicode__(self):
        return self.title

    def __str__(self):
        return self.title

    def get_absolute_url(self):
        #return reverse("detail", kwargs={ "slug": self.slug })
        return reverse("posts:detail", kwargs={ "slug": self.slug })

    class Meta:
        ordering = ["-timestamp", "-updated"]


def create_slug(instance, new_slug=None):
    slug = slugify(instance.title)
    if new_slug is not None:
        slug = new_slug
    qs = Post.objects.filter(slug=slug).order_by("-id")
    exists = qs.exists()
    if exists:
        new_slug = "%s-%s" %(slug, qs.first().id)
        return create_slug(instance, new_slug=new_slug)
    return slug

def pre_save_post_receiver(sender, instance, *args, **kwargs):
    if not instance.slug:
        instance.slug = create_slug(instance)

pre_save.connect(pre_save_post_receiver, sender=Post)

views.py

from django.contrib import messages
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render, get_object_or_404, redirect

from .forms import PostForm
# Create your views here.
from .models import Post


def post_create(request):
    form = PostForm(request.POST or None, request.FILES or None,)
    if form.is_valid():
        instance = form.save(commit=False)
        instance.save()
        # message success
        messages.success(request, "Successfully Created!")
        return HttpResponseRedirect(instance.get_absolute_url())
    context = {
        "form": form,
    }
    return render(request, "post_form.html", context)

def post_detail(request, slug=None):
    #instant = Post.objects.get(id=0)
    instance = get_object_or_404(Post, slug=slug)
    context = {
        "title": instance.title,
        "instance": instance,
    }
    return render(request, "post_detail.html", context)

def post_list(request):
    queryset_list = Post.objects.all() #.order_by("-timestamp")
    paginator = Paginator(queryset_list, 5) # Show 5 contacts per page
    page_request_var = "page"
    page = request.GET.get(page_request_var)
    try:
        queryset = paginator.page(page)
    except PageNotAnInteger:
        # If page is not an integer, deliver first page.
        queryset = paginator.page(1)
    except EmptyPage:
        # If page is out of range (e.g. 9999), deliver last page of results.
        queryset = paginator.page(paginator.num_pages)

    context = {
        "object_list": queryset,
        "title": "List",
        "page_request_var": page_request_var
        }
    return render(request, "post_list.html", context)
    #return HttpResponse("<h1>List</h1>")


def post_update(request, slug=None):
    instance = get_object_or_404(Post, slug=slug)
    form = PostForm(request.POST or None, request.FILES or None, instance=instance)
    if form.is_valid():
        instance = form.save(commit=False)
        instance.save()
        # message success
        messages.success(request, "<a href='#'>Item</a> Saved", extra_tags='html_safe')
        return HttpResponseRedirect(instance.get_absolute_url())

    context = {
        "title": instance.title,
        "instance": instance,
        "form":form,
    }
    return render(request, "post_form.html", context)   

def post_delete(request, slug=None):
    instance = get_object_or_404(Post, slug=slug)
    instance.delete()
    messages.success(request, "Successfully deleted!")
    return redirect("posts:list")

urls.py

from django.conf.urls import url
from django.contrib import admin
from . import views

# from .views import (
#     post_list,
#     post_create,
#     post_detail,
#     post_update,
#     post_delete,
#     )

# video: https://www.youtube.com/watch?annotation_id=annotation_3160187747&feature=iv&index=14&list=PLEsfXFp6DpzQFqfCur9CJ4QnKQTVXUsRy&src_vid=nw3R2TXlkwY&v=1KuyH8JVn6A 
# shows a different solution here for version 1.10

urlpatterns = [
    #url(r'^$', views.post_home),
    # url(r'^$', views.post_list, name='list'),
    # url(r'^create/', views.post_create), #name='create'
    # url(r'^(?P<slug>[\w-]+)/', views.post_detail, name='detail'), 
    # url(r'^(?P<slug>[\w-]+)/edit/', views.post_update, name='update'),
    # url(r'^(?P<slug>[\w-]+)/delete/', views.post_delete),

    url(r'^$', views.post_list, name='list'),
    url(r'^create/$', views.post_create, name='create'),
    url(r'^(?P<slug>[\w-]+)/$', views.post_detail, name='detail'),
    url(r'^(?P<slug>[\w-]+)/edit/$', views.post_update, name='update'),
    url(r'^(?P<slug>[\w-]+)/delete/$', views.post_delete),

    # url(r'^$', posts.views.post_home),
    # url(r'^create/', home_create),
    # url(r'^list/', home_list),
    # url(r'^detail/', home_detail),
    # url(r'^update/', home_update),
    # url(r'^delete/', home_delete),

    # url(r'^posts/$', "<appname>.views.<function_name>"),
]

post_list.py

{% extends "base.html" %}

{% block head_title %}
{{ instance.title }} | {{ block.super }}
{% endblock head_title %}


{% block content %}

    <div class="row justify-content-md-center">
        <div class='col-md-8'>
            <h1>{{title}}</h1>
        </div>

        {% for obj in object_list %}

        <div class='col-md-8 pt-3'>
            <div class="card" style="min-width: 20rem;">
                {% if obj.image %}
                <img src="{{ obj.image.url }}" class="card-img-top">
                {% endif %}
                <div class="card-body">
                <h4 class="card-title">
                    <a href='{{ obj.get_absolute_url }}'>{{ obj.title }}</a>
                    <small>{{ obj.timestamp|timesince }} ago</small>
                </h4>
                <p class="card-text">{{ obj.content|linebreaks|truncatechars:120 }}</p>
                <p class="card-text">{{ obj.updated }}</p>
                <p class="card-text">{{ obj.id }}</p>
                <!-- <a href='{% url "posts:detail" id=obj.id %}'>{{ obj.title }}</a><br/>  -->
                <a href="{{ obj.get_absolute_url }}" class="btn btn-primary">View</a>
                </div>
            </div>
        </div>

        {% endfor %}

        <div class='col-md-8'>
            <div class="pagination">
                <span class="step-links">
                    {% if object_list.has_previous %}
                        <a href="?{{ page_request_var }}={{ object_list.previous_page_number }}">previous</a>
                    {% endif %}

                    <span class="current">
                        Page {{ object_list.number }} of {{ object_list.paginator.num_pages }}.
                    </span>

                    {% if object_list.has_next %}
                        <a href="?{{ page_request_var }}={{ object_list.next_page_number }}">next</a>
                    {% endif %}
                </span>
            </div>
        </div>

    </div>

{% endblock content %}
LeoFranco
  • 23
  • 7
  • 1
    Please, can you narrow your question down? – Jan Aug 23 '17 at 18:23
  • I'd be happy too. Just not sure where to begin. My issue is that after changing my blog posts from ID to Slug URLs I now get an error page. Can I expand on anything specific? – LeoFranco Aug 23 '17 at 18:30
  • Please don't post images of errors. Copy and paste the text in your question. – Alasdair Aug 23 '17 at 18:39
  • Your first version of `get_absolute_url` with the namespace is correct. The problem is that you still have some code somewhere else that is calling `reverse` or using the `{% url %}` tag with the id instead of the slug. – Alasdair Aug 23 '17 at 18:41
  • @Alasdair that makes a lot of sense (and sorry for the links, that it was cleaner). And that's what I've been looking for. Anything I could post here that you would like to look at? – LeoFranco Aug 23 '17 at 18:47
  • The problem might be in your `post_list.html` or base template. If you can't find any templates or views that are still using the id instead of the slug, then perhaps you've forgotten to save the changes, or you haven't restarted the server after restarting changes. – Alasdair Aug 23 '17 at 18:53
  • I have dreamed that that was the issue. Removed my db and started new migrations, restarted computer, etc. I think you earlier suggestion might be right. Somewhere there is old code stuck. I updated my question and added post_list.py – LeoFranco Aug 23 '17 at 19:04

1 Answers1

3

The error is coming from this line

<!-- <a href='{% url "posts:detail" id=obj.id %}'>{{ obj.title }}</a><br/>  -->

<!-- is an html comment, so Django still tries to reverse the url when it renders the template.

You can either remove the line entirely, change it to a Django comment {# ... #}, or change it to use the slug instead of id.

Alasdair
  • 298,606
  • 55
  • 578
  • 516
  • O my.. you are my Hero. How would you suggest writing "safe" comments? Or should I leave them out of certain areas? – LeoFranco Aug 23 '17 at 21:39
  • As I said, the Django template language allows single line [comments](https://docs.djangoproject.com/en/1.11/topics/templates/#comments) with `{# comment goes here #}`. – Alasdair Aug 23 '17 at 21:57