'Category' object has no attribute 'post_set' - python

so I am trying to add a category system for posts by following a tutorial on this website https://djangopy.org/how-to/how-to-implement-categories-in-django/ (I changed my code up a little)
Everything works like creating categories, adding a post, viewing a post, but if I try to go to the category page to view posts only in that category so /category/CATNAME but it shows me this error
'Category' object has no attribute 'post_set'
models.py
from django.db import models
from django.contrib.auth.models import User
from django.utils.text import slugify
from markdownx.models import MarkdownxField
from markdownx.utils import markdownify
from taggit.managers import TaggableManager
class Category(models.Model):
name = models.CharField(max_length=100)
short_desc = models.CharField(max_length=160)
slug = models.SlugField()
parent = models.ForeignKey('self', blank=True, null=True, related_name='children', on_delete=models.CASCADE)
class Meta:
unique_together = ('slug', 'parent',)
verbose_name_plural = "Categories"
def __str__(self):
full_path = [self.name]
k = self.parent
while k is not None:
full_path.append(k.name)
k = k.parent
return ' -> '.join(full_path[::-1])
def save(self, *args, **kwargs):
value = self.title
self.slug = slugify(value, allow_unicode=True)
super().save(*args, **kwargs)
class Thread(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=150)
content = MarkdownxField()
tags = TaggableManager()
slug = models.SlugField(unique=True)
category = models.ForeignKey('Category', null=True, blank=True, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.title
class Meta:
verbose_name_plural = 'Threads'
def get_cat_list(self):
k = self.category
breadcrumb = ["dummy"]
while k is not None:
breadcrumb.append(k.slug)
k = k.parent
for i in range(len(breadcrumb)-1):
breadcrumb[i] = '/'.join(breadcrumb[-1:i-1:-1])
return breadcrumb[-1:0:-1]
def save(self, *args, **kwargs):
value = self.title
self.slug = slugify(value, allow_unicode=True)
super().save(*args, **kwargs)
views.py
from django.shortcuts import render, redirect, get_object_or_404
from .models import Category, Thread
from .forms import NewThreadForm
def show_thread_view(request, hierarchy=None):
category_slug = hierarchy.split('/')
category_queryset = list(Category.objects.all())
all_slugs = [ x.slug for x in category_queryset ]
for slug in category_slug:
if slug in all_slugs:
# parent = get_object_or_404(Category, slug=slug, parent=parent)
parent = Category.objects.filter(slug__in=category_slug, parent=None).first()
thread = get_object_or_404(Thread, slug=slug)
instance = get_object_or_404(Thread, slug=slug)
breadcrumbs_link = instance.get_cat_list()
category_name = [' '.join(i.split('/')[-1].split('-')) for i in breadcrumbs_link]
breadcrumbs = zip(breadcrumbs_link, category_name)
context = {
'thread': thread,
'instance': instance,
'breadcrumbs': breadcrumbs,
}
return render(request, "forums/threads/thread_detail.html", context)
def show_category_view(request, hierarchy=None):
category_slug = hierarchy.split('/')
category_queryset = list(Category.objects.all())
all_slugs = [ x.slug for x in category_queryset ]
parent = None
for slug in category_slug:
if slug in all_slugs:
#parent = get_object_or_404(Category, slug=slug, parent=parent)
parent = Category.objects.filter(slug__in=category_slug, parent=None).first()
context = {
'category': parent,
'post_set': parent.post_set.all(),
'sub_categories': parent.children.all(),
}
return render(request, "forums/categories.html", context)
def new_thread_form_view(request):
if request.method == "POST":
form_data = request.POST or None
form = NewThreadForm(form_data)
if form.is_valid():
news = form.save(commit=False)
news.author = request.user
news.save()
return redirect('/forums')
else:
form = NewThreadForm()
context = {
'form': form
}
return render(request, "forums/threads/thread_form.html", context)
categories.html
{% extends 'base.html' %}
{% load static %}
{% block content %}
<br>
{% if sub_categories %}
<h3>Sub Categories</h3>
{% for i in sub_categories %}
{{ i.name }}
{% endfor %}
{% endif %}
<div class="row small-up-1 medium-up-3" >
{% if post_set %}
{% for i in post_set %}
<div class="columns">
<div class=" card-article-hover card">
<a href="{{ i.slug }}">
<img src="{{ i.cover_photo.url }}">
</a>
<div class="card-section">
<a href="{{ i.slug }}">
<h6 class="article-title">{{ i.title | truncatechars:30}}</h6>
</a>
</div>
<div class="card-divider flex-container align-middle">
{{ i.user.get_full_name }}
</div>
<div class="hover-border">
</div>
</div>
</div>
{% endfor %}
{% endif %}
</div>
{% endblock %}

The model which has ForeignKey relation to Category model is Thread(as per shared code from the question). So you need to use parent.thread_set.all() to get all the threads related to that category. Also if you define related_name inside Thread to Category ForeignKey like following example:
class Thread(..):
category = models.ForeignKey('Category', null=True, blank=True, on_delete=models.CASCADE, related_name='threads')
Then you can get the threads by parent.threads.all(). More information can be found in documentation.

Related

Django: How do I assign a button to take info and save()

Is there a way for the button to do the following? : When user press the button it takes the user.username of the current user and automatically fill up a form of BookInstance from models.py and save it to the database.
From models.py :
class BookInstance(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
book = models.ForeignKey("Book", on_delete=models.RESTRICT, null=True)
imprint = models.CharField(max_length=200, blank=True, null=True)
due_back = models.DateField(blank=True, null=True)
borrower = models.ForeignKey(
User, on_delete=models.SET_NULL, blank=True, null=True)
LOAN_STATUS = (
('m', 'Maintenance'),
('o', 'On Loan'),
('a', 'Available'),
('r', 'Reserved')
)
status = models.CharField(
max_length=1, choices=LOAN_STATUS, blank=True, default='a')
class Meta:
ordering = ['due_back']
def __str__(self):
return f'{self.id} - {self.book.title}'
def get_absolute_url(self):
return reverse("catalog:book_list")
class Book(models.Model):
title = models.CharField(max_length=50)
author = models.ForeignKey(
'Author', on_delete=models.SET_NULL, null=True)
summary = models.TextField(
max_length=500, help_text="Enter brief description")
isbn = models.CharField('ISBN', max_length=13, unique=True)
genre = models.ManyToManyField(Genre, help_text="Select genre")
language = models.ForeignKey(
"Language", on_delete=models.SET_NULL, null=True)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse("catalog:book_detail", kwargs={"pk": self.pk})
This is my from my views.py :
def borrowBook(request, pk):
context = {
'book_instance': BookInstance.objects.all()
}
success_url = reverse_lazy('catalog:index')
if request.method == "POST":
form = BorrowForm(request.POST or None)
if form.is_valid():
book_instance.id = BookInstance.objects.get(pk=pk)
book_instance.book = BookInstance.objects.get(book=book)
book_instance.borrower = request.user
book_instance.status = 'o'
book_borrowed_count = BookInstance.objects.filter(
owner=request.user).count()
if book_borrowed_count < 4:
book_instance = form.save(commit=False)
book_instance.save()
else:
print("Maximum limit reached!")
return redirect('catalog:index')
return render(request, 'catalog/book_detail.html', {'form': form})
here's from my BorrowForm from forms.py :
class BorrowForm(forms.ModelForm):
class Meta:
model = BookInstance
fields = '__all__'
here's my from my urls.py :
path("book_list/book/<int:pk>/borrow", views.borrowBook, name="borrowBook"),
I also tried using a CBV here:
class BorrowBookView(PermissionRequiredMixin, CreateView):
permission_required = 'login'
model = BookInstance
fields = '__all__'
template_name = 'catalog/borrow_form.html'
success_url = reverse_lazy('catalog:index')
def post(self, request, *args, **kwargs):
book_instance.id = BookInstance.objects.get(pk=pk)
book_instance.book = BookInstance.objects.get(book=book)
book_instance.borrower = request.user
book_instance.status = 'o'
book_instance = form.save(commit=False)
book_instance.save()
CBV path from urls.py :
path("book_list/book/<int:pk>/borrow/",
views.BorrowBookView.as_view(), name="book_borrow"),
Here's how I implemented the button using suggestions from here:
<form action="#" method="post">
{% csrf_token %}
<button
type="submit"
class="btn btn-dark flex-shrink-0 "
value="{{ book.id }}">Borrow
</button>
but when I pressed it doesn't seem to save anything to the database and just popup errors, though I may implemented the button or the function from my is views wrong. Thanks and appreciate for any help provided.
You do not need a Django form for this. Forms are usually used for when you want to create objects or edit its fields (like in the admin page). While here an user is not editing nor creating an object (book), but borrowing one.
So basically, we just need to list all available book instances (status='a'), and have a button to "borrow" it. The borrow action is to update status to 'r' or 'o' and have the borrower updated to the current user which is guaranteed to exist inside the request object by LoginRequiredMixin
views.py
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views import View
from django.contrib import messages
from django.urls import reverse
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404
from .models import BookInstance
class BorrowBook(LoginRequiredMixin, View):
def get(self, request, *args, **kwargs):
book_id = kwargs['pk']
available_books = BookInstance.objects.filter(book__pk=book_id, status='a')
return render(request, 'borrow_book.html', {'available_books': available_books})
def post(self, request, *args , **kwargs):
book_instance_id = request.POST['id']
obj = get_object_or_404(BookInstance, id=book_instance_id)
obj.status = 'r'
obj.borrower = request.user
# Maybe also update due_back data
# obj.due_back = ...
obj.save()
messages.success(request, "Your book is reserved.")
# I used the redirection to the same template
# But you probably want to send the user somewhere else
return HttpResponseRedirect(reverse('core:borrow-book', kwargs={'pk': 1}))
borrow_book.html
{% extends 'base.html' %}
{% block content %}
{% if messages %}
<ul class="messages">
{% for message in messages %}
<li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% for instance in available_books %}
<form action="{% url 'core:borrow-book' instance.book.id %}" method="POST">
{% csrf_token %}
<input type="hidden" name="id" value="{{instance.id}}">
<p>{{instance.book}}</p>
<p>{{instance.book.language.name}}</p>
<input type="submit" value="Borrow this book.">
</form>
{% endfor %}
{% endblock content %}
urls.py
from django.urls import path
from core import views
app_name = 'core'
urlpatterns = [
path("book_list/book/<int:pk>/borrow/", views.BorrowBook.as_view(), name="borrow-book"),
]

Populating Field in Form with Images in Django

I'm trying to populate a field with images from a model in Django. The idea is that the user will enter the name of their product, then ultimately an API with google images will look that up and populate a field on the next page with a couple images to select.
At this stage, I'm just trying to get a field on the form to show multiple images for the user to select and I'm having a hard time. Here's some of my code, any input is helpful. I know there is a lot out there around this topic and I've looked through a lot but I'm still stuck. My most recent attempt was to create a custom Widget, I'm really open to any solution that will work.
I'm really not a Django/Python wizard so bear with me :) Thank you!
Models.py
class ProductImage(models.Model):
imageId = models.AutoField(auto_created=True, primary_key=True)
image = models.ImageField(upload_to='images',null=True,blank=True)
def __str__(self):
return str(self.image)
class Item(models.Model):
itemId = models.AutoField(auto_created=True, primary_key=True)
itemName = models.CharField(
"Name",
max_length=1024,
)
category = models.ForeignKey(Category,
on_delete=models.CASCADE,
related_name='category'
)
itemImage = models.ForeignKey(ProductImage,
on_delete=models.CASCADE,
related_name='photo',
default="",
)
itemOwner = models.ForeignKey(Customer,
on_delete=models.CASCADE,
related_name='my_items'
)
itemAvaialable = models.BooleanField()
costPerItem = models.IntegerField(verbose_name='Cost per Item (USD)')
itemDescription = models.TextField(null=True, blank=True)
itemAddedDate = models.DateField(auto_now_add=True)
asin = models.CharField(null=True, blank=True, max_length=10)
forms.py
class ImageCreationForm(forms.ModelForm):
class Meta:
model = ProductImage
fields = ('image',)
def clean_itemImage(self):
itemImage = self.cleaned_data['itemImage']
valid_extensions = ['jpg', 'jpeg']
extension = itemImage.rsplit('.', 1)[1].lower()
if extension not in valid_extensions:
raise forms.ValidationError('The given product Image file does not ' \
'match valid image extensions.')
return itemImage
class ItemCreationForm(forms.ModelForm):
class Meta:
model = Item
fields = '__all__'
widgets = {
'itemAddedDate': forms.HiddenInput,
}
Views.py
class AddImageView(LoginRequiredMixin,CreateView):
model = ProductImage
template_name = 'addImage.html'
fields = ('image',)
def form_valid(self, form):
form.is_valid()
form.instance.itemOwner = self.request.user
return super().form_valid(form)
def get_success_url(self):
return reverse('RentalApp:my_products')
class AddProductView(LoginRequiredMixin, CreateView):
model = Item
template_name = 'addProduct.html'
image = forms.FileField(required=False)
fields = ('itemName',
'category',
'itemImage',
'itemAvaialable',
'costPerItem',
'itemDescription')
login_url = '/users/login/'
def form_valid(self, form):
form.fields["itemImage"].queryset = ProductImage.objects.all()
form.is_valid()
form.instance.itemOwner = self.request.user
return super().form_valid(form)
def get_success_url(self):
return reverse('RentalApp:my_products')
Widgets.py
class ImageWidget(forms.widgets.Widget):
def render(self, name, value, attrs=None, **kwargs):
html = Template("""<img src="$link"/>""")
return mark_safe(html.substitute(link=value))
Serializers.py
class ProductImageSerializer(serializers.ModelSerializer):
product_image = serializers.SerializerMethodField()
class Meta:
model = ProductImage
fields = ('image')
class ProductImageRequirementSerializer(serializers.ModelSerializer):
class Meta:
model = ProductImage
fields = ('image')
class ProductSerializer(serializers.ModelSerializer):
Product_requirement = serializers.SlugRelatedField(many=True,read_only=True,slug_field='text')
club_image = ProductImageSerializer(many=True)
class Meta:
model = ProductImage
fields = ('image')
Template for adding a product:
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<div id="login">
<h5 class="text-center pt-5">Add Product</h5>
<div id="login-row" class="row justify-content-center align-items-center">
<div id="login-column" class="col-md-6">
<div id="login-box" class="col-md-12">
<form action="" method="post" id="login-form" enctype="multipart/form-data" class="form">{% csrf_token %}
{{ form|crispy }}
<br>
<button class="btn btn-success ml-2" type="submit">Save</button>
<a href="{% url 'RentalApp:my_products' %}" class="btn btn-secondary ml-2">
Cancel</a>
</form>
</div>
</div>
</div>
</div>
{% endblock content %}

Back button to a related object

I'm building an app where I can add recipes and add ingredients to those recipes. On view recipe_details I have a button to add_new_ingredient. When I'm on new_ingredient_form I want to have back button to get back to the details of recipe. I'm trying to pass recipe's pk but it doesn't work. How am I able to pass recipe's pk to be able to back to previous view?
models.py
class Recipe(Timestamp):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, unique=True)
title = models.CharField(max_length=100, unique=True)
preparation = models.TextField()
def __str__(self):
return self.title
class Ingredient(Timestamp):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, unique=True)
recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE)
name = models.CharField(max_length=100)
amount = models.PositiveSmallIntegerField(blank=True, null=True)
unit = models.ForeignKey('Unit', on_delete=models.SET_NULL, blank=True, null=True)
def __str__(self):
return self.name
views.py
class RecipeView(generic.DetailView):
model = Recipe
context_object_name = 'recipe'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['ingredients_list'] = Ingredient.objects.filter(recipe=self.object.pk)
return context
class AddIngredientView(generic.edit.CreateView):
model = Ingredient
fields = [
'name',
'amount',
'unit'
]
success_url = '/'
template_name = 'recipes/add_ingredient.html'
def dispatch(self, request, *args, **kwargs):
self.recipe = get_object_or_404(Recipe, pk=self.kwargs['pk'])
return super().dispatch(request, *args, **kwargs)
def form_valid(self, form):
form.instance.recipe = self.recipe
return super().form_valid(form)
def get_success_url(self):
if 'add_another' in self.request.POST:
url = reverse_lazy('recipes:add_ingredient', kwargs={'pk': self.object.recipe_id})
else:
url = reverse_lazy('recipes:recipe', kwargs={'pk': self.object.recipe_id})
return url
add_ingredient.html
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<form method="POST">
{% csrf_token %}
{{ form|crispy }}
<button class="btn btn-success" type="submit">Save</button>
<button class="btn btn-success" type="submit" name="add_another">Save and add another</button>
Back
</form>
{% endblock %}
You can go to the previous page by adding {{request.META.HTTP_REFERER}} to the href property of the button.
Django request to find previous referrer

