No Reverse Match Why? - django

I get this error whenever i try to open my localhost:8000 which happens to be the homepage of the app i'm working on.
NoReverseMatch at /
Reverse for 'category_detail' with arguments '()' and keyword arguments '{}' not found. 1 pattern(s) tried: ['category/(?P<slug>[\\-\\w]+)/$']
Below are the {% url %} use in template, my models.py, my urls.py and views.py
What is happening? What am I doing wrong?
#in Template.
View
#my urls.py
from django.conf.urls import patterns, url
from . import views
urlpatterns = patterns('',
url(r'^$', views.home.as_view(), name='home'),
url(r'^categories/$', views.MyCategory.as_view(), name='all_categories'),
url(r'^category/(?P<slug>[-\w]+)/$', views.CategoryDetail.as_view(), name='category_detail'),
url(r'^tip/(?P<slug>[-\w]+)/$', views.ToolkitDetail.as_view(), name='toolkit_detail'),
)
#my models.py
from django.db import models
from django.core.urlresolvers import reverse
class Category(models.Model):
slug = models.SlugField()
title = models.CharField(max_length=250)
heading = models.CharField(max_length=750)
description = models.TextField()
link = models.URLField()
def get_absolute_url(self):
return reverse('category_detail', kwargs={'slug': self.slug})
#my views.py
class MyCategory(ListView):
model = Category
context_object_name = 'category'
template_name = 'category.html'
class CategoryDetail(DetailView):
slug_field = 'slug'
model = Category
slug_url_kwarg = 'slug'
context_object_name = 'category'
template_name = 'category_detail.html'
The above is the summation of my problem. Please any assistance is appreciated. I'm sure its clear enough my problem, but since StackOverflow won't let me post without adding more text, I'm doing so. I think the excerpt above is self explanatory as to my problem. I think I'm released to post now.

Your url name 'category_detail' has compulsory parameter - slug. So in template you could write:
View
As your model have special method for generating url you can do it simple:
{{ category.get_absolute_url }}

Your template contains {% url 'category_detail' %}, i.e. tries to reverse "category_detail" url pattern, without giving the required slug keyword argument. This might work:
{% url 'category_detail' slug="foo" %}

Related

Django can't find my DetailView with Primary Key

I'm trying to make a simple page with Django where posts are being displayed using primary keys. However, the DetailView page with my posts can't be displayed.
This is my model:
class Post(models.Model):
author = models.CharField(max_length=100)
title = models.CharField(max_length=100)
posted_date = timezone.now()
text = models.TextField()
def get_absolute_url(self):
return reverse("postdetail", kwargs={"pk": self.pk})
def __str__(self):
return self.title
and here is my url pattern list:
urlpatterns = [
url(r'^posts/(?P<pk>\d+)$', views.PostDetail.as_view(), name="postdetail"),
url(r'^$', views.index, name="index"),
url(r'^thanks/$', views.Thanks.as_view(), name="thanks"),
url(r'^postlist/$', views.PostList.as_view(), name="postlist"),
]
The template is in my templates folder under the name "post_detail.html".
Here is the DetailView:
class PostDetail(DetailView):
context_object_name = 'post_details'
model = Post
I can't see where I might have a mistake. I have a ListView that shows my posts including the primary keys. But when I try to open the URL http://127.0.0.1:8000/posts/1/
I get:
`Page not found (404)
Request Method: GET
Request URL: http://127.0.0.1:8000/posts/1/
Using the URLconf defined in myproj.urls, Django tried these URL patterns, in this order:
admin/
^posts/(?P<pk>\d+)$ [name='postdetail']
^$ [name='index']
^thanks/$ [name='thanks']
^postlist/$ [name='postlist']
The current path, posts/1/, didn’t match any of these.
Do you have a clue why the URL cannot be found? Thank you very much.`
Instead of url() use path()
from django.urls import path
urlpatterns = [
path('', views.index, name = "index"),
path('posts/<int:pk>', views.PostDetail.as_view(), name = "postdetail"),
path('postlist/', views.PostList.as_view(), name="postlist"),
path('thanks', views.Thanks.as_view(), name = "thanks"),
]
NB: url() been deprecated since version 3.1 and removed in 4.0
You are trying to access : http://127.0.0.1:8000/posts/1/ when you define the following http://127.0.0.1:8000/posts/1 without the backlash.
Try : http://127.0.0.1:8000/posts/1
First Things Is Your Views. You can send your database data with return it to the HTML.
views.py
def post(request,id):
post = Post.objects.get(id=id)
return render(request,'blog/index.html',{'post':post})
and before sending data into the HTML you should get the post id in your urls.py
urls.py
path('post/<int:id>', views.post, name='post.blog')
then in your HTML you can handle that data comes from your views.py
index.html
<div class="post-content content">{{ post.body|safe }}</div>

