NoReverseMatch at /category/clothes/ Django class based views - django

I have a django project that is close to an e-commerce wesite in terms of functionality.
There are four pages linked to one another. First page displays Categories, 2nd Subcategories, 3rd Product list and 4th Product detail and I'm using slugs to navigate.
ERROR
Reverse for 'product-list' with arguments '('', 'women-clothes')' and keyword arguments '{}' not found. 1 pattern(s) tried: ['category/(?P<category_slug>[-\\w]+)/(?P<subcategory_slug>[-\\w]+)/$']
Category to Subcategory linking code on the category_list.html is {{ category.name }} and on the views.py
class CategoryListView(ListView):
models = Category
template_name = 'products/category_list.html'
context_object_name = "Category list"
def get_queryset(self):
"""
Returns all categories.
"""
return Category.objects.get_queryset().all()
and urls.py
app_name = 'products'
urlpatterns = [
url(r'^$', CategoryListView.as_view(), name='categories'),
url(r'^(?P<category_slug>[-\w]+)/$', SubcategoryListView.as_view(), name='sub-category'),
url(r'^(?P<category_slug>[-\w]+)/(?P<subcategory_slug>[-\w]+)/$', ProductListView.as_view(), name='product-list'),
url(r'^(?P<category_slug>[-\w]+)/(?P<subcategory_slug>[-\w]+)/(?P<pk>\d+)/$', ProductDetailView.as_view(), name='product-detail'),]
The problem is linking subcategory_list.html to product_list. Since I need a category_slug and subcategory_slug to be pass to
{{ object.name }}.
I don't know how to implement this logic to using cbv. I want to pass category_slug since it is from a Category model and querying from Subcategory model.
views.py
class SubcategoryListView(ListView):
"""
Browse all products in the sub-catalogue.
"""
model = Subcategory
template_name = 'products/subcategory_list.html'
context_object_name = "Sub-Category list"
category_model = Category
def get_queryset(self):
"""
Returns all sub-categories.
"""
self.category = get_object_or_404(Category, category_slug = self.kwargs.get('category_slug'))
return Subcategory.objects.filter(category = self.category)
category.html which works.
{% for category in object_list %}
<div class="col-xs-12 col-md-12">
{{ category.name }}
<p>{{ category.category_slug }}</p>
</div>
{% endfor %}
subcategory.html
{% for object in object_list %}
<div class="col-xs-12 col-md-12">
{{ object.name }}
<p>subcategory_slug:{{ object.subcategory_slug }}</p>
</div>
{% endfor %}
How can get category_slug and pass it in the above view so as I can iterate on them on the template?

I don't really see what this has to do with CBVs. You haven't shown much of your template, but presumably you are iterating over subcategories and want to link to the individual list page for that subcategory. So, you just need to pass the slug and the category slug for the current subcategory in your loop.
This would be easier if you showed the rest of your template and your models, but assuming object is the subcategory, with a field called "subcategory_slug`, and the SubCategory model has an FK to Category:
{{ object.name }}

I was able to solve this error.
Changes
views.py
class SubcategoryListView(ListView):
"""
Browse all products in the sub-catalogue.
"""
model = Subcategory
template_name = 'products/subcategory_list.html'
context_object_name = "Sub-Category list"
category_model = Category
def get_queryset(self):
"""
Returns all sub-categories.
"""
self.category = get_object_or_404(Category, category_slug = self.kwargs.get('category_slug'))
return Subcategory.objects.filter(category = self.category)
def get_context_data(self, **kwargs):
"""
Returns self.category_slug needed
on the subcategory_list.html as a
one of the {% url %} slug params.
"""
context = super(SubcategoryListView, self).get_context_data(**kwargs)
context['categories'] = Category.objects.all()
context['category_slug'] = self.kwargs.get('category_slug')
return context
On the subcategory_list.html i changed object.category_slug to category_slug.
subcategory_list.html
{{ object.name }} .

Related

How to make a filter in Django Listview queryset