Pagination in DetailView [Django]

I have Article and ArticleCategory in my model. Article has many categories.
MODELS
class ArticleCategory(Created):
category_name = models.CharField(max_length=128)
slug = models.SlugField(null=False, unique=False)
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.category_name)
return super().save(*args, **kwargs)
def __str__(self):
return self.category_name
class Article(Created):
title = models.CharField(max_length=120)
author = models.ForeignKey(User, on_delete=models.CASCADE)
snippet = models.TextField(null=False) # ustawić max_lenght
body = RichTextField(null=False)
category = models.ManyToManyField(ArticleCategory, related_name='articles') # TODO: ustawić on_delete
image = models.ImageField(blank=True, null=True, upload_to='article_image/')
slug = models.SlugField(null=False, unique=False)
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.title)
return super().save(*args, **kwargs)
def get_absolute_url(self):
return reverse('news:article_detail', kwargs={'pk': self.pk, 'slug': self.slug})
Also I have Url to specific category with all Articles with this category:
URL
urlpatterns = [
path('show/<int:pk>/<slug:slug>', ArticleDetailView.as_view(), name='article_detail'),
path('all/', AllArticlesListView.as_view(), name='all_articles_list'),
path('category/<slug:slug>/', CategoryArticlesList.as_view(), name='category_articles_list'),
]
In the VIEW i created
class CategoryArticlesList(DetailView):
template_name = 'news/category_articles_list.html'
model = ArticleCategory
And finally template CATEGORY_ARTICLES_LIST.HTML
SPECIFIC CATEGORY: {{ articlecategory.category_name | upper }}
AND ALL NEWS WITH THIS CATEGORY
{% for article in articlecategory.articles.iterator reversed %}
<h3>{{ article.title }}</h3> <br>
{{ article.body | safe }} <br>
{% endfor %}
My question is... How can I make Pagination to all specific category articles in Views?
If I use ListView in class CategoryArticlesList there will be big problem with query form me... I dont know what I should do.
A DetailView does not do pagination, since, well there is only a single object. You can howver make it a ListView and add some logic to pass the articlecategory:
from django.shortcuts import get_object_or_404
class ArticlesByCategoryListView(ListView):
template_name = 'news/category_articles_list.html'
model = Article
queryset = Article.objects.order_by('-pk')
paginate_by = 25
def get_queryset(self, *args, **kwargs):
return super().get_queryset(*args, **kwargs).filter(
category__slug=self.kwargs['slug']
)
def articlecategory(self):
return get_object_or_404(ArticleCategory, slug=self.kwargs['slug'])
then in the template, we can render this with:
SPECIFIC CATEGORY: {{ view.articlecategory.category_name|upper }}
AND ALL NEWS WITH THIS CATEGORY
{% for article in page_obj %}
<h3>{{ article.title }}</h3> <br>
{{ article.body|safe }} <br>
{% endfor %}

