url path from categories in DetailView - django

I use a DetailView to display all elements inside a category.
I am trying to get my category slugs as variables in urls. But I don't know how to make the link between categories and details.
So far when I click on one of the generated link in the Index.html such as http://localhost:8000/oneofthecategories, I get the following error:
Exception Type: FieldError
Exception Value:
Cannot resolve keyword 'slug' into field. Choices are: author, category, category_id, id, title
Files:
#Models.py
class categories(models.Model):
name = models.CharField(max_length=50,unique=True)
slug = models.SlugField(max_length=100,unique=True)
def __str__(self):
return self.name
class details(models.Model):
title = models.CharField(max_length=100)
author = models.CharField(max_length=42)
category = models.ForeignKey('categories', on_delete=models.CASCADE,related_name="detail")
def __str__(self):
return self.title
#Views.py
class IndexView(generic.ListView):
model = categories
context_object_name = "list_categories"
template_name='show/index.html'
class CategoryDetails(DetailView):
model = details
context_object_name = "detail"
template_name = "show/categories.html"
slug_url_kwarg = 'slug'
#Urls.py
urlpatterns = [
path('', views.IndexView.as_view(), name='index'),
path('<slug:slug>', views.CategoryDetails.as_view(), name='category_details'),
]
#Index.html
{% load staticfiles %}
<p>These is a list of categories</p>
{% for category in list_categories %}
<div class="article">
<h3>{{ category.name }}</h3>
{% for detail in category.categories.all %}
<p> {{detail.title}} </p>
<li>URLS</li>
{% endfor %}
</div>

Related

Filter products using Category Model in Django

