I am currently working on a tutorial in the "Python Crash course" Book.
The tutorial is about creating a "Learning Log" Webapp with Django.
The idea of the app is to allow users to:
1. create "Topics" they have learned about
2. add "Entries" to those Topics, describing details they have learned specific to those topics
I am currently stuck at creating an Entry form and receive an Error, when I run http://127.0.0.1:8000/new_entry/1
NoReverseMatch at /new_entry/1
Reverse for 'topic' with arguments '('',)' not found. 1 pattern(s) tried: ['topics/(?P<topic_id>[0-9]+)/$']
Request Method: GET
Request URL: http://127.0.0.1:8000/new_entry/1
Django Version: 3.0.5
Exception Type: NoReverseMatch
Exception Value:
Reverse for 'topic' with arguments '('',)' not found. 1 pattern(s) tried: ['topics/(?P<topic_id>[0-9]+)/$']
Exception Location: C:\Users\DR\Desktop\learning_log\ll_env\lib\site-packages\django\urls\resolvers.py in _reverse_with_prefix, line 677
Python Executable: C:\Users\DR\Desktop\learning_log\ll_env\Scripts\python.exe
Python Version: 3.8.2
Python Path:
['C:\\Users\\DR\\Desktop\\learning_log',
'C:\\Program '
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.8_3.8.752.0_x64__qbz5n2kfra8p0\\python38.zip',
'C:\\Program '
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.8_3.8.752.0_x64__qbz5n2kfra8p0\\DLLs',
'C:\\Program '
'Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.8_3.8.752.0_x64__qbz5n2kfra8p0\\lib',
'C:\\Users\\DR\\AppData\\Local\\Microsoft\\WindowsApps\\PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0',
'C:\\Users\\DR\\Desktop\\learning_log\\ll_env',
'C:\\Users\\DR\\Desktop\\learning_log\\ll_env\\lib\\site-packages']
Server time: Wed, 15 Apr 2020 19:46:06 +0000
The forms.py file:
from django import forms
from .models import Topic, Entry
class TopicForm(forms.ModelForm):
class Meta:
model = Topic
fields = ['text']
labels = {'text': ''}
class EntryForm(forms.ModelForm):
class Meta:
model = Entry
fields = ['text']
labels = {'text': ''}
widgets = {'text': forms.Textarea(attrs={'cols': 80})}
The urls.py file:
from django.urls import path
from . import views
app_name = "learning_logs"
urlpatterns = [
# --snip--
# Page for adding new entry
path('new_entry/<int:topic_id>', views.new_entry, name='new_entry'),
]
And views.py:
from django.shortcuts import render, redirect
from .models import Topic
from .forms import TopicForm, EntryForm
#--snip
def new_entry(request, topic_id):
"""Add a new entry for a particular topic."""
topic = Topic.objects.get(id=topic_id)
if request.method != 'POST':
# No data submitted; create a blank form.
form = EntryForm()
else:
# POST data submitted; process data.
form = EntryForm(data=request.POST)
if form.is_valid():
new_entry = form.save(commit=False)
new_entry.topic = topic
new_entry.save()
return redirect('learning_logs:topic', topic_id=topic_id)
# Display a blank or invalid form.
context = {'topic': topic, 'form': form}
return render(request, 'learning_logs/new_entry.html', context)
And finally the new_entry.html:
{% extends "learning_logs/base.html" %}
{% block content %}
<p>{{ topic }}</p>
<p>Add a new entry:</p>
<form action="{% url 'learning_logs:new_entry' topic.id %}" method='post'>
{% csrf_token %}
{{ form.as_p }}
<button name="submit">Add entry</button>
</form>
{% endblock content %}
And topic.html:
{% extends "learning_logs/base.html" %}
{% block content %}
<p>Topic: {{ topic }}</p>
<p>Entries:</p>
<p>
Add new entry
</p>
<ul>
{% for entry in entries %}
<li>
<p>{{ entry.date_added|date:'M d, Y H:i' }}</p>
<p>{{ entry.text|linebreaks }}</p>
</li>
{% empty %}
<li>There are no entries for this topic yet.</li>
{% endfor %}
</ul>
{% endblock content %}
Any help would be much appreciated!
The mistake was in new_entry.html:
It should read:
{% url 'learning_logs:topic' topic.id %}
and not:
{% url 'learning_logs:topic' topic_id %}
Thanks to Klaus for pointing me at the line!
Related
In a Django app I am building for a course, I am attempting to pass a parameter from one template to a function in views.py through a url path.
The path definition in urls.py contains the parameter name, and the same name is required by the function in views.py.
The link in my template points to the correct url path and names a value for the parameter, but I am still receiving a NoReverseMatch error. Weird because I have another url path that requires a parameter and which is working perfectly.
entry.html Here is the link to the path called edit in urls.py. I want to pass the value of the variable entryTitle to the url as entry:
{% extends "encyclopedia/layout.html" %}
{% block title %}
{{ entryTitle }}
{% endblock %}
{% block body %}
{{ entry|safe }}
<button>
Edit Entry
</button>
{% endblock %}
urls.py the edit path is the last one defined in urlpatterns
from django.urls import path
from . import views
urlpatterns = [
path("", views.index, name="index"),
path("wiki/<str:entry>", views.entry, name="entry"),
path("search", views.search, name="search"),
path("new", views.new_page, name="new"),
path("wiki/edit/<str:entry>", views.edit, name="edit")
]
views.py This is the edit function, requiring entry as an argument UPDATE: also showing the entry function
class EditPageForm(forms.Form):
content = forms.CharField(
widget=forms.Textarea(),
label="Edit Content:")
def edit(request, entry):
if request.method == "POST":
#Edit file and redirect
form = EditPageForm(request.POST)
if form.is_valid():
content = form.cleaned_data["content"]
util.save_entry(entry, content)
return HttpResponseRedirect(reverse('entry', kwargs={'entry': entry}))
else:
#Load form with initial values filled
content = util.get_entry(entry)
form = EditPageForm(initial={"content": content})
return render(request, "encyclopedia/edit.html", {
"editform": form,
"entryTitle": entry
})
def entry(request, entry):
markdowner = Markdown()
entryPage = util.get_entry(entry)
if entryPage is None:
return render(request, "encyclopedia/notfound.html", {
"entryTitle": entry
})
else:
return render(request, "encyclopedia/entry.html", {
"entry": markdowner.convert(entryPage),
"entryTitle": entry
})
Clicking the link in entry.html gives me this error:
NoReverseMatch at /wiki/edit/HTML
Reverse for 'edit' with no arguments not found. 1 pattern(s) tried: ['wiki/edit/(?P<entry>[^/]+)$']
If I view page source while on entry.html in the server, that link appears to correctly render the path with the correct value for entryTitle showing up, which I think means that some communication must be happening between the template and urls.py, but then the path isn't found when the link is clicked.
Here is the 'view page source' for entry.html with "HTML" as the value contained by entryTitle:
<button>
Edit Entry
</button>
Does anyone see anything wrong with my code or have ideas how I could troubleshoot? Been stuck on this one for a few days. Thank you
UPDATE here is the edit.html template:
{% extends "encyclopedia/layout.html" %}
{% block title %}
Edit Entry
{% endblock %}
{% block body %}
<h2>Edit encyclopedia entry for {{ entryTitle }}</h2>
<form action="{% url 'edit' %}" method="POST">
{% csrf_token %}
{{ editform }}
<input type="submit" value="Update">
</form>
{% endblock %}
I'm new to Django and programming in general. I'm going through the book Django For Beginners and have added my own flair in the forms section but have run into a problem. Below I have detailed the error and provided my code.
ERROR
NoReverseMatch at /drug/12/
Reverse for 'drug_edit' with keyword arguments '{'pk': ''}' not found. 1 pattern(s) tried: ['drug/(?P[0-9]+)/edit/$']
Request Method: GET
Request URL: http://127.0.0.1:8000/drug/12/
Django Version: 3.1.3
Exception Type: NoReverseMatch
Exception Value:
Reverse for 'drug_edit' with keyword arguments '{'pk': ''}' not found. 1 pattern(s) tried: ['drug/(?P[0-9]+)/edit/$']
Exception Location: C:\Users\scott\django_sites\history_cloud1\history1_env\lib\site-packages\django\urls\resolvers.py, line 685, in _reverse_with_prefix
Python Executable: C:\Users\scott\django_sites\history_cloud1\history1_env\Scripts\python.exe
Python Version: 3.8.3
models.py
from django.db import models
from django.urls import reverse
class DrugInfo(models.Model):
drug_name = models.CharField(max_length=75)
patient_name = models.CharField(max_length=25, default='Enter Patient Name',)
author = models.ForeignKey(
'auth.User',on_delete=models.CASCADE,
)
def __str__(self):
return self.drug_name
def get_absolute_url(self):
return reverse('drug_detail', args=[str(self.id)])
urls.py
from django.urls import path
from .views import (
drugListView,
drugDetailView,
drugCreateView,
drugDeleteView,
drugUpdateView,
)
urlpatterns = [
path('drug/<int:pk>/delete/',
drugDeleteView.as_view(), name='drug_delete'),
path('drug/new/', drugCreateView.as_view(), name='drug_new'),
path('drug/<int:pk>/', drugDetailView.as_view(), name='drug_detail'),
path('drug/<int:pk>/edit/',
drugUpdateView.as_view(), name='drug_edit'),
path('', drugListView.as_view(), name='home'),
]
views.py
from django.views.generic import ListView, DetailView
from django.views.generic.edit import (
CreateView, UpdateView, DeleteView
)
from django.urls import reverse_lazy
from .models import DrugInfo
class drugListView(ListView):
model = DrugInfo
template_name = 'home.html'
class drugDetailView(DetailView):
model = DrugInfo
template_name = 'drug_detail.html'
class drugCreateView(CreateView):
model = DrugInfo
template_name = 'drug_new.html'
fields = ['drug_name', 'patient_name', 'author']
class drugUpdateView(UpdateView): # new
model = DrugInfo
template_name = 'drug_edit.html'
fields = ['drug_name', 'patient_name']
class drugDeleteView(DeleteView): # new
model = DrugInfo
template_name = 'drug_delete.html'
success_url = reverse_lazy('home')
drug_edit.html
{% extends 'base.html' %}
{% block content %}
<h1>Edit Drug Information</h1>
<form action="" method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Update">
</form>
{% endblock content %}
base.html
{% load static %}
<html>
<head>
<title>Script Tracker Title Base HTML</title>
<link href="https://fonts.googleapis.com/css?family=Tangerine" rel="stylesheet">
<link href="{% static 'css/base.css' %}" rel="stylesheet">
</head>
<body>
<div>
<header>
<div class="nav-left">
<h1>Script Tracker</h1>
</div>
<div class="nav-right">
+ Add New Drug
</div>
</header>
{% block content %}
{% endblock content %}
</div>
</body>
</html>
drug_detail.html
{% extends 'base.html' %}
{% block content %}
<div class="drug-entry">
<h2>{{ post.drug_name }}</h2>
<h2>{{ post.patient_name }}</h2>
</div>
<p>+ Edit Drug Information</p>
<p>+ Delete Drug</p>
{% endblock content %}
Summary
I'm very new to all this and so I apologize if I have not posted all the needed information. I know I'm close and I have something messed up with the pk stuff. http://127.0.0.1:8000/drug/12/edit/ works fine when I type in the browser. Any suggestions would be greatly appreciated.
In your template drug_detail.html you refer to the DrugInfo object as follows: post or drug. While in fact the drugDetailView never passes such variables into the context. A DetailView by default passes the instance as object or <lowercase_model_name> (druginfo here). If you want to set the name it uses you can set context_object_name on the class:
class drugDetailView(DetailView):
model = DrugInfo
template_name = 'drug_detail.html'
context_object_name = 'drug'
Now in the template drug_detail.html:
{% extends 'base.html' %}
{% block content %}
<div class="drug-entry">
<!-- Use `drug` instead of `post` now -->
<h2>{{ drug.drug_name }}</h2>
<h2>{{ drug.patient_name }}</h2>
</div>
<p>+ Edit Drug Information</p>
<p>+ Delete Drug</p>
{% endblock content %}
I am trying to make a website that sorts equipment based on their category and i am getting the reverse match error when i link the urls on the webpage. Please i need advice on how i can properly pull this off. Thanks in advance.
urls.py
from django.urls import path
from . import views
app_name = 'equipments_catalogue'
urlpatterns = [
path('equipment_list/', views.EquipmentListView.as_view(), name='equipment_list'),
path('equipment_categories/', views.EquipmentCategoryView.as_view(),
name='equipment_categories'),
path('equipment_by_category/<str:cats>/', views.EquipmentListByCategory,
name='equipment_by_category')
]
views.py
from django.shortcuts import render
from django.views.generic import ListView
from . import models
# Create your views here.
class EquipmentCategoryView(ListView):
model = models.Category
template_name = 'equipment_categories.html'
def EquipmentListByCategory(request, cats):
equipment_category = models.Equipment.objects.filter(category=cats)
return render(request, 'equipment_by_category.html', {'cats': cats , 'equipment_category':
equipment_category})
class EquipmentListView(ListView):
model = models.Equipment
template_name = 'equipment_list.html'
template
{% extends 'base.html' %}
{% load static %}
{% block title %}Home{% endblock %}
{% block content %}
<h1>Welcome to JPI Equipment Categories Page</h1>
{% for category in object_list %}
<a href="{% url 'equipments_catalogue:equipment_categories' equipment_by_category.category
%}">{{ category.name }}</a><br><br>
{% endfor %}
{% endblock %}
You're calling it wrong. The correct way to call it is {% url '<app_name>:<url_name>' %}
Your app name is equipments_catalogue, your url name is equipment_by_category, not equipment_categories, which is correct, and the argument you should be passing should be a string, something like category.name:
{% url 'equipments_catalogue:equipment_by_category' category.name %}
Python/Django beginner here - I get this error:
Reverse for 'topic' with arguments '('',)' and keyword arguments '{}'
not found. 1 pattern(s) tried: ['topics/(?P\d+)/$']
when trying to load my template.
This is my template:
{% extends "learning_logs/base.html" %}
{% block content %}
<p>Topics</p>
<ul>
{% for topic in topics %}
<li>
{{ topic }}
</li>
{% empty %}
<li>No topics for now</li>
{% endfor %}
</ul>
{% endblock content %}
This is my views.py
from django.shortcuts import render
from .models import Topic
# Create your views here.
def index(request):
'''Home page for learning log'''
return render(request, 'learning_logs/index.html')
def topics(request):
'''Show all topics'''
topics = Topic.objects.order_by('date_added')
context = {'topics': topics}
return render(request, 'learning_logs/topics.html', context)
def topic(request, topic_id):
'''Show a single topic and all its entries'''
topic = Topic.objects.get(id=topic_id)
entries = topic.entry_set.order_by('-date_added')
context = {'topic': topic, 'entries': entries}
return render(request, 'learning_logs/topic.html', context)
I have been on this a while now, read some previous answers here, but they all had to do with auth/login not working. Also tried removing the '' after the url as some answers suggested but it didnt work. I am using Python Crash Course: A Hands-On, Project-Based Introduction to Programming for my tutorials.
Any help will be appreciated.
Finally, this is my urls.py code
from django.conf.urls import url
from . import views
urlpatterns = [
# Home page
url(r'^$', views.index, name='index'),
url(r'^topics/$', views.topics, name='topics'),
url(r'^topics/(?P<topic_id>\d+)/$', views.topics, name='topic'),
According to the error, there was an argument passed into the url tag, but it was empty:
Reverse for 'topic' with arguments '('',)'...
That's because of the topic_id variable, it is not defined. You should use topic.id instead:
{{ topic }}
I need some help getting the add_page function to work properly. I am very new to HTML and even newer to Django. The chapter I am working on can be found here: http://www.tangowithdjango.com/book17/chapters/forms.html. Currently my relevent files look like this:
Forms.py
from django import forms
from rango.models import Page, Category
class CategoryForm(forms.ModelForm):
name = forms.CharField(max_length=128, help_text="Please enter the category name.")
views = forms.IntegerField(widget=forms.HiddenInput(), initial=0)
likes = forms.IntegerField(widget=forms.HiddenInput(), initial=0)
slug = forms.CharField(widget=forms.HiddenInput(), required=False)
# An inline class to provide additional information on the form.
class Meta:
# Provide an association between the ModelForm and a model
model = Category
fields = ('name',)
class PageForm(forms.ModelForm):
title = forms.CharField(max_length=128, help_text="Please enter the title of the page.")
url = forms.URLField(max_length=200, help_text="Please enter the URL of the page.")
views = forms.IntegerField(widget=forms.HiddenInput(), initial=0)
class Meta:
# Provide an association between the ModelForm and a model
model = Page
# What fields do we want to include in our form?
# This way we don't need every field in the model present.
# Some fields may allow NULL values, so we may not want to include them...
# Here, we are hiding the foreign key.
# we can either exclude the category field from the form,
exclude = ('category',)
#or specify the fields to include (i.e. not include the category field)
#fields = ('title', 'url', 'views')
def clean(self):
cleaned_data = self.cleaned_data
url = cleaned_data.get('url')
# If url is not empty and doesn't start with 'http://', prepend 'http://'.
if url and not url.startswith('http://'):
url = 'http://' + url
cleaned_data['url'] = url
return cleaned_data
Views.py:
from django.shortcuts import render
from django.http import HttpResponse
from rango.models import Category, Page
from rango.forms import CategoryForm, PageForm
def index(request):
# Query the database for a list of ALL categories currently stored.
# Order the categories by no. likes in descending order.
# Retrieve the top 5 only - or all if less than 5.
# Place the list in our context_dict dictionary which will be passed to the template engine.
category_list = Category.objects.order_by('-likes')[:5]
page_list = Page.objects.order_by('-view')[:5]
context_dict = {'categories': category_list,
'pages': page_list}
# Render the response and send it back!
return render(request, 'rango/index.html', context_dict)
def category(request, category_name_slug):
# Create a context dictionary which we can pass to the template rendering engine.
context_dict = {}
try:
# Can we find a category name slug with the given name?
# If we can't, the .get() method raises a DoesNotExist exception.
# So the .get() method returns one model instance or raises an exception.
category = Category.objects.get(slug=category_name_slug)
context_dict['category_name'] = category.name
context_dict['category_name_slug'] = category_name_slug
# Retrieve all of the associated pages.
# Note that filter returns >= 1 model instance.
pages = Page.objects.filter(category=category)
# Adds our results list to the template context under name pages.
context_dict['pages'] = pages
# We also add the category object from the database to the context dictionary.
# We'll use this in the template to verify that the category exists.
context_dict['category'] = category
except Category.DoesNotExist:
# We get here if we didn't find the specified category.
# Don't do anything - the template displays the "no category" message for us.
pass
# Go render the response and return it to the client.
print context_dict
return render(request, 'rango/category.html', context_dict)
def add_category(request):
# A HTTP POST?
if request.method == 'POST':
form = CategoryForm(request.POST)
# Have we been provided with a valid form?
if form.is_valid():
# Save the new category to the database.
form.save(commit=True)
# Now call the index() view.
# The user will be shown the homepage.
return index(request)
else:
# The supplied form contained errors - just print them to the terminal.
print form.errors
else:
# If the request was not a POST, display the form to enter details.
form = CategoryForm()
# Bad form (or form details), no form supplied...
# Render the form with error messages (if any).
return render(request, 'rango/add_category.html', {'form': form})
def add_page(request, category_name_slug):
try:
cat = Category.objects.get(slug=category_name_slug)
except Category.DoesNotExist:
cat = None
if request.method == 'POST':
form = PageForm(request.POST)
if form.is_valid():
if cat:
page = form.save(commit=False)
page.category = cat
page.views = 0
page.save()
# probably better to use a redirect here.
return category(request, category_name_slug)
else:
print form.errors
else:
form = PageForm()
context_dict = {'form':form, 'category': cat}
return render(request, 'rango/add_page.html', context_dict)
urls.py
from django.conf.urls import patterns, url
from rango import views
urlpatterns = patterns('',
url(r'^$', views.index, name='index'),
# url(r'^about/$', views.about, name='about'),
url(r'^add_category/$', views.add_category, name='add_category'),
url(r'^category/(?P<category_name_slug>[\w\-]+)/add_page/$', views.add_page, name='add_page'),
url(r'^category/(?P<category_name_slug>[\w\-]+)/$', views.category, name='category'),)
I think this ^ is where I am encountering the issue. I manage to get to the "add a page" screen, but when I try to submit something, I receive an error that states I am only supplying 1 argument and add_page() requires 2. I think I may need an additional url that is similar to the "add_category" URL, but that must mean by other URL is pointing to the wrong place?
category.html
<!DOCTYPE html>
<html>
<head>
<title>Rango</title>
</head>
<body>
<h1>{{ category_name }}</h1>
{% if category %}
{% if pages %}
<ul>
{% for page in pages %}
<li>{{ page.title }}</li>
{% endfor %}
</ul>
{% else %}
<strong>No pages currently in category.</strong>
{% endif %}
<li>Add a New Page</li>
{% else %}
The specified category {{ category_name }} does not exist!
{% endif %}
</body>
</html>
add_page.html:
<!DOCTYPE html>
<html>
<head>
<title>Rango</title>
</head>
<body>
<h1>Add a Page</h1>
<form id="page_form" method="post" action="/rango/add_page/">
{% csrf_token %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% for field in form.visible_fields %}
{{ field.errors }}
{{ field.help_text }}
{{ field }}
{% endfor %}
<input type="submit" name="submit" value="Create Page" />
</form>
</body>
</html>
I edited the add_page function to include category_name_slug:
def add_page(request, category_name_slug):
try:
cat = Category.objects.get(slug=category_name_slug)
except Category.DoesNotExist:
cat = None
if request.method == 'POST':
form = PageForm(request.POST)
if form.is_valid():
if cat:
page = form.save(commit=False)
page.category = cat
page.views = 0
page.save()
# probably better to use a redirect here.
return category(request, category_name_slug)
else:
print form.errors
else:
form = PageForm()
# made the change here
context_dict = {'form':form, 'category': cat, 'category_name_slug': category_name_slug}
return render(request, 'rango/add_page.html', context_dict)
Then I edited the add_page.html to look like this:
<!DOCTYPE html>
<html>
<head>
<title>Rango</title>
</head>
<body>
<h1>Add a Page</h1>
<form id="page_form" method="post" action="/rango/category/{{ category_name_slug }}/add_page/">
{% csrf_token %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% for field in form.visible_fields %}
{{ field.errors }}
{{ field.help_text }}
{{ field }}
{% endfor %}
<input type="submit" name="submit" value="Create Page" />
</form>
</body>
</html>
if you dont wanna edit your views.py
just doit
<!DOCTYPE html>
<html>
<head>
<title>Rango</title>
</head>
<body>
<h1>Add a Page</h1>
<form id="page_form" method="post" action="/rango/category/{{ category }}/add_page/">
{% csrf_token %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% for field in form.visible_fields %}
{{ field.errors }}
{{ field.help_text }}
{{ field }}
{% endfor %}
<input type="submit" name="submit" value="Create Page" />
</form>
</body>
</html>
but i have problem to it still cannot be save on database.