Getting an error whenever i click on add to cart button

i created a cart app inside an ecommerce site am building but when i click on
the add cart form that is rendered on my page, it doesn't come through. any
help will be appreciated
inside the cart app, i have the normal django files in it.(__init__.py,
admin.py, apps.py, cart.py (added by me), models.py, tests.py, urls.py and views.py)
Here are the content of files in my cart app that i filled with content.
for
cart.py:
from cart.models import CartItems
from catalog.models import Product
from django.shortcuts import get_object_or_404
from django.http import HttpResponseRedirect
import decimal
import random
CART_ID_SESSION_KEY = 'cart_id'
# get the current user's cart id, sets new one if blank
def _cart_id(request):
if request.session.get(CART_ID_SESSION_KEY,'') == '':
request.session[CART_ID_SESSION_KEY] = _generate_cart_id()
return request.session[CART_ID_SESSION_KEY]
def _generate_cart_id():
cart_id = ''
characters =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890!##$%^&*()'
cart_id_length = 50
for y in range(cart_id_length):
cart_id += characters[random.randint(0, len(characters)-1)]
return cart_id
# return all items from the current user's cart
def get_cart_items(request):
return CartItems.objects.filter(cart_id=_cart_id(request))
# add an item to the cart
def add_to_cart(request):
postdata = request.POST.copy()
# get product slug from post data, return blank if empty
product_slug = postdata.get('self.slug','')
# get quantity added, return 1 if empty
quantity = postdata.get('quantity',1)
# fetch the product or return a missing page error
p = get_object_or_404(Product, slug=product_slug)
#get products in cart
cart_products = get_cart_items(request)
product_in_cart = False
# check to see if item is already in cart
for cart_item in cart_products:
if cart_item.product.id == p.id:
# update the quantity if found
cart_item.augment_quantity(quantity)
product_in_cart = True
if not product_in_cart:
# create and save a new cart item
ci = CartItems()
ci.product = p
ci.quantity = quantity
ci.cart_id = _cart_id(request)
ci.save()
# returns the total number of items in the user's cart
def cart_distinct_item_count(request):
return get_cart_items(request).count()
models.py:
from django.db import models
from catalog.models import Product
class CartItems(models.Model):
cart_id = models.CharField(max_length=50)
date_added = models.DateTimeField(auto_now_add=True)
quantity = models.IntegerField(default=1)
product = models.ForeignKey(Product, unique=False,
on_delete=models.CASCADE)
class Meta:
db_table = 'cart_items'
ordering = ['date_added']
def total(self):
return self.quantity * self.product.price
def name(self):
return self.product.name
def price(self):
return self.product.price
def get_absolute_url(self):
return self.product.get_absolute_url
urls.py:
from django.urls import path
from cart import views
urlpatterns = [
path('', views.show_cart, name='show_cart')
]
views.py:
from django.shortcuts import render
from django.template import RequestContext
from cart import cart
def show_cart(request):
cart_item_count = cart.get_cart_items(request)
context_instance = RequestContext(request)
page_title = "showing Cart"
context = {'page_title' : page_title,
'cart_item_count': cart_item_count,
'context_instance': context_instance
}
template = 'cart/cart.html'
return render(request, template, context)
Below are the models.py, views.py and forms.py of another app(catalog)
in the project that have inheritance of the cart app
models.py:
from django.db import models
from django.urls import reverse
class Category(models.Model):
name = models.CharField(max_length=50, db_index=True)
slug = models.SlugField(max_length=50, unique=True, help_text="Unique
value for product page URL, created from name.")
description = models.TextField(blank=False)
is_active = models.BooleanField(default=True)
meta_keywords = models.CharField("Meta Keywords", max_length=255,
help_text="Comma-delimited set of SEO keywords for meta tag")
meta_description = models.CharField("Meta Description", max_length=255,
help_text="Content for description meta tag")
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
verbose_name = "category"
ordering = ["-created_at"]
verbose_name_plural = "Categories"
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('catalog:show_category', args=[self.slug])
class Product(models.Model):
name = models.CharField(max_length=255, unique=True)
slug = models.SlugField(max_length=255, unique=True, help_text="Unique
value for product page URL, created from name.")
brand = models.CharField(max_length=50)
sku = models.CharField(max_length=50)
price = models.DecimalField(max_digits=9, decimal_places=2)
old_price = models.DecimalField(max_digits=9, decimal_places=2,
blank=True, default=0.00)
image = models.ImageField(upload_to='media', blank=True)
is_active = models.BooleanField(default=True)
is_bestseller = models.BooleanField(default=False)
is_featured = models.BooleanField(default=False)
quantity = models.IntegerField()
description = models.TextField(blank=False)
meta_keywords = models.CharField(max_length=255, help_text = 'Comma-
delimited set of SEO keywords for meta tag')
meta_description = models.CharField(max_length=255, help_text = 'Content
for description meta tag')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
categories = models.ManyToManyField(Category)
class Meta:
db_table = 'products'
ordering = ['-created_at']
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('catalog:show_product', args=[self.slug])
def sale_price(self):
if self.old_price > self.price:
return self.price
else:
return None
views.py:
from django.shortcuts import render, get_object_or_404, redirect
from catalog.models import Category, Product
from django.template import RequestContext
from django.urls import reverse
from cart import cart
from django.http import HttpResponseRedirect
from catalog.forms import ProductAddToCartForm
def index(request):
context_instance = RequestContext(request)
template = "catalog/index.html"
page_title = 'Musical Instruments and Sheet Music for Musicians'
context = {'page_title': page_title,
'context_instance': context_instance}
return render(request, template, context)
def show_category(request, category_slug):
template = "catalog/category.html"
context_instance = RequestContext(request)
c = get_object_or_404(Category, slug=category_slug)
products = c.product_set.all()
page_title = c.name
meta_keywords = c.meta_keywords
meta_description = c.meta_description
context = {
'context_instance': context_instance,
'c': c,
'products': products,
'page_title': page_title,
'meta_keywords': meta_keywords,
'meta_description': meta_description,
}
return render(request, template, context)
def show_product(request, product_slug):
p = get_object_or_404(Product, slug=product_slug)
context_instance = RequestContext(request)
categories = p.categories.filter(is_active=True)
page_title = p.name
meta_keywords = p.meta_keywords
meta_description = p.meta_description
if request.method == 'POST':
postdata = request.POST.copy()
form = ProductAddToCartForm(request, postdata)
if form.is_valid():
cart.add_to_cart(request)
if request.session.test_cookie_worked():
request.session.delete_test_cookies()
return redirect('cart: show_cart')
else:
form = ProductAddToCartForm(request=request, label_suffix=':')
form.fields['product_slug'].widget.attrs['value'] = product_slug
request.session.set_test_cookie()
context = {
'context_instance': context_instance,
'p': p,
'categories': categories,
'page_title': page_title,
'meta_keywords': meta_keywords,
'meta_description': meta_description,
'form':form
}
template = "catalog/product.html"
return render(request, template, context)
urls.py:
from django.urls import path
from catalog import views
app_name = 'catalog'
urlpatterns = [
path('', views.index, name='index'),
path('<category_slug>/', views.show_category, name='show_category'),
path('product/<product_slug>', views.show_product, name='show_product'),
]
template that the form is embedded in
{% extends "catalog.html" %}
{% block content %}
{% block sidebar %}
{% endblock %}
<div class="container mt-2 pl-5">
<div class="row text-justify ml-5 pl-5">
<div class="col-md img-responsive">
<img src="{{ p.image.url }}" alt="{{ p.name }}" class="img-responsive
center-block img-thumbnail" width="100%"/>
</div>
<div class="col-md">
<h4>{{ p.name }}</h4>
Brand: <em>{{ p.brand }}</em>
<br /><br />
SKU: {{ p.sku }}
<br />
In categor{{ categories.count|pluralize:"y,ies" }}:
{% for c in categories %}
{{ c.name }}
{% if not forloop.last %}, {% endif %}
{% endfor %}
<br /><br />
{% if p.sale_price %}
Was: <s>$ {{ p.old_price }}</s>
<br />
Now: $ {{ p.price }}
{% else %}
Price: $ {{ p.price }}
{% endif %}
<br /><br />
<form method="POST" action=".">
{{ form.as_p }}
{% csrf_token %}
<br />
<input type="submit" value="Add To Cart" name="submit" alt="Add To Cart"
/>
</form>
</div>
</div>
<div class=" text-center pt-5 mb-5 pb-5">
<h3>Product Description</h3>
{{ p.description }}
</div>
</div>
{% endblock %}
whenever i click the add to cart, i get this error:
Page not found (404)
Request Method: POST
Request URL:http://127.0.0.1:8000/catalog/product/
Raised by:catalog.views.show_category
As you can see in your error message
Raised by:catalog.views.show_category
catalog.views.show_category raised the error, so since it is a 404 it is probably raised by
c = get_object_or_404(Category, slug=category_slug)
Now, it is trying to search a category... Why? The url is http://127.0.0.1:8000/catalog/product/
Let's check your paths
path('<category_slug>/', views.show_category, name='show_category'),
Interesting, so any path followed by a slash calls show_category! Not what you would want, that means that http://127.0.0.1:8000/catalog/product/ is trying to show a category with slug name 'product'!!
Now there are multiple ways to fix it. All of them involve restructuring your paths (and changing the rest of the application accordingly)

Categories