I'm building an ecommerce website and want to filter products using categories but I don't know how to render the products. And add the categories in the navigation bar from where people can navigate to different categories.
Here's my views.py file:
from django.shortcuts import render
from django.views import View
from .models import Product, Category
class ProductView(View):
def get(self, request, *args, **kwargs):
products = Product.objects.filter(is_active = True)
context = {
'products': products,
}
return render(request, 'Product/products.html', context)
class ProductDetailView(View):
def get(self, request, slug):
product = Product.objects.get(slug = slug)
context = {
'product': product
}
return render(request, 'Product/productdetail.html', context)
class CategoryView(View):
def get(self, request):
category = Category.objects.all()
products = Product.objects.filter(category__slug = slug)
context = {
'category': category,
'products':products,
}
return render(request, 'Product/category.html', context)
And this is my models.py
from django.db import models
from Seller.models import Seller
class Category(models.Model):
category = models.CharField(max_length = 255)
slug = models.SlugField(max_length = 255)
def __str__(self):
return self.category
class Product(models.Model):
product = models.ForeignKey(Seller, on_delete = models.CASCADE)
title = models.CharField(max_length = 255)
image = models.ImageField(upload_to = 'images', null = True, blank = True)
file = models.FileField(upload_to = 'files', null = True, blank = True)
actual_price = models.PositiveIntegerField(default = '0')
selling_price = models.PositiveIntegerField(default = '0')
slug = models.SlugField(max_length = 255, unique = True)
category = models.ForeignKey(Category, on_delete = models.CASCADE)
description = models.TextField()
is_active = models.BooleanField(default = True)
def __str__(self):
return self.title
I also don't know how to render it in html.
To render you will need to use Django templates. They're not very hard to implement.
The template can start as something like this:
<html>
<body>
{% for product in products %}
<a href="products/{{ product.id }}">
<div>
<img src="{{ product.image.url }}">
<div>
<h3> {{ product.title }}</h3>
<p> {{ product.description|linebreaks }}</p>
</div>
</div>
</a>
{% endfor%}
</body>
</html>
Filtering products based on category is simple:
products = Product.objects.filter(category__category='burger_buns')
If you want multiple categories:
category_names = 'burger_buns sausages hammers'.split()
products = Product.objects.filter(category__category__in=category_names)
Or you can use the reverse accessor (more here):
category = Category.objects.get(id=1)
products = category.product_set.all()
I highly recommend reading the docs on this.
When you defined the ForeignKey relation to Category from Product, there'll also be a backward relationship. By default is named category.product_set. So in your template, when a user chooses a category instance, you can get its corresponding products with category1.product_set. To change this name, define a related_name for the ForeignKey relation.
category = models.ForeignKey(Category, related_name="products", on_delete = models.CASCADE)
now to access the category's corresponding products, just do something like category1.products.
Your question is composed, then i'll answer to the categories issue (How to show categories in a nav bar and go through them).
views.py
from django.views.generic.list import ListView
from .models import Category, Product
class CategoryListView(ListView):
model = Category
# Override the context_object_name, default is 'object_list'
context_object_name = 'categories'
# Assume that category_list.html is in app/templates/ folder
template_name = 'category_list.html'
class CategoryDetailView(DetailView):
model = Category
context_object_name = 'category' # Default is 'object'
# Assume that category_detail.html is in app/templates/ folder
template_name = 'category_detail.html'
# Add the product of this category to the context
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# This line retrieve all the products of this category
context['products'] = self.product_set.all()
return context
urls.py
from django.urls import path
from .views import CategoryListView, CategoryDetailView
urlpatterns = [
path('category/', CategoryListView.as_view(), name='category-list'),
path('category/<slug:slug>/', CategoryDetailView.as_view(), name='category-detail'),
]
category_list.html
<h1 class="head">Products Categories</h1>
{% for cat in categories %}
<div>
<a href="{% url 'category-detail' cat.slug %}">
<h3>{{ cat.category }}</h3>
</a>
</div>
{% endfor %}
</div>
category_detail.html
<h1 class="head">{{ category.category }}</h1>
<h3>Products for this category</h3>
<div>
{% for product in products %}
<div>
<h5>{{ product.title }}<h5/>
<div>{{ product.description }}</div>
<img src="{{ product.image.url }}"/>
</div>
{% endfor %}
</div>
first, you need to go to urls.py and do
urls.py as an example
from .view import CategoryView
url_patterns =[
path("products/category/<slug:slug>/,CategoryView.as_view(),name="product-category")
]
in view
class CategoryView(View):
def get(self, request, slug):
category = Category.objects.all()
products = Product.objects.filter(category__slug = slug)
context = {
'category': category,
'products':products,
}
return render(request, 'Product/category.html', context)

NoReverseMatch at /products/ "adding products to the basket"

I am fairly new and working on a project and want to just add products to the basket
I have struggled with this for a few days and just don't know what to do to make it work.
can anybody help me or refer me to some page that can help me?
Thank You
NoReverseMatch at /products/
Reverse for 'addtobasket' with arguments '('Python Tutorial',)' not found. 1 pattern(s) tried: ['productapp/\.\./basket/$']
models:
class Products(models.Model):
products_name = models.CharField(max_length=30)
pub_date = models.DateTimeField(auto_now=True)
price = models.CharField(max_length=10)
note = models.CharField(max_length=200)
inventory = models.IntegerField(default=1)
product_pic = models.ImageField(upload_to ='images/', default='images/broken/broken.png')
def __str__(self):
if (self.inventory<=0):
return self.products_name + ' (Out of Stock)'
return self.products_name
class Basket(models.Model):
products = models.ForeignKey(Products, on_delete=models.CASCADE)
pub_date = models.DateTimeField(auto_now=True)
amount = models.IntegerField(default=1)
def __str__(self):
return str(self.products)
urls:
app_name = 'productapp'
urlpatterns = [
path('products/', views.products, name='products'),
path('basket/', views.basket, name ='basket'),
path('', views.home, name='home'),
path('../basket/', views.addtobasket, name='addtobasket'),
]
views:
def products(request):
products_list = Products.objects.order_by('-pub_date')
context = {'products_list': products_list}
return render(request, 'productapp/products.html', context)
def basket(request):
basket_list = Basket.objects.order_by('-pub_date')
context = {'basket_list': basket_list}
return render(request, 'productapp/basket.html', context)
def addtobasket(request, name):
basket = Basket.objects.all()
product = Products.objects.get(products_name=name)
basket.products.add(product)
return HttpResponseRedirect(reverse("basket"))
html:
<body>
<section>
{% block content %}
{% if products_list %}
{% for product in products_list %}
<div>
<div>
<p>{{product.products_name}}</p>
<p>{{product.price}}</p>
Add to Basket
</div>
</div>]
{% endfor %}
{% else %}
<p>No products avalable at this moment.</p>
{% endif %}
{% endblock %}
</section>
</body>
A path with ../ makes not much sense, since that means the url should contain the dots.
Furthermore you should specify the URL parameter in the path:
app_name = 'productapp'
urlpatterns = [
path('products/', views.products, name='products'),
path('basket/', views.basket, name ='basket'),
path('', views.home, name='home'),
path('basket/add/<str:name>/', views.addtobasket, name='addtobasket'),
]
Adding an item to the basket should not work with a GET request, since the HTTP protocol specifies that GET requests should not have side-effects. You will need to work with a POST request, for example by implementing a mini-form with method="post", or through an AJAX post request.