I have a list of brands. Each brand has a list of categories
I want to make each brand a link. And when the user click the brand he passes to the page with all the categories related to that brand
Here is a template with brands:
<ul>
{% for brand in object_list %}
<li>{{ brand.name }}</li>
{% endfor %}
</ul>
And here is the view:
class CategoryListView(ListView):
model = Category
queryset = Category.objects.filter(brand=pk)
template_name = 'category_list.html'
But it gives an error. Can someone help me?
Thank you
Override get_queryset method:
class CategoryListView(ListView):
model = Category
template_name = 'category_list.html'
def get_queryset(self, **kwargs):
qs = super().get_queryset(**kwargs)
return qs.filter(brand_id=self.kwargs['pk'])
And the url should look like this:
path('category/<int:pk>/', CategoryListView.as_view())

django class based view pass url parameter in create post to current category

lets's say that i have three categories (tutorials, news, jobs).
and i have class based views to list all posts, list posts by category and create new posts.
and sure post is the same model and fields to all categories.
my problem is :
if user was in category list template (let's say tutorial) .. i want the user when he create new post .. it is saved directly to tutorial category .. and if user was in list template (let's say news) .. he will create new post which will be saved directly to news category.
i mean create new post saved directly to current category.
i believe i will use (pass url parameter to class based views) but actually i failed to do that .. and i searched tonnage of questions without got what i want.
can any body help .. with sample please.
models.py
class Category(models.Model):
name = models.CharField(max_length=50)
slug = models.SlugField(max_length=50, unique=True)
def save(self, *args, **kwargs):
if not self.slug and self.name:
self.slug = slugify(self.name)
super(Category, self).save(*args, **kwargs)
class Post(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
category = models.ForeignKey(Category, on_delete=models.CASCADE, null=True)
author = models.ForeignKey(User, on_delete=models.CASCADE)
views.py
def PostListView(request, category_slug=None):
category = None
posts = Post.objects.all().prefetch_related().annotate(commentscountperpost=Count('comments'))
categories = Category.objects.prefetch_related().annotate(total_product_category=Count('post'))
if category_slug:
category = Category.objects.get(slug=category_slug)
posts = posts.filter(category=category)
context = {
'title': 'Home Page',
'posts': posts,
'total_posts': total_posts,
'categories': categories,
'category': category,}
return render(request, 'blog/index.html', context)
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
template_name = 'blog/new_post.html'
form_class = PostCreateForm
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
forms.py
class PostCreateForm(forms.ModelForm):
title = forms.CharField(label='Title')
content = forms.CharField(label='Content', widget=forms.Textarea)
class Meta:
model = Post
fields = ['title', 'content']
urls.py
path('index_list/', PostListView, name='list'),
path('<slug:category_slug>', PostListView, name='post_category_list'),
path('new_post/', PostCreateView.as_view(), name='new_post'),
new_post.html template
{% extends 'base.html' %}
{% block content %}
{% load crispy_forms_tags %}
<div class="border p-4 mb-5">
<legend class="border-bottom pb-1 mb-3">New Post </legend>
<form method="POST">
{% csrf_token %}
{{form|crispy}}
<input class="btn btn-secondary mt-4" type="submit" value="Add New Post">
</form>
</div>
{% endblock content %}
list.html template
{% for category in categories %}
<h5><a class="text-primary" href="{% url 'post_category_list' category.slug %}">
{{ category.name }} ({{ category.total_product_category }})</a></h5>
{% endfor %}
<h5>{{ total_posts }} Total Posts </h5>
{% if category %}
New {{ category }}
{% endif %}
You can try like this:
class PostCreateView(generic.CreateView):
model = Post
template_name = 'blog/new_post.html'
form_class = CreatePostForm
slug_url_kwarg = 'slug'
def form_valid(self, form):
category = Category.objects.get(slug=self.kwargs['slug'])
form.instance.category = category
form.instance.author = self.request.user
return super(PostCreateView, self).form_valid(form)
And in the urls
path('new_post/<slug>/', PostCreateView.as_view(), name='new_post'),
yes i found it , depending on arjun answer, greate thanks for arjun
in list posts per category template change :
New {{ category }}
to:
New {{ category }}
and it works fine,
thanks.

View didn't return an HttpResponse

My project is here github Project
I'm getting this error.
ValueError at /salesapp/add/ashton-aged-maduro/
The view salesapp.views.add_CartItem didn't return an HttpResponse object. It returned None instead.
I get this error when I click the 'Add to Cart' button on my singleproduct.html template which calls the ProductAddToCart form. The view is add_CartItem.
I also get the error Field is required when I don't initially set the form values. I'm just stuck now.
This is models.py
class Product(models.Model):
itemid = models.CharField(max_length=128, unique=True)
itemname = models.CharField(max_length=128)
brand = models.CharField(max_length=128)
image = models.ImageField(upload_to='static/images/')
notes = models.CharField(max_length=250)
price = models.IntegerField()
slug = models.SlugField(unique=True)
def save(self, *args, **kwargs):
self.slug = slugify(self.itemname)
super(Product, self).save(*args, **kwargs)
def __str__(self):
return self.itemname
class CartItem(models.Model):
cart_id = models.CharField(max_length=50)
date_added = models.DateTimeField(auto_now_add=True)
quantity = models.IntegerField(default=1)
itemid = models.ForeignKey('Product', unique=False)
class Meta:
db_table = 'cart_items'
ordering = ['date_added']
def name(self):
return self.product.name
My forms.py
class ProductAddToCartForm(forms.ModelForm):
cart_id = forms.CharField(max_length=50)
date_added = forms.DateTimeField()
quantity = forms.IntegerField()
slug = forms.CharField(widget=forms.HiddenInput(), required=False)
#itemid = models.ForeignKey('Product', unique=False)
class Meta:
model = CartItem
fields = ('cart_id', 'date_added', 'quantity', 'slug', 'itemid', )
My views.py
def add_CartItem(request, product_name_slug):
print('In add_CartItem --------------------')
form = ProductAddToCartForm(request.POST)
p = Product.objects.get(slug=product_name_slug)
form = ProductAddToCartForm(initial={'cart_id': 123, 'date_added':date.date.today(), 'quantity': 1, 'slug':p.slug, 'id':p.id, 'itemid':p.itemid})
form.save(commit=False)
print(form)
print(p.slug)
print(p.id)
print(p.itemid)
if form.is_valid():
print(p)
print('In form.is_valid()--------------------------------')
ci = CartItem.objects.create(cart_id=1, date_added=date.date.today(), quantity=1, itemid=p)
form.save(commit=True)
return index(request)
else:
print(form.errors) #return render(request, 'salesapp/errors.html', {'form': form})
My urls.py
from django.conf.urls import url
from salesapp import views
urlpatterns = [
url(r'^$', views.index, name='index'),
url(r'about/', views.about, name='about'),
url(r'customers/', views.customers, name='customers'),
url(r'products/', views.products, name='products'),
url(r'^add_product/$', views.add_product, name='add_product'),
url(r'^add_customer/$', views.add_customer, name='add_customer'),
url(r'items/(?P<product_name_slug>[\w\-]+)/$', views.show_product, name='show_product'),
url(r'^add/(?P<product_name_slug>[\w\-]+)/$', views.add_CartItem, name='add_CartItem'),
#url(r'^cart/$', views.show_cart, name='show_cart'),
#url(r'^register/$', views.register, name='register'),
#url(r'^login/$', views.user_login, name='login'),
#url(r'^logout/$', views.user_logout, name='logout'),
#url(r'^restricted/', views.restricted, name='restricted'),
]
and my template where I want to display the ProductAddToCartForm but add a product to the CartItem table.
<!DOCTYPE html>
{% extends 'salesapp/base.html' %}
{% load staticfiles %}
{% block title_block %}
{{ product.itemname }}
{% endblock %}
{% block body_block %}
<div>
<div>
<ul style="list-style:none; text-align:center;">
<li style="float:left; width:25%; margin:20px;">
<img src="/{{ product.image }}"/>
<div>
<b>{{ product.itemname }}</b><br/>
Price per cigar:<br/>
<b>${{ product.price }}</b>
<p>{{ product.notes }}</p>
</div>
<form method="post" action="/salesapp/add/{{ product.slug }}/" class="cart">
{% csrf_token %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% for field in form.visible_fields %}
{{ field.errors }}
{{ field.help_text }}
{{ field }}
{% endfor %}
<br />
<input type="submit" value="Add To Cart" name="submit" alt="Add To Cart" />
</form>
<div class="cb"></div>
</li>
</ul>
</div>
<!-- More code -->
</div>
{% endblock %}
A view function in django should either return a json or a dict or can return a Webpage
You can either do one of following
1) return a json or dict
return {"value1":"value","value2":"value"}
2)redirect to a web page
return redirect('/some/url/')
3)return Http response
return HttpResponse(status=<status code>,content="some content")
4)render a template
t = loader.get_template('myapp/index.html')
c = {'foo': 'bar'}
return HttpResponse(t.render(c, request),
content_type='application/xhtml+xml')
Because you did not return any response to the view,
According to the docs
A view function is simply a Python function that
takes a Web request and returns a Web response.
You need to return to use render method for initial rendering of form and for redirection to another view you can use redirect method of Django.
A view function must return an HttpResponse. For example, if the process was successfull and you dont want to return anything, you can return HttpResponse(status=200)
When a view handles forms, you have to split GET and POST requests. In the GET part you need to instantiate the form without data. In the POST part you fill the form with request.POST data. And this data must have ALL mandatory fields. A typical view function scheme to handle a form is the folowing:
def view(request):
if request.method == "GET":
form = MyForm()
return ...
if request.method == "POST":
form = MyForm(request.POST)
form.save()
return ...
In your template, you have to show all form fields. Then, all form fields will be passed with the request. If you dont, you need to fill the form fields in the view.
first delete the prints, almost when you make a question, are useless in Django
def add_CartItem(request, product_name_slug):
form = ProductAddToCartForm(request.POST)
if request.method == 'POST':
if form.is_valid():
ci = CartItem.objects.create(cart_id=1, date_added=date.date.today(), quantity=1, itemid=p)
ci.save()#you save your model changes, not your form
return HttpResponseRedirect(reverse('your:url'))#your return the success url or the same
else:
#here error, if form isn't valid
else:
form = ProductAddToCartForm(request.POST)
return render(request, 'your/template.html', {'form': form})
That is the correct way to work with forms in Django, first you must make a if statement asking to the browser if is a post request or a normal request, if is post request, take the data from the forms and are adding to the database, if not, Django return a empty template form.
Let me know if you problem solve

