Confused by Django 404s for simple pages - python

Im doing my first Django site myself after going through several tutorials and running into some errors that I can't figure out what the problem is as they are simple page requests.
I was trying to make a category detail view e.g. 127.0.0.1:8000/news and Ive followed the same setup as other pages such as the index and post detail which have worked fine but this is giving a 404.
here are my files
models.py
from django.db import models
from django.core.urlresolvers import reverse
class EntryQuerySet(models.QuerySet):
def published(self):
return self.filter(publish=True)
class Blog(models.Model):
title = models.CharField(max_length = 200)
slug = models.SlugField(max_length = 100, db_index = True)
body = models.TextField()
publish = models.BooleanField(default=True)
created = models.DateTimeField(auto_now_add=True)
category = models.ForeignKey('blog.category')
objects = EntryQuerySet.as_manager()
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post_detail', kwargs={'slug':self.slug})
class Meta:
verbose_name = 'Blog entry'
verbose_name_plural = 'Blog entries'
ordering = ['-created']
class Category(models.Model):
cat_title = models.CharField(max_length = 200, db_index = True)
cat_slug = models.SlugField(max_length = 100, db_index = True)
def __str__(self):
return self.cat_title
def get_absolute_url(self):
return reverse('category_detail', kwargs={'cat_slug':self.cat_slug})
class Meta:
verbose_name = 'Category'
verbose_name_plural = 'Categories'
views.py
from django.views import generic
from . import models
class index_view(generic.ListView):
queryset = models.Blog.objects.published()
template_name = 'index.html'
class post_view(generic.DetailView):
model = models.Blog
template_name = 'post_view.html'
class category_view(generic.ListView):
model = models.Category
template_name = 'category_view.html'
class category_detail_view(generic.DetailView):
model = models.Category
template_name = 'category_detail_view.html'
class About_page(generic.DetailView):
template_name = 'about.html'
app urls.py
from django.conf.urls import url
from django.contrib import admin
from . import views
urlpatterns = [
url(r'^$', views.index_view.as_view(), name='index'),
url(r'^categories/$', views.category_view.as_view(), name='category_detail'),
url(r'^(?P<slug>\S+)$', views.post_view.as_view(), name='post_detail'),
url(r'^(?P<cat_slug>\S+)$', views.category_detail_view.as_view(), name='category_detail_view'),
url(r'^about/$', views.About_page.as_view(), name='about'),
this is the category detail page 'category_detail_view.html'
{% extends 'base.html' %}
{% block title %} The category detail view page {% endblock %}
{% block category_detail %}
{% for cat_title in object_list %}
<ul>
<li>{{ category.cat_title }}</li>
{% endfor %}
</ul>
{% endblock %}
and the about page
{% extends 'base.html' %}
<h2>This is the about page</h2>
both of these pages return this error message
Page not found (404)
Request Method: GET
Request URL: http://127.0.0.1:8000/about/
Raised by: blog.views.post_view
No Blog entry found matching the query
I dont understand why 'blog.post_view' is being raised when neither of these templates refer to the post_view.
I have an index page with all published posts listed, a categories page with all categories listed and a post detail page all of which work fine and are almost exactly the same as these views and templates.

When Django resolves the url /about/, it goes through your url patterns in order. It matches the post_detail url pattern, so runs the post_view, treating about as a slug. Since you don't have any posts with the slug about, you get the 404 error.
One solution is to move the about url pattern above the post_detail pattern. You should also change the category url pattern, otherwise it won't work. For example, you could do:
url(r'^about/$', views.About_page.as_view(), name='about'),
url(r'^(?P<slug>\S+)$', views.post_view.as_view(), name='post_detail'),
url(r'^categories/(?P<cat_slug>\S+)$', views.category_detail_view.as_view(), name='category_detail_view'),

Related

Django 3 'NoReverseMatch at /post/1/

My blog has the capacity to make posts. I wanted to have a feature where i can update/edit the blog and when i try to implement the feature i run into the following error;
NoReverseMatch at /post/1/ Reverse for 'post_edit' with arguments
'('',)' not found. 1 pattern(s) tried: ['post/(?P[0-9]+)/edit/$']
I know what line is causing the problem:
/post_detail.html
+Edit Blog Post
without the line on top, i get no errors. I am just a begginer learning Django and i cannot make any sense of why this is not working. It is suggested in the tutorial i am following.
/urls.py
urlpatterns = [
path('post/<int:pk>/edit/', BlogUpdateView.as_view(), name='post_edit'), # new
path('post/new/', BlogCreateView.as_view(), name='post_new'),
path('post/<int:pk>/', BlogDetailView.as_view(), name='post_detail'),
path('', BlogListView.as_view(), name='home'),
]
/post_detail.html
{% extends 'base.html' %}
{% block content %}
<div class="post-entry">
<h2>
{{ my_posts.title }}
</h2>
<p>
{{ my_posts.body }}
</p>
</div>
+Edit Blog Post
{% endblock content %}
views.py
class BlogListView(ListView):
model = Post
template_name = 'home.html'
class BlogDetailView(DeleteView):
model = Post
context_object_name = 'my_posts'
template_name = 'post_detail.html'
class BlogCreateView(CreateView):
model = Post
template_name = 'post_new.html'
fields = '__all__'
class BlogUpdateView(UpdateView):
model = Post
template_name = 'post_edit.html'
fields = ['title', 'body']
/models.py
class Post(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey(
'auth.User',
on_delete=models.CASCADE,
)
body = models.TextField()
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post_detail', args=[str(self.id)])
The name of your context object is: context_object_name = 'my_posts', not 'post'. Therefore the object is my_posts in your template.
The link should thus be:
+Edit Blog Post
Try:
path(r'post/<int:pk>/edit/', BlogUpdateView.as_view(), name='post_edit'), # new
I added an r in front of your url.
We could write model class name "post" in lowercase or "object" with or without context_object_name to make it work.
<a
+ edit post
or
+ edit post
at post_detail.html

Adding UUIDs to my Django app yields a NoReverseMatch error

I have a books app using a UUID with a listview of all books and detailview of individual books. I keep getting the following error message:
NoReverseMatch at /books/
Reverse for 'book_detail' with arguments '('/books/71fcfae7-bf2d-41b0-abc8-c6773930a44c',)' not found. 1 pattern(s) tried: ['books/(?P<pk>[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$']
Here is the models.py file:
# books/models.py
import uuid
from django.db import models
from django.urls import reverse
class Book(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
title = models.CharField(max_length=200)
author = models.CharField(max_length=200)
price = models.DecimalField(max_digits=6, decimal_places=2)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('book_detail', args=[str(self.id)])
The urls.py file where I'm using to convert the id from the model to a uuid.
# books/urls.py
from django.urls import path
from .views import BookListView, BookDetailView
urlpatterns = [
path('', BookListView.as_view(), name='book_list'),
path('<uuid:pk>', BookDetailView.as_view(), name='book_detail'),
]
The top-level urls.py file looks like this and adds a books/ route.
# urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('books/', include('books.urls')),
The views.py file:
from django.views.generic import ListView, DetailView
from .models import Book
class BookListView(ListView):
model = Book
context_object_name = 'book_list'
template_name = 'books/book_list.html'
class BookDetailView(DetailView):
model = Book
context_object_name = 'book'
template_name = 'books/book_detail.html'
And the relevant templates file.
<!-- templates/book_detail.html -->
{% extends '_base.html' %}
{% block content %}
{% for book in book_list %}
<div>
<h2>{{ book.title }}</h2>
</div>
{% endfor %}
{% endblock content %}
I believe I'm implementing this correctly but the URL is not liking my UUID. What is amiss?
The problem is not "adding uuid". The problem is that you are doing the URL reversal twice: once in get_absolute_url and once in the {% url %} tag. Use one or the other, not both.
Either:
{{ book.title }}
Or:
{{ book.title }}

Django get_absolute_url in inclusion_tag Issue in Template

The error I'm getting is:
NoReverseMatch at / Reverse for 'view_page' not found. 'view_page' is
not a valid view function or pattern name.
Basically I have a template tag for my app to display the navigation menu on each page, which is dynamic so I can't hard-code the links. I've written an inclusion tag and template:
from django import template
from ..models import Page
register = template.Library()
#register.inclusion_tag("tags/page_links.html")
def page_links():
all_pages = Page.objects.all()
return {'pages': all_pages}
And the template tag html in the templates/tags directory:
<ul>
{% for page in pages %}
<li>{{ page.link_name }}</li>
{% endfor %}
</ul>
Each page object has an #permalink get_absolute_url() function to get the link. This works fine in other parts of the site but this inclusion tag does not. I'm using it like so in my base.html file (so I don't have to add it to each page):
{% load static %}
{% load page_tags %}
...
<p><b>Navigation:</b></p>
{% page_links %}
...
But it appears that the pages are having trouble using the view_page view (which otherwise works) in the template tag. What am I missing here?
EDIT: Added URLs and Model code
urls.py:
from django.contrib import admin
from django.urls import path, include
from django.conf.urls import url
from .views import index, view_page
urlpatterns = [
path('admin/', admin.site.urls),
path('blog/', include('blog.urls')),
url(r'^$', index),
url(r'^(?P<slug>[^\.]+).html', view_page),
]
And the Model:
class Page(SortableMixin):
title = models.CharField(max_length=80, unique=True)
slug = models.SlugField(max_length=80, unique=True)
link_name = models.CharField(max_length=40, unique=True)
passthrough_page = models.BooleanField(default=False)
passthrough_link = models.CharField(max_length=40, default=None, null=True)
content = models.TextField(default=None, null=True)
class Meta:
ordering = ['the_order']
the_order = models.PositiveIntegerField(default=0, editable=False, db_index=True)
#permalink
def get_absolute_url(self):
if not self.passthrough_page:
return 'view_page', None, {'slug': self.slug}
else:
return self.passthrough_link
A change from #permalink (which I was informed has been deprecated) to calling reverse directly seems to have done the trick:
In the model code:
def get_absolute_url(self):
if not self.passthrough_page:
return reverse('view_page', kwargs={'slug': self.slug})
else:
return self.passthrough_link

How to access pk in views (Django)

I'm writing a small chat programm in Django but have problems getting any further.
Here's the code:
models.py
from django.db import models
from datetime import datetime
from django.utils import timezone
class Chat(models.Model):
chatname = models.CharField(max_length=100)
description = models.TextField()
created_at = models.DateTimeField(default=datetime.now, blank=True)
def __str__(self):
return self.chatname
class Comment(models.Model):
chat = models.ForeignKey(Chat, on_delete=models.CASCADE)
commenter = models.CharField(max_length=30)
comment = models.TextField()
created_at = models.DateTimeField(default=datetime.now, blank=True)
def __str__(self):
return self.comment
urls.py
from django.conf.urls import url
from . import views
from django.views.generic import ListView
from chat.views import CommentList
app_name = 'chats'
urlpatterns = [
url(r'^$', views.index, name="index"),
url(r'^comments/(?P<pk>[0-9]+)/$', views.CommentList.as_view(), name='comments'),
]
views.py
from django.shortcuts import render, redirect
from django.http import HttpResponse
from django.contrib.auth import authenticate, login
from django.views import generic
from .models import Chat, Comment
def index(request):
username = None
if request.user.is_authenticated():
username = request.user.username
chats = Chat.objects.all()[:10]
context = {
'chats':chats
}
return render(request, 'chat/index.html', context)
class CommentList(generic.ListView):
queryset = Comment.objects.filter(chat_id=1)
context_object_name = 'comments'
My comment_list.html
{% extends "chat/base.html" %}
{% block content %}
Go back
<h3>Comments</h3>
<h2>{{chat.id}}</h2>
<ul>
{% for comment in comments %}
<li>{{ comment.commenter }}: {{ comment.comment }}</li>
{% endfor %}
</ul>
{% endblock %}
My database structure contains these two models: Chat and Comment. Each chat(room) is supposed to have its own comments. I used 'models.ForeignKey' to be able to filter the comments for each chat(room). In my index.html I list all the chats and each of these has a hyperlink to the /comments/ section.
In my views.py I have this line: 'queryset = Comment.objects.filter(chat_id=1)'
Chat_id is the column in the comments sql table and as it is now it will only show comments that belong to the chat with pk=1. How can I auto access the chat for the different urls /comments/1/ /comments/2/ and so on..?
Hope the explanation is clear. Sorry beginner here, I can try to explain further if it doesn't make a lot of sense.
Best,
Fabian
You should define the get_queryset method instead of the standalone queryset attribute.
def get_queryset(self, *args, **kwargs):
return Comment.objects.filter(chat_id=self.kwargs['pk'])
Instead of CommentList you can use plain view:
def comments_index(request, chatid):
return render(request, 'xxx/comment_list.html', {
'comments': Comment.objects.filter(chat_id=chatid)
})
And in urls:
url(r'^comments/(?P<chatid>[0-9]+)/$', views.comments_index, name='comments'),

Django Many-to-Many Accessing both Models

I'm looking to build a small 'Twitter style' site using Django to get to grips with things and have decided to try and allow multiple users edit each post (eventually based on permissions). Now what I'm struggling with is accessing each user's posts. Below is the code for my model, view and template which shows "There aint no post here" for all users. I'm looking to be able to show all posts that the user has and don't seem to be getting anywhere:
models.py
from django.db import models
class User(models.Model):
username = models.CharField(max_length = 200)
email = models.EmailField(max_length = 75)
password = models.CharField(max_length = 64)
created_date = models.DateTimeField('date created')
def __unicode__(self):
return self.username
class Meta:
ordering = ('created_date',)
class Post(models.Model):
users = models.ManyToManyField(User)
title = models.CharField(max_length = 300)
post = models.TextField()
posted_date = models.DateTimeField('date created')
votes = models.IntegerField()
def __unicode__(self):
return self.title
class Meta:
ordering = ('posted_date',)
views.py
from django.shortcuts import render, get_object_or_404
from django.http import HttpResponse
from users.models import User, Post
def index(request):
latest_user_list = User.objects.order_by('username')[:5]
context = {'latest_user_list': latest_user_list}
return render(request, 'users/index.html', context)
def detail(request, user_id):
user = get_object_or_404(User, pk=user_id)
post_list = Post.objects.filter(id == user.id)
return render(request, 'users/detail.html', {'user': user, 'post': post_list})
urls.py
from django.conf.urls import patterns, url
from users import views
urlpatterns = patterns('',
url(r'^$', views.index, name='index'),
url(r'^(?P<user_id>\d+)/$', views.detail, name='detail'),
)
(template) - detail.html
<h1>{{ user.username }}</h1>
{% if post_list %}
<ul>
{% for post in post_list%}
<li>{{ post.title }}</li>
{% endfor %}
</ul>
{% else %}
<p> There aint no posts here </p>
{% endif %}
The variable you're passing to the template is called post not post_list.
Change the name for the list object in your view.
def detail(request, user_id):
user = get_object_or_404(User, pk=user_id)
post_list = Post.objects.filter(id == user.id)
return render(request, 'users/detail.html', {'user': user, 'post_list': post_list})

Categories