This is the error I get when clicking on Edit post under any one of the posts. Would appreciate any help as all this django stuff is confusing me but trying my best to learn. My new post function works and clicking blog/posts to go to the overview page for the blog or to look at all the posts works as well.
NoReverseMatch at /edit_post/1/
Reverse for 'posts' with arguments '(1,)' not found. 1 pattern(s) tried: ['posts/$']
Error during template rendering
In template C:\Users\seng\Desktop\Python projects\c19\nineteen_one\blogs\templates\blogs\base.html, error at line 0
urls.py
"""Defines url paterns for blogs"""
from django.urls import path
from . import views
app_name = 'blogs'
urlpatterns =[
#Home page
path('', views.index, name='index'),
# Page that shows all posts/
path('posts/', views.posts, name='posts'),
#Page for adding a new blogpost
path('new_post/', views.new_post, name='new_post'),
#Page for editing a post
#maybe remove the id?
path('edit_post/<int:post_id>/', views.edit_post, name='edit_post'),
]
views.py
from django.shortcuts import render, redirect
from .models import BlogPost
from .forms import BlogPostForm
# Create your views here.
def index(request):
"""The home page for blogs"""
return render(request, 'blogs/index.html')
def posts(request):
"""Show all blogposts"""
posts = BlogPost.objects.order_by('date_added')
context = {'posts': posts}
return render(request, 'blogs/posts.html', context)
def new_post(request):
"""Add a new blogpost"""
if request.method != 'POST':
#No data submitted; create a blank form.
form = BlogPostForm()
else:
#POST data submitted, process data
form = BlogPostForm(data=request.POST)
if form.is_valid():
form.save()
return redirect('blogs:posts')
#Display a blank or invalid form
context = {'form': form}
return render(request, 'blogs/new_post.html', context)
def edit_post(request, post_id):
"""Edit existing post"""
post = BlogPost.objects.get(id=post_id)
if request.method != "POST":
#Initial request, pre-fill form with the current post
form = BlogPostForm(instance=post)
else:
#Post data submitted, process data
form = BlogPostForm(instance=post, data=request.POST)
if form.is_valid():
form.save()
return redirect('blogs:posts', post_id=post.id)
context = {'post':post, 'form':form}
return render(request, 'blogs/edit_post.html', context)
forms.py
from django import forms
from .models import BlogPost
class BlogPostForm(forms.ModelForm):
class Meta:
model = BlogPost
fields = ['text', 'title']
labels = {'text':'This is the text box', 'title' :"Title here"}
edit_post.html
{% extends "blogs/base.html" %}
{% block content %}
<p>{{ post }}</p>
<p>Edit post:</p>
<form action="{% url 'blogs:edit_post' post.id %}" method='post'>
{% csrf_token %}
{{ form.as_p }}
<button name="submit">Save changes</button>
</form>
{% endblock content %}
posts.html
{% extends "blogs/base.html" %}
{% block content %}
<p>Posts</p>
<ul>
{% for post in posts %}
<li>
<p>{{ post }}</p>
<p>
Edit post
</p>
</li>
{% empty %}
<li>No posts have been added yet.</li>
{% endfor %}
</ul>
Add a new post
{% endblock content %}
new_post.html
{% extends "blogs/base.html" %}
{% block content %}
<p>Add a new post:</p>
<form action="{% url 'blogs:new_post' %}" method='post'>
{% csrf_token %}
{{ form.as_p }}
<button name="submit">Add post</button>
</form>
{% endblock content %}
The issue is with this line in edit_post.html:
<p>{{ post }}</p>
If you are editing the post with id 1, then this link is to the url /posts/1. But that has no match in your urls.py file.
Either you need to remove the post.id parameter from the link, or create a view and a corresponding path in urls.py for this link.
Related
I am trying to create a way to edit individual blog posts from their individual html. Here are the relevant files and trace back. I am somewhat understanding that the issue lies in blog_post.id being due to the fact that blog_post has not carried over from the for loop on blog_posts.html. I have read up on others having this issue and they all structured their pages to have the edit button being inside the original for loop, which makes sense in hindsight. BUT now that I have run into this issue, I'm determined to understand how I can solve it without going back and restructuring my pages to align with the others I saw.
urls.py
from django.urls import path
from . import views
app_name = 'blogs'
urlpatterns = [
# Home page
path('', views.index, name='index'),
path('blog_posts/', views.blog_posts, name='blog_posts'),
path('blog_posts/<int:blog_post_id>/', views.blog_post, name='blog_post'),
path('new_blog_post/', views.new_blog_post, name='new_blog_post'),
path('edit_blog_post/<int:blog_post_id>/', views.edit_blog_post, name='edit_blog_post'),
]
views.py
from .models import BlogPost
from .forms import BlogPostForm
def index(request):
"""Home page for Blog."""
return render(request, 'blogs/index.html')
def blog_posts(request):
"""Show all Blog Posts."""
blog_posts = BlogPost.objects.order_by('date_added')
context = {'blog_posts': blog_posts}
return render(request, 'blogs/blog_posts.html', context)
def blog_post(request, blog_post_id):
"""Show details of an individual blog post."""
blog_post = BlogPost.objects.get(id=blog_post_id)
title = blog_post.title
id = blog_post_id
date = blog_post.date_added
text = blog_post.text
context = {'title': title, 'text': text, 'date': date}
return render(request, 'blogs/blog_post.html', context)
def new_blog_post(request):
"""Add a new blog post"""
if request.method != 'POST':
# No data submitted, create a blank form.
form = BlogPostForm()
else:
# POST data submitted, process data.
form = BlogPostForm(data=request.POST)
if form.is_valid():
form.save()
return redirect('blogs:blog_posts')
# Display a blank or invalid form.
context = {'form': form}
return render(request, 'blogs/new_blog_post.html', context)
def edit_blog_post(request, blog_post_id):
"""Edit an existing blog post's title or text."""
blog_post = BlogPost.objects.get(id=blog_post_id)
if request.method != 'POST':
# Initial request, prefill with the current data.
form = BlogPostForm(instance=blog_post)
else:
# POST data submitted; process new data.
form = BlogPostForm(instance=blog_post, data=request.POST)
if form.is_valid():
form.save()
return redirect('blogs:blog_post', blog_post_id=blog_post.id)
context = {'blog_post': blog_post, 'form': form}
return render(request, 'blogs/edit_blog_post.html', context)
models.py
from django.db import models
class BlogPost(models.Model):
"""A post the user is posting on their blog."""
title = models.CharField(max_length=200)
text = models.TextField()
date_added = models.DateTimeField(auto_now_add=True)
def __str__(self):
"""Return a string representation of the model"""
return f"{self.title.title()}"
blog_posts.html
{% extends 'blogs/base.html' %}
{% block content %}
<p>Blog Posts</p>
<ul>
{% for blog_post in blog_posts %}
<li>
{{ blog_post }}
</li>
{% empty %}
<li>No posts have been made yet.</li>
{% endfor %}
</ul>
Add a new blog post
{% endblock content %}
blog_post.html
{% extends 'blogs/base.html' %}
{% block content %}
<p>Blog Post: {{ title }}</p>
<p>Entry:</p>
<p>{{ text }}</p>
<p>{{ date }}</p>
<p>
Edit Blog Post
</p>
{% endblock content %}
edit_blog_post.html
{% extends "blogs/base.html" %}
{% block content %}
<p>
{{ blog_post }}
</p>
<p>Edit Blog Post</p>
<form action="{% url 'blogs:edit_blog_post' blog_post.id %}" method='post'>
{% csrf_token %}
{{ form.as_p }}
<button name="submit">Save Changes</button>
</form>
{% endblock content %}
Reverse for 'edit_blog_post' with arguments '('',)' not found. 1 pattern(s) tried: ['edit_blog_post/(?P<blog_post_id>[0-9]+)/\Z']
3 {% block content %}
4
5 <p>Blog Post: {{ title }}</p>
6
7 <p>Entry:</p>
8
9 <p>{{ text }}</p>
10 <p>{{ date }}</p>
11
12 <p>
13 Edit Blog Post
14 </p>
15
16 {% endblock content %}
If I've read the question correctly, You're getting the error becuase you are not providing the necessary ID to the URL construction part of your template.
You're separating out the elements (date, content etc) to send to the template, but not passing the ID at the same time. You could send the ID in as a separate context variable, but that's extra typing for no real reward.
It's easiest to pass in the post itself via context and refer to its attributes in the template - I think it makes it easier to read also. That way the ID is there when you need to contruct the edit link, and if you change the model to possess extra fields, you don't need to convert and add to the context as the whole post is already there.
views.py
def blog_post(request, blog_post_id):
"""Show details of an individual blog post."""
blog_post = BlogPost.objects.get(id=blog_post_id) #this is all we need
context = {"blog_post_context": blog_post}
return render(request, 'blogs/blog_post.html', context)
blog_post.html
{% extends 'blogs/base.html' %}
{% block content %}
<p>Blog Post: {{ blog_post_context.title }}</p>
<p>Entry:</p>
<p>{{ blog_post_context.text }}</p>
<p>{{ blog_post_context.date }}</p>
<p>
Edit Blog Post
</p>
{% endblock content %}
If that all works, look into using get_object_or_404 rather than Post.objects.get for some additional robustness.
I assume you got the error when you try visiting the blog_post.html page. If I'm correct, then here's an approach you could take...
In your views.py
def blog_post(request, blog_post_id):
"""Show details of an individual blog post."""
# blog_post = BlogPost.objects.get(id=blog_post_id)
blog_post = get_object_or_404(id=blog_post_id) # Recommended
# Commented lines below are somewhat not necessary...
# title = blog_post.title
# id = blog_post_id
# date = blog_post.date_added
# text = blog_post.text
context = {'blog_post': blog_post}
return render(request, 'blogs/blog_post.html', context)
edit_blog_post.html is expecting an object called blog_post to be able to access the blog_post.id for {% url 'blogs:edit_blog_post' blog_post.id %}.
Now within the edit_blog_post.html file.
{% block content %}
<p>Blog Post: {{ blog_post.title }}</p>
<p>Entry:</p>
<p>{{ blog_post.text }}</p>
<p>{{ blog_post.date_added }}</p>
<p>
Edit Blog Post
</p>
{% endblock content %}
Im currently following a Django tutorial to learn views and URLs. I have watched the tutorial over and over and cant see what I am doing wrong.
I receive the error:
Exception Value:
Reverse for 'list-events' not found. 'list-events' is not a valid view function or pattern name.
Views.py
from django.shortcuts import render, redirect
import calendar
from calendar import HTMLCalendar
from datetime import datetime
from .models import Event, Venue
from .forms import VenueForm, EventForm
from django.http import HttpResponseRedirect
# Create your views here.
# all events is listed in the urls.py hence why the function is named all_events
def update_event(request, event_id):
event = Event.objects.get(pk=event_id)
form = EventForm(request.POST, instance=event)
if form.is_valid():
form.save()
return redirect('list-events')
return render(request, 'events/update_event.html',
{'event': event,
'form':form})
def add_event(request):
submitted = False
if request.method == 'POST':
form = EventForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect('/add_event?submitted=True')
else:
form = EventForm
if 'submitted' in request.GET:
submitted = True
return render(request, 'events/add_event.html', {'form': form, 'submitted': submitted})
def update_venue(request, venue_id):
venue = Venue.objects.get(pk=venue_id)
form = VenueForm(request.POST, instance=venue)
if form.is_valid():
form.save()
return redirect('list-venues')
return render(request, 'events/update_venue.html',
{'venue': venue,
'form': form})
def search_venues(request):
if request.method == "POST":
searched = request.POST['searched']
venues = Venue.objects.filter(name__contains=searched)
return render(request,
'events/search_venues.html',
{'searched': searched,
'venues': venues})
else:
return render(request,
'events/search_venues.html',
{})
def show_venue(request, venue_id):
venue = Venue.objects.get(pk=venue_id)
return render(request, 'events/show_venue.html',
{'venue': venue})
def list_venues(request):
venue_list = Venue.objects.all()
return render(request, 'events/venue.html',
{'venue_list': venue_list})
URLS.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.home, name="home"),
path('<int:year>/<str:month>/', views.home, name="home"),
path('events', views.all_events, name='list_events'),
path('add_venue', views.add_venue, name='add-venue'),
path('list_venues', views.list_venues, name='list-venues'),
path('show_venue/<venue_id>', views.show_venue, name='show-venue'),
path('search_venues', views.search_venues, name='search-venues'),
path('update_venue/<venue_id>', views.update_venue, name='update-venue'),
path('add_event', views.add_event, name='add-event'),
path('update_event/<event_id>', views.update_event, name='update-event'),
event_list.html:
{% extends 'events/base.html' %}
{% block content %}
<h1>Event </h1>
<br />
{% for event in event_list %}
<div class="card">
<div class="card-header">
{{Event}}
</div>
<div class="card-body">
<h5 class="card-title">Venue: {{ event.venue }}</h5>
<p class="card-text">
<ul>
<li>Date: {{ event.event_date }}</li>
<li>Event Venue: {{ event.venue.web }}</li>
<li>Manager: {{ event.manager }}</li>
<li>Desc: {{ event.description }}</li>
<li>Attendees:<br />
{% for user in event.attendees.all %}
{{ user }}<br/>
{% endfor %}
</li>
</ul>
</p>
</div>
<div class="card-footer text-muted">
Update Event
</div>
</div>
<br /><br />
{% endfor %}
{% endblock %}
You have defined your url in the "urls.py" file with the name of "list_events" (note the underscore). In the views you have used it as "list-events" (note the hyphen).
You can fix that by following one of these options:
1.You can change that in the views (by using underscore instead of hyphen):
return redirect('list_events')
2.Or changing it in the url (by using hyphen instead of underscore):
path('events', views.all_events, name='list-events'),
I'm using this app to register the user of my website https://github.com/egorsmkv/simple-django-login-and-register. The problem is that no metter what I do my form are not visible (they were when I did not use this registration app and the code worked just fine). This is my code:
model
class UserBio(models.Model):
name = models.CharField(max_length=120)
age = models.CharField(max_length=2)
phone = models.CharField(max_length=10)
height = models.CharField(max_length=3)
weight = models.CharField(max_length=3)
form
class UserBio(forms.ModelForm):
class Meta:
model = UserBio
fields = (name', 'age', 'phone', 'height', 'weight')
views
def add_bio(request):
submitted = False
if request.method == "POST":
info_form = UserBio(request.POST)
if info_form.is_valid():
info_form.save()
return HttpResponseRedirect('add_information?submitted=True')
else:
info_form = UserBio()
if 'submitted' in request.GET:
submitted = True
return render(request, 'accounts/profile/add_information.html', {'form': info_form, 'submitted':submitted})
urls
urlpatterns = [
path('add/information', views.add_information, name='add_information'),
]
html
{% extends 'layouts/default/base.html' %}
{% block title %} Add info {% endblock %}
{% load i18n %}
{% block content %}
<h4>{% trans 'Add Info' %}</h4>
{% if submitted %}
Sumitted correctly
{% else %}
<form method="post">
{% csrf_token %}
{{ info_form.as_p }}
</form>
</div>
<br/>
</body>
{% endif %}
{% endblock %}
Any help would be very apprecieted!
because in your views def add_bio change your url acc to your function views
path('add/information', views.add_bio, name='add_information'),
and in you template
{{ form.as_p }}
You passed the info_form variable to the template with variable name form. Indeed:
# name of the variable for the template ↓
return render(request, 'accounts/profile/add_information.html', {'form': info_form, 'submitted':submitted})
This thus means that you render this with:
{{ form.as_p }}
You should also trigger the correct view:
urlpatterns = [
path('add/information/', views.add_bio, name='add_information'),
]
The path does not point to the template: a path points to a view, and the view can (this is not required) render zero, one or multiple templates to create a HTTP response.
I am setting up a simple blog site with Django, and run into this error when trying to link to a page that allows users to edit existing blog posts.
Reverse for 'edit_post' with arguments '('',)' not found. 1 pattern(s) tried: ['edit_post/(?P<title_id>[0-9]+)/$']
If I understand this error correctly, it means that Django can't find a urlpattern that matches the url being requested. To my eyes I have the urlpattern set up correctly, but I still get the error.
The link in question appears on the text.html template, which is the template that displays the text of a particular blog post.
Here is the relevant code:
urls.py
"""Defines URL patterns for blogs."""
from django.urls import path
from . import views
app_name = 'blogs'
urlpatterns = [
# Home page, shows all posts in chronological order.
path('', views.index, name='index'),
# A page to show the text of a specific post.
path('text/<int:title_id>/', views.text, name='text'),
# Page for adding a new post.
path('new_post/', views.new_post, name='new_post'),
# Page for editing a post.
path('edit_post/<int:title_id>/', views.edit_post, name='edit_post'),
]
views.py
from django.shortcuts import render, redirect
from .models import BlogPost
from .forms import BlogPostForm
def index(request):
"""The home page for blogs, shows all posts."""
titles = BlogPost.objects.order_by('date_added')
context = {'titles': titles}
return render(request, 'blogs/index.html', context)
def text(request, title_id):
"""Show a single post title and its text."""
title = BlogPost.objects.get(id=title_id)
text = title.text
context = {'title': title, 'text': text}
return render(request, 'blogs/text.html', context)
def new_post(request):
"""Add a new post."""
if request.method != 'POST':
# No data submitted; create a new form.
form = BlogPostForm()
else:
# POST data submitted; process data.
form = BlogPostForm(data=request.POST)
if form.is_valid():
new_post = form.save(commit=False)
new_post.save()
return redirect('blogs:index')
# Display a blank or invalid form.
context = {'form': form}
return render(request, 'blogs/new_post.html', context)
def edit_post(request, title_id):
"""Edit an existing post."""
post = BlogPost.objects.get(id=title_id)
if request.method != 'POST':
# Initial request: pre-fill form with the current post.
form = BlogPostForm(instance=post)
else:
# Post data submitted; process data.
form = BlogPostForm(instance=post, data=request.POST)
if form.is_valid():
form.save()
return redirect('blogs:index')
context = {'post': post, 'form': form}
return render(request, 'blogs/edit_post.html', context)
index.html (this is the homepage for the blog)
{% extends "blogs/base.html" %}
{% block content %}
<p>Blog is a generic site for a blogger to post content for their audience.</p>
<p>Posts</p>
<ul>
{% for title in titles %}
<li>
{{ title }}
</li>
{% empty %}
<li>No posts have been added yet.</li>
{% endfor %}
</ul>
Create a new post
{% endblock content %}
text.html (this page displays the text content of a particular post, and also the link to edit the post)
{% extends "blogs/base.html" %}
{% block content %}
<p>Title: {{ title }}</p>
<p>{{ text }}</p>
Edit post
{% endblock content %}
edit_post.html (this page should display the existing post and allow it to be edited)
{% extends "blogs/base.html" %}
{% block content %}
<p>Edit post:</p>
<p>Title:</p>
<form action="{% url 'blogs:edit_post' title.id %}" method='post'>
{% csrf_token %}
{{ form.as_p }}
<button name="submit">Save changes</button>
</form>
{% endblock content %}
How the edit_post function in views.py should work (in theory) is to create an instance based upon the post's title id, and then allowing the user to edit it and save changes.
I'm not sure where I'm going wrong here and any suggestions are greatly appreciated.
The name of the post object you pass to the template, is not title, but post:
{% extends "blogs/base.html" %}
{% block content %}
<p>Edit post:</p>
<p>Title:</p>
<form action="{% url 'blogs:edit_post' post.pk %}" method='post'>
{% csrf_token %}
{{ form.as_p }}
<button name="submit">Save changes</button>
</form>
{% endblock content %}
If you use title.id, it will not find that variable, and thus this will be resolved to the empty string. If you use post.id, or post.pk, it will resolve to the .id field, or primary key of the post object.
I'm trying to make a website that lets visitors search for books using another search engine. I have a script that takes a query, and returns some HTML with the results of the search, but I'm struggling to make a front end for this. I am using django because it seemed like the best option when I started, but now I am going in circles and I can't figure out how to make this thing - I'm just getting overwhelmed because the different tutorials and documentation that I'm reading all go into the advanced stuff before I can get the basic thing working.
Do I need separate search and results templates? Right now I'm getting the error The view book_search.views.search didn't return an HttpResponse object. It returned None instead.
How can I fix this error and/or design this whole thing better?
Here's what I have so far (the script that returns the results in html is pull.py):
The views and urls are from inside the book_search app.
views.py:
from django.shortcuts import render
from django.http import HttpResponse
from . import pull
from .forms import SearchForm
def index(request):
return HttpResponse("Welcome to the index page")
def test_search(request):
context = {'query': 'test query'}
return render(request, 'book_search/search.html', context)
def search(request):
if request.method == "GET":
form = SearchForm(request.GET)
if form.is_valid():
query = form.cleaned_data['query']
results = pull.main(query)
context = {'query': query, 'form': form, 'results': results}
return render(request, 'book_search/results.html', context)
apps.py:
from django.apps import AppConfig
class BookSearchConfig(AppConfig):
name = 'book_search'
urls.py:
from django.urls import path
from . import views
urlpatterns = [
path('index', views.index, name='index'),
path('test', views.test_search, name='test_search'),
path('', views.search, name='search'),
]
forms.py:
class SearchForm(forms.Form):
query = forms.CharField(label='Search', max_length=200)
template base.html:
<html>
<head>
</head>
<body>
<form method="GET" action="/search/">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit">
</form>
{% block content %}{% endblock %}
</body>
</html>
template results.html:
{% block content %}
{% results %}
{% endblock content %}
Since we guessed that form isn't valid (because no POST handler - you do not send anything to the form) and wrong indentation gives None response, now you can fix reference before assignment:
def search(request):
if request.method == "GET":
form = SearchForm()
context = {'form': form}
elif request.method == "POST":
form = SearchForm(request.POST)
if form.is_valid():
query = form.cleaned_data['query']
results = pull.main(query)
context = {'query': query, 'form': form, 'results': results}
return render(request, 'book_search/results.html', context)
and render errors in results.html template by putting this:
{% if form.errors %}
{% for field in form %}
{% for error in field.errors %}
<div class="alert alert-danger">
<strong>{{ error|escape }}</strong>
</div>
{% endfor %}
{% endfor %}
{% for error in form.non_field_errors %}
<div class="alert alert-danger">
<strong>{{ error|escape }}</strong>
</div>
{% endfor %}
{% endif %}