Django bug report, different foreign key as URL parameter will cause detailed view not executing

Edit3:
the culprit is re_path, not foreign key
Original:
This bug is so subtle I couldn't really find a niche way to describe it, For example I have two apps, News and Blogs
In blogs.py model I have something like this:
class BlogType(models.Model):
blog_type = CharField(max_length=20)
def __str__(self):
return self.blog_type
class Blogs(models.Model):
title = models.CharField(max_length=20)
blog_author = models.ForeignKey(User, on_delete=models.CASCADE)
blog_type = models.ForeignKey(BlogType, on_delete=models.DO_NOTHING)
here a blog_type is a foreignkey defined inside the same models.py
In blogs url.py
from django.urls import path, re_path
from . import views
from .views import blogspage
urlpatterns = [
path('', views.blogs, name='blogs'),
re_path(r'(?P<blog_type>[\w-]+)/(?P<pk>[0-9]+)$', blogspage.as_view(), name='blogspage'),
]
Here using the forignkey as a url parameter
And in blogs views.py
class blogspage(DetailView):
model=Blogs
template_name='blogs/blogspage.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
print('Print the object if this is executed', self.get_object())
In Django template you would pass something like:
<div>{{b.title}}</div>
Now the news model.py you have:
class NewsType(models.Model):
news_type = CharField(max_length=20)
def __str__(self):
return self.news_type
class News(models.Model):
title = models.CharField(max_length=20)
news_author = models.ForeignKey(User, on_delete=models.CASCADE)
news_type = models.ForeignKey(NewsType, on_delete=models.DO_NOTHING)
The news views, news urls, news templates, are the exactly the same as blog except the name and template name, basically replacing every single "blogs" to "news"
Then here is where the bug would occur, Only one detailed view will ever execute, for example when someone clicks "{%url "blogspage" blog.blog_type blog.id%}" It will go to url blog/blog_type/pk but the content will be news, However, If <foreignkey> blog_type is removed from url parameter, Only then the detailed view of blog will execute and the correct blog content will be rendered.
news url.py:
urlpatterns = [
path('', views.news, name='news'),
re_path(r'(?P<news_type>[\w-]+)/(?P<pk>[0-9]+)$', newspage.as_view(), name='newspage'),
]
root url.py
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('news.urls')),
path('blogs/',include('blogs.urls')),
] # path('news/', include('news.urls')), will not conflit
# switch the position between blogs.urls and news.urls also no conflit
Edit: www.example.com/blogs/blog_type/1 and www.example.com/news/news_type/1 will not conflit but www.example.com/blogs/blog_type/1 and www.example.com/news_type/1 will
Edit2: Because "" and "/blogs" bugged I automatically assume "/blogs" and "/news" will also bug, But the bug only exist between "" and "/blogs"
It looks like an issue with Django path and re_path method, where as per your scenario the re_path for both blogs and news URLs for newspage and blogpage are matching and django is not able to resolve the correct URL and redirect on the first matching URL.

How to redirect an UpdateView upon success?