Django Class Based Views not rendering variables in Template

I'm pretty new to Django and already have read a lot about class-based views before coming here. I'm trying to build a one page website, with dynamic blocks that can be written from the Django admin. My problem is that I cannot manage to render variables from my database in my template. Here's what I wrote:
models.py
from django.db import models
from tinymce.models import HTMLField
class MyResume(models.Model):
subline = models.CharField(max_length=200)
content = HTMLField()
class Meta:
verbose_name = "My Resume"
verbose_name_plural = "My Resume"
def __str__(self):
return "My Resume"
class AboutMe(models.Model):
subline = models.CharField(max_length=200)
content = HTMLField()
cover_img = models.ImageField(upload_to="about_me")
class Meta:
verbose_name = "About me"
verbose_name_plural = "About me"
def __str__(self):
return "About me"
class Experience(models.Model):
subline = models.CharField(max_length=200)
pres_content = models.TextField()
exp_content = HTMLField()
date_exp = models.IntegerField()
class Meta:
verbose_name = "Experience"
verbose_name_plural = "Experiences"
def __str__(self):
return "Experience"
class ProjectPost(models.Model):
title = models.CharField(max_length=200)
technology = models.CharField(max_length=100)
subline = models.CharField(max_length=200)
content = HTMLField()
project_post_cover = models.ImageField(upload_to="projectpost_cover")
class Meta:
verbose_name = "Project Post"
verbose_name_plural = "Project Posts"
def __str__(self):
return self.title
class BlogPost(models.Model):
title = models.CharField(max_length=200)
overview = models.TextField()
timestamp = models.DateTimeField(auto_now_add=True)
content = HTMLField()
blogpost_thumbnail = models.ImageField(upload_to="blogpost_thumbnail")
class Meta:
verbose_name = "Blog Post"
verbose_name_plural = "Blog Posts"
def __str__(self):
return self.title
class Contact(models.Model):
subline = models.CharField(max_length=200)
presentation_content = HTMLField()
def __str__(self):
return "Contact section text"
views.py
from django.shortcuts import render
from django.views.generic.base import TemplateView
from .models import *
class IndexTemplateView(TemplateView):
template_name = 'index.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) # first, call super get context data
context['myresume'] = MyResume.objects.all()
context['aboutme'] = AboutMe.objects.all()
context['experience'] = Experience.objects.all()
context['projectpost'] = ProjectPost.objects.all()
context['blogpost'] = BlogPost.objects.all()
context['contact'] = Contact.objects.all()
return context
core.urls.py:
from django.urls import path
from .views import IndexTemplateView
urlpatterns = [
path('', IndexTemplateView.as_view()),
]
appname.urls.py:
from django.contrib import admin
from django.urls import include, path
from django.conf.urls import url
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path('admin/', admin.site.urls),
url(r'^tinymce/', include('tinymce.urls')),
path('', include('core.urls')),
]+ static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
and a snippet from my index.html template:
{% extends 'base.html' %}
{% load static %}
{% block content %}
(...)
<!-- ==================== ABOUT ==================== -->
<section id="about" class="section">
<h2 class="title">ABOUT</h2>
<div class="section-des">
{{ aboutme.subline }}
</div>
<div class="content-670">
<p>
{{ aboutme.content }}
</p>
</div>
<img class="about-img block-right" data-jarallax-element="0 -40" src="{{ aboutme.cover_img.url }}" alt="">
(...)
{% endblock content %}
All of the variables you've added to your template context are querysets, not objects. But you're trying to read them as individual objects in your template, which is why it doesn't work. You have two options:
Iterate over each queryset in your template:
{% for item in aboutme %}
<h2 class="title">ABOUT</h2>
{{ item.subline }}
{{ item.content }}
{% endfor %}
If you only want to display one of each model, then you need to fix your context method to only return one object, e.g.,
context['aboutme'] = AboutMe.objects.first()
Where here you're just returning the first AboutMe object in the database. If you do this, then your existing template logic will work. How you determine which object to return depends on what your data looks like.
{% for obj in myresume %}
<p>{{ obj.subline }}</p>
{% endfor %}
{% for obj in aboutme %}
<p>{{ obj.subline }}</p>
{% endfor %}
you can call your objects in this way in your templates. Also modify str function in models to get the dynamic content.
class MyResume(models.Model):
subline = models.CharField(max_length=200)
content = HTMLField()
class Meta:
verbose_name = "My Resume"
verbose_name_plural = "My Resume"
def __str__(self):
return self.sublime