redirect with primary key django {% url %}

I want make blog where I have categories and posts inside.
Categories should be displayed, and when you click on it, you are redirected to another site where articles of this category are shown.
models.py:
class Category(CMSPlugin):
title = models.CharField(max_length=20, default='category')
def __unicode__(self):
return self.title
class Blog_post(CMSPlugin):
category = models.ForeignKey(Category)
style = models.ForeignKey(Blog_style)
title = models.CharField(max_length=200, default='title')
description = models.CharField(max_length=200,default='description')
image = models.ImageField(upload_to='static', null=True, blank=True)
text = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
published_date = models.DateTimeField(blank=True, null=True)
def publish(self):
self.published_date = timezone.now()
self.save()
def __unicode__(self):
return self.title
views.py
def Blog_list(request):
posts = Blog_post.objects.filter(published_date__lte=timezone.now()).order_by('published_date')
category = Category.objects.all()
return render(request, 'blogspot.html', {'posts': posts, 'category':category})
def post_detail(request, pk):
post = get_object_or_404(Blog_post, pk=pk)
return render(request, 'post_detail.html', {'post': post})
def category_detail(request, pk):
cat = get_object_or_404(Category, id=pk)
post_with_category = Blog_post.objects.filter(category=cat)
return render(request, 'articles.html', {'post_with_category': post_with_category})
blogspot.html
{% for post in posts %}
<h1>{{post.title}}</h1>
<a href="{% url 'category_detail' pk=post.category.id %}" >{{ post.category }}</a>
{{post.title}}
{{ post.description }}
{{ post.image }}
{{ post.text }}{{ post.published_date }}
{% endfor %}
So far works all ok. I can click on {{post.title}} and im redirected to post_detail. Now i want to make same logic with categories. When i click on {{post.category}} i want redirect to articles.html where u can see all articles in specific category.
EDIT:
I inserted code to show posts in categories. I stucked with for loop. If i use loop mentioned in post, I get all posts and categories. The problem is if i have 2 posts in one category and this loop will show 2x "category" in template.
So I edited my for loop.
{% for post in category %}
{{post.title}}
{% endfor %}
If I insert <a href="{% url 'category_detail' pk=post.category.id %}" >{{post.title}} in this loop i get no reverse match.
I tried to modify views.py category_detail
And url should looks like localhost/<category>/
And another question is, what is QRM alternative comand for "select*from Table Where Column_id= id ;
urls.py
url(r'^blog/$', views.Blog_list, name='Blog_list'),
url(r'^blog/(?P<pk>\d+)/$', views.post_detail, name='post_detail'),
If I understand your question, django allows you to reference FK objects through the main object. So, since your post.category is an instance of your Category model, you should be able to use post.category.id to do a reverse lookup, so your template would have something along the lines of:
<a href="{% url 'category_detail' pk=post.category.id %}" >{{ post.category }}</a>
Then, in your category_detail view you would just use the pk to get the lookup:
cat = get_object_or_404(Category, id=pk)
post_with_category = Blog_post.objects.filter(category=cat)
Then you could display the list of posts on the new url link that have the same category, via your new post_with_category object list.
EDIT:
Your urls would want to include something like the following for the above html href tag to work:
url(r'^cat/(?P<pk>[0-9]+)/$', views.category_detail, name='category_detail'),

