Search code examples
pythondjangodjango-urlscs50

Why does Django raise a NoReverseMatch error when using the 'entry' iterator in a For loop and url method?


NoReverseMatch at / Reverse for 'entry' with keyword arguments '{'title': ''}' not found. 1 pattern(s) tried:['wiki/(?P[^/]+)\Z'] Line 12.


    {% extends "encyclopedia/layout.html" %}
    
    {% block title %}
        Encyclopedia
    {% endblock %}
    
    {% block body %}
        <h1>All Pages</h1>
    
        <ul>
            {% for entry in entries %}
                <li><a href="{% url 'entry'  title=entry %}">{{ entry }}</a></li> 
                {% comment %} <p> {{ entry }} </p> {% endcomment %}
            {% endfor %}
        </ul> 
            {% comment %} <a href "/wiki/{{ entry }}"> <li>{{ entry }}</li> </a> {% endcomment %}
    
    {% endblock %}

I am trying to print a list of entries with their links, but Django complains about the iterator 'entry' in the For cycle, it can actually print the elements of the list, but when using the url django method, it won't render the page 'cause that error.

urls.py


    from django.urls import path
    
    from . import views
    
    urlpatterns = [
        path("", views.index, name="index"),
        path("wiki/<str:title>", views.entry, name="entry"),
        path("search/", views.search, name="search"),
        path("new/", views.new_page, name="new_page"),
        path( "edit/", views.edit, name="edit"),
        path( "save_edit/", views.save_edit, name="save_edit"),
        path( "rand/", views.rand, name= "rand")
    ]

views.py


    from django.shortcuts import render
    import markdown
    from . import util
    import random
    
    def convert_md_to_html(title):
        content = util.get_entry(title)
        markdowner = markdown.Markdown()
        if content == None:
            return None
        else:
            return markdowner.convert(content)
    
    
    def index(request):
        return render(request, "encyclopedia/index.html", {
            "entries": util.list_entries()
            
        })
    
    def entry(request, title):
        html_content = convert_md_to_html(title)
        if html_content == None:
            return render(request, "encyclopedia/error.html",{
                "message": "This entry does not exist"
            })
        else:
            return render(request, "encyclopedia/entry.html",{
                "title": title,
                "content": html_content
            })
        
    def search(request):
        if request.method == "POST":
            entry_search = request.POST['q']
            html_content = convert_md_to_html(entry_search)
            if html_content is not None:
                return render(request, "encyclopedia/entry.html",{
                    "title": entry_search,
                    "content":html_content
            })
    
            else:
                allEntries = util.list_entries()
                recommendation = []
                for entry in allEntries:
                    if entry_search.lower() in entry.lower():
                        recommendation.append(entry)
                return render(request, "encyclopedia/search.html", {
                    "recommendation": recommendation
                })
            
    def new_page(request):
        if request.method == "GET":
            return render(request, "encyclopedia/new.html")
        else:
            title = request.POST['title']
            content = request.POST['content']
            titleExist = util.get_entry(title)
            if titleExist is not None:
                return render(request, "encyclopedia/error.html", {
                    "message": "Entry page already exist"
                })
            else:
                util.save_entry(title, content)
                html_content = convert_md_to_html(title)
                return render(request, "encyclopedia/entry.html", {
                    "title": title,
                    "content": html_content
                })
    
    def edit(request):
        if request.method == 'POST':
            title = request.POST['entry_title']
            content = util.get_entry(title)
            return render(request, "encyclopedia/edit.html",{
                "title": title,
                "content": content
            })
    
    def save_edit(request):
        if request.method == "POST":
            title = request.POST['title']
            content = request.POST['content']
            util.save_entry(title, content)
            html_content = convert_md_to_html(title)
            return render(request, "encyclopedia/entry.html",{
                "title": title,
                "content": html_content
            })
    
    def rand(request):
        allEntries = util.list_entries()
        rand_entry = random.choice(allEntries)
        html_content = convert_md_to_html(rand_entry)
        return render(request, "encyclopedia/entry.html",{
            "title" : rand_entry,
            "content" : html_content
        })


Solution

  • As the error message says, one of the entries is an empty string ''. An empty string does not match with any url parameter.

    Looking into your view function, en empty string seems to be intentionally allowed. If so, you can try re_path

    from django.urls import re_path
    
    urlpatterns = [
        # regular paths
        re_path(r'^entry/(?P<title>[\w]*)$', views.entry, name='entry')
    ]
    

    Please note \w will match alphanumeric characters as well as underscore. If your entry contains other characters, e.g. dash -, you will have to change this re expression.