reverse url link within list view template

i created methods within my model class to handle my redirects all work except for the one i created for a link in my list view(go_to_create method ) im using class based views
class Todo(models.Model):
name = models.CharField(max_length=100, default='unamedTodo')
description = models.CharField(max_length=200)
Todo_date = models.DateTimeField('Todo Date')
pub_date = models.DateTimeField('Date Published')
def get_absolute_url(self):
return reverse('ToDo:detail', kwargs={'id': self.id})
def get_back_home(self):
return reverse('ToDo:todos', kwargs={})
def go_to_update(self):
return reverse('ToDo:update', kwargs={'id': self.id})
def go_to_create(self):
return reverse('ToDo:create', kwargs={})
class TodoCreateView(CreateView):
template_name = 'ToDo/todo_create.html'
form_class = TodoForm
queryset = Todo.objects.all()
from django.urls import path
from .views import (
TodoListView,
TodoDetailView,
TodoCreateView,
TodoUpdateView,
TodoDeleteView,
)
app_name = "ToDo"
urlpatterns = [
path('Todos/', TodoListView.as_view(), name='todos'),
path('Todos/<int:id>/', TodoDetailView.as_view(), name='detail'),
path('Todos/create/', TodoCreateView.as_view(), name='create'),
path('Todos/<int:id>/update/', TodoUpdateView.as_view(), name='update'),
path('Todos/<int:id>/delete/', TodoDeleteView.as_view(), name='delete')
]
<h1>ToDo's</h1>
<ul>
{% for object in object_list %}
<li>
<p>
{{ object.id }} -
{{ object.name }}
</p>
</li>
{% endfor %}
<p>Create new Todo here</p>
</ul>
the link calling the go_to_create method does not work i stay on the same page no error is generated
Instead of a method call directly on template,that will do the job.
{% url 'ToDo:create' %}

Django dynamic filtering by passing slug from template

So I've read this article on dynamic filtering in Django; Dynamic filtering
Though I had something working I needed two similar views, one to show all different categories with their slug as parameter, and a second view to show all posts from all categories without a parameter.
How can I use a single ListView and pass an empty parameter from the template to show all posts from all categories in a list?
Or would this even be possible?
urls.py
urlpatterns = [
path('', views.PostListView.as_view(), name='index'),
path('<slug:slug>/', views.PostbyCategoryListView.as_view(), name="category"),
]
views.py
class PostListView(ListView):
model = Post
template_name = 'index.html'
class PostbyCategoryListView(ListView):
model = Post
template_name = 'index.html'
def get_queryset(self):
self.category = get_object_or_404(Category, slug=self.kwargs['slug'])
return Post.objects.filter(category=self.category)
index.html
...
<h6>Categories</h6>
{% if category_list %}
<ul>
<li class="category-item">
All categories
</li>
{% for category in category_list %}
<li class="category-item">
{{ category.name }}
</li>
{% endfor %}
</ul>
{% else %}
<p>No categories</p>
{% endif %}
...
models.py
class Category(models.Model):
name = models.CharField(max_length=200)
slug = models.CharField(max_length=200)
class Post(models.Model):
category = models.ForeignKey('Category',default=1, on_delete=models.SET_NULL, null=True, related_name="posts")
title = models.CharField(max_length=200)
content = models.TextField(max_length=2000)