Django NoReverseMatch and Blank Page

I am using Django 1.4.5.
I get an error when I access the detail of entry via admin's page.
"NoReverseMatch at /admin/r/12/1/": Reverse for 'detail' with
arguments '()' and keyword arguments '{'category': u'category',
'slug': u'entry-test'}' not found
And get nothing/blank page when access the detail of entry on front's page (/category/entry-test/).
And what's the right of models & URLname pattern if I want to access the entry detail via sub-category (/category/subcategory/entry-test/)?
Model snippet:
class Entry (models.Model):
title = models.CharField()
slug = models.SlugField()
category = models.ForeignKey('entry.Category')
def __unicode__(self):
return self.title
#models.permalink
def get_absolute_url(self):
return ('entry.views.detail', (), {'category': self.category.slug, 'slug': self.slug})
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.title)
return super(Entry, self).save(*args, **kwargs)
class Category(models.Model):
title = models.CharField()
slug = models.SlugField()
parent = models.ForeignKey('self', blank=True, null=True, related_name='children')
def __unicode__(self):
if self.parent:
return self.parent.title, self.title
return self.title
#permalink
def get_absolute_url(self):
return ('entry.views.category', (), {'slug': self.slug})
Views snippet:
def category(request):
category = Category.objects.all()
return render_to_response('category.html', locals(), context_instance=RequestContext(request))
def list(request, slug_id):
category = get_object_or_404(Category, slug=slug_id)
list = Entry.objects.filter(category=category)
return render_to_response("list.html", locals(), context_instance=RequestContext(request))
def detail(request, category_id, slug_id):
entry = Entry.objects.filter(slug=slug_id)
category = Category.objects.filter(slug=category_id)
return render_to_response('detail.html', locals(), context_instance=RequestContext(request))
URLs snippet:
urlpatterns = patterns('entry.views',
(r'^$', 'category', name='entry-category'),
url(r'^(?P<slug_id>[-\w]+)/$', 'list', name='entry-list'),
url(r'^(?P<category_id>[-\w]+)/(?P<slug_id>[-\w]+)/$', 'detail', name='entry-detail'),
Template snippet:
category.html
<ul>
{% for category in category %}
<li>{{ category.title }} ({{ category.entry_set.all.count }})</li>
{% endfor %}
</ul>
list.html
<ul>
{% for entry in list %}
<li>{{ entry.title }}</li>
{% endfor %}
</ul>
detail.html
<ul>
{% for entry in entry.category %}
<li>{{ entry.title }}
<br />{{ entry.description }}</li>
{% endfor %}
</ul>
Where and what I could be doing wrong?
If any one can help I'd appreciate it! Please help me.
Your keyword arguments for the url are category_id and slug_id, but you are passing in category and slug in your get_absolute_url method. This version should work:
#models.permalink
def get_absolute_url(self):
return ('entry.views.detail',
(), {'category_id': self.category.slug, 'slug_id': self.slug})
Also consider renaming your arguments. id is generally numeric and slug is alphanumeric.
For your blank page problem (from your comment):
Your URL /category/entry-test/ - this will map to your detail view if the snippet your pasted in your question is in your main urls.py. If you have no matching results (the .filter() call has no results), you'll see a "blank" page, because the <ul> will not have any li elements.
If in your main urls.py, you have something like url('^category/', include(some.other.urls)), then the URL will map to your list view, and you have the same issue - the .filter() is not returning any results, and hence you see a "blank" page.
I do little mistake on detail template:
<ul>
{% for entry in entry %}
<li>
{{ entry.title }}
<br />
{{ entry.description }}
</li>
{% endfor %}
</ul>
The problem is solved now.