I created a small Django application to manage data that fits a simple a model. For now I only need two views: one to list all records and another to edit a record with a generic form. Everything functions as expected, except the redirection from the edit view upon a successful update. In urls.py are the following contents:
from django.urls import path
from . import views
app_name = 'reqs'
urlpatterns = [
path('', views.IndexView.as_view(), name='index'),
path('<int:pk>/', views.ReqUpdateView.as_view(), name='update'),
]
In forms.py:
from django.forms import ModelForm
from .models import Requirement
class RequirementForm(ModelForm):
class Meta:
model = Requirement
fields = ['name', 'priority', 'source' , 'rationale']
And the templeate requirement_form.html:
<h1>{{ requirement.id }} - {{ requirement.name }}</h1>
<form method="post" novalidate>
{% csrf_token %}
<table>
{{ form.as_table }}
<tr><td></td><td><button type="submit">Save</button></td></tr>
</table>
</form>
{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
<br><br>
Back to list
Finally views.py, on a first attempt to redirect the update to the list:
from django.views.generic import ListView, UpdateView
from django.urls import reverse_lazy
from .models import Requirement
from .forms import RequirementForm
class IndexView(ListView):
template_name = 'reqs/index.html'
context_object_name = 'requirements_list'
def get_queryset(self):
return Requirement.objects.order_by('subject')
class ReqUpdateView(UpdateView):
model = Requirement
form_class = RequirementForm
success_url = reverse_lazy('/')
With this formulation the Save button produces this error:
Reverse for '/' not found. '/' is not a valid view function or pattern name.
I also tried an empty string as argument to reverse_lazy, as well as the path name index, but a similar error message is produced.
On a second attempt I tried to redirect to the same page, redefining the get_success_url method to do nothing:
class ReqUpdateView(UpdateView):
model = Requirement
form_class = RequirementForm
context_object_name = 'requirement_update'
def get_success_url(self):
pass #return the appropriate success url
This returns a 404 error trying to redirect the browser to /reqs/1/None.
A third attempt to redirect to the form with the same record:
class ReqUpdateView(UpdateView):
model = Requirement
form_class = RequirementForm
context_object_name = 'requirement_update'
def get_success_url(self):
pk = self.kwargs["pk"]
return reverse("update", kwargs={"pk": pk})
Which complains about not finding the view:
Reverse for 'update' not found. 'update' is not a valid view function or pattern name.
How can I redirect success to a valid URL? It can either be the items list or the item update view, as long as it works.
There are few misconception that you did
reverse parameter should be as documented
URL pattern name or the callable view object
You have set namespace but you are not reversing with namespace as documented
So in your case
def get_success_url(self):
pk = self.kwargs["pk"]
return reverse("reqs:update", kwargs={"pk": pk})
reverse / reverse_lazy are used to get the url using view name or pattern name. If you want to use a url directly just write:
success_url = '/'
For the case of return reverse("update", kwargs={"pk": pk}) not working since you set app_name = 'reqs' you should be using return reverse("reqs:update", kwargs={"pk": pk}) instead.

Django Class Based View CreateView url resolution

I am fairly new to CBV and am trying to make sense of it. I copied the following example from the django doc page:
https://docs.djangoproject.com/en/dev/topics/class-based-views/generic-editing/
models.py
from django.core.urlresolvers import reverse
from django.db import models
class Author(models.Model):
name = models.CharField(max_length=200)
def get_absolute_url(self):
return reverse('author-detail', kwargs={'pk': self.pk})
views.py
from django.views.generic.edit import CreateView, UpdateView, DeleteView
from django.core.urlresolvers import reverse_lazy
from myapp.models import Author
class AuthorCreate(CreateView):
model = Author
fields = ['name']
class AuthorUpdate(UpdateView):
model = Author
fields = ['name']
class AuthorDelete(DeleteView):
model = Author
success_url = reverse_lazy('author-list')
urls.py
from django.conf.urls import patterns, url
from myapp.views import AuthorCreate, AuthorUpdate, AuthorDelete
urlpatterns = patterns('',
# ...
url(r'author/add/$', AuthorCreate.as_view(), name='author_add'),
url(r'author/(?P<pk>\d+)/$', AuthorUpdate.as_view(), name='author_update'),
url(r'author/(?P<pk>\d+)/delete/$', AuthorDelete.as_view(), name='author_delete'),
)
At author/add/ I indeed get the form, but when I enter the string I get the following error:
Reverse for 'author-detail' with arguments '()' and keyword arguments '{'pk': 3}' not found.
It seems like the new entry has been added to the database, but it could not resolve the URL for the next view?
So I am puzzled, what is this get_absolute_url() object's method supposed to do, how does it work (I could not grasp it from the django doc) and how do I fix the issue?
Thanks.
EDIT 1: added the template:
author_form.html:
<form action="" method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Create" />
</form>
By default, when a new model is created django will redirect you to a models absolute url as returned by the get_absolute_url method. In your example you would need to add a url with the name author-detail that accepts a pk keyword argument.
urlpatterns = patterns('',
# ...
url(r'author/(?P<pk>\d+)/$', AuthorDetail.as_view(), name='author-detail'),
)
Note the name of the url matches the name of the view in the get_absolute_url method.
Use it in your templates:
{% for author in authors %}
{{author.name}}
{% endfor %}

Django custom tag no longer works

I made a custom tag to display a list of categories and its url which worked until I made a category detail view which will only display articles based on the category.
Here is the category view:
from blog.models import Category, Entry
from django.shortcuts import render_to_response, get_object_or_404
from django.views.generic import list_detail
#for template tag to display all categories
def all_categories(request):
return render_to_response('category_detail.html', {
'categories': Category.objects.all()
})
def category_detail(request, slug):
"""
object_list
List of posts specific to the given category.
category
Given category.
"""
return list_detail.object_list(
request,
queryset = Entry.objects.filter(categories = Category.objects.filter(slug = slug)),
extra_context = {'category': Category.objects.filter(slug = slug)},
template_name = 'blog/category_detail.html',
)
Categories url:
from django.conf.urls.defaults import *
from django.views.generic.list_detail import object_list, object_detail
from blog.views.category import category_detail
from blog.models import Category, Entry
# for category detail, include all entries that belong to the category
category_info = {
'queryset' : Category.objects.all(),
'template_object_name' : 'category',
'extra_context' : { 'entry_list' : Entry.objects.all }
}
urlpatterns = patterns('',
url(r'^$', 'django.views.generic.list_detail.object_list', {'queryset': Category.objects.all() }, 'blog_category_list'),
url(r'^(?P<slug>[-\w]+)/$', category_detail),
)
and the custom category tag:
from django import template
from blog.models import Category
def do_all_categories(parser, token):
return AllCategoriesNode()
class AllCategoriesNode(template.Node):
def render(self, context):
context['all_categories'] = Category.objects.all()
return ''
register = template.Library()
register.tag('get_all_categories', do_all_categories)
Also here is how i am using the custom tag in base.html:
{% load blog_tags %}
<p>
{% get_all_categories %}
<ul>
{% for cat in all_categories %}
<li>{{ cat.title }}</li>
{% endfor %}
</ul>
</p>
Before I added category_detail in the view, the custom tag would display the url correctly like: /categories/news. However, now all of the links from the custom tag just displays the url or the current page your on. Whats weird is that it displays the category name correctly.
Does anyone know how to get the urls to work again?
EDIT
here is my category model, maybe something is wrong with my get_absolute_url():
import datetime
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
class Category(models.Model):
title = models.CharField(max_length = 100)
slug = models.SlugField(unique = True)
class Meta:
ordering = ['title']
verbose_name_plural = "Categories"
def __unicode__(self):
return self.title
#models.permalink
def get_absolute_url(self):
return ('category_detail', (), {'slug': self.slug })
EDIT: updated get_absolute_url for Category model, however, that did not fix my problem. Also if i wasnt clear earlier, the category url worked even before changing the get_absolute_url
I am going to bet that get_absolute_url is actually returning an empty string. This would make the links reload the current page. Check out your HTML and look for something like this:
Category Title Example
If the URLs are actually blank, there is probably an error in get_absolute_url. When a Django template encounters an error when outputting a template variable it returns an empty string. Try calling get_absolute_url from a Django shell and see if it returns properly:
Category.objects.all()[0].get_absolute_url()
It looks like you renamed your view from blog_category_detail to category_detail, but forgot to update the reference in get_absolute_url.
Update: It doesn't look like the reverse URL lookup for 'category_detail' would succeed. Your urls file does not name the category_detail URL. You should either change the get_absolute_url reference to app_name.views.category_detail (or wherever it is stored) or name the URL by replacing that last line in your urls file with:
url(r'^(?P<slug>[-\w]+)/$', category_detail, name='category_detail'),
To find the source of the problem, you should debug this code from a command line. Something like this should do:
$ python manage.py shell
>>> from blog.mobels import Category
>>> cat = Category.objects.all()[0]
>>> cat.get_absolute_url()