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)
Related
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"),
]
I am trying to use here Django dependent dropdown list. But at the same time, the error occurred. If I make an ajax request click the product category and the dependent dropdown not working togetherly and cannot make an ajax request shows an error. How to resolve this error? Can please someone help me to clear this issue.
Models.py
from django.db import models
class ProductCategory(models.Model):
name = models.CharField(max_length=30)
def __str__(self):
return self.name
class SubCategory(models.Model):
country = models.ForeignKey(ProductCategory, on_delete=models.CASCADE)
name = models.CharField(max_length=30)
def __str__(self):
return self.name
class Vendor(models.Model):
designer_name = models.CharField(max_length=100, default='')
design_name = models.CharField(max_length=200)
description = models.TextField(max_length=5000)
productcategory = models.ForeignKey(ProductCategory, on_delete=models.SET_NULL,
null=True)
subcategory = models.ForeignKey(SubCategory, on_delete=models.SET_NULL, null=True)
def __str__(self):
return self.designer_name
forms.py
from django import forms
from .models import Vendor, ProductCategory, SubCategory
class DesignerForm(forms.ModelForm):
class Meta:
model = Vendor
descr = forms.CharField( widget=forms.Textarea )
fields = ('designer_name','design_name', 'description', 'productcategory', 'subcategory')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['subcategory'].queryset = SubCategory.objects.none()
if 'productcategory' in self.data:
try:
productcategory_id = int(self.data.get('productcategory'))
self.fields['subcategory'].queryset = SubCategory.objects.filter(productcategory_id=productcategory_id).order_by('name')
except (ValueError, TypeError):
pass # invalid input from the client; ignore and fallback to empty City queryset
elif self.instance.pk:
self.fields['subcategory'].queryset = self.instance.productcategory.subcategory_set.order_by('name')
Views.py
from .models import *
from django.shortcuts import render, redirect
from .forms import *
def Products(request):
if request.method == 'POST':
form = DesignerForm(request.POST)
if form.is_valid():
form.save()
return redirect('Products')
else:
form = DesignerForm()
return render(request, 'jinja2/products.jinja',{'form': form})
def ajax_load_subcategories(request):
productcategory_id = request.GET.get('productcategory')
subcategories =
SubCategory.objects.filter(productcategory_id=productcategory_id).order_by('name')
return render(request, 'jinja2/city_dropdown_list_options.jinja', {'subcategories':
subcategories})
admin.py
from django.contrib import admin
from boutique_store.models import Vendor, ProductCategory, SubCategory
# Register your models here.
admin.site.register(ProductCategory)
admin.site.register(SubCategory)
admin.site.register(Vendor)
urls.py
from django.urls import include, path
from . import views
urlpatterns = [
path('add/', views.Products, name='Products'),
path('ajax/load_subcategories/', views.ajax_load_subcategories,
name='ajax_load_subcategories'), # <-- this one here
]
products.jinja
{% block content %}
<h2>Person Form</h2>
<form method="post" id="designerForm" data-subcategories-url="{% URL
'ajax_load_subcategories' %}" novalidate>
{% csrf_token %}
<table>
{{ form.as_table }}
</table>
<button type="submit">Save</button>
</form>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"
integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous">
</script>
<script>
$("#id_productcategory").change(function () {
var url = $("#designerForm").attr("data-subcategories-url");
var productcategoryId = $(this).val();
$.ajax({
url: URL,
data: {
'productcategory': productcategoryId
},
success: function (data) {
$("#id_subcategory").html(data);
}
});
});
</script>
{% endblock %}
city_dropdown_list_options.jinja
<option value="">---------</option>
{% for subcategory in subcategories %}
<option value="{{ subcategory.pk }}">{{ subcategory.name }}</option>
{% endfor %}
In SubCategory model you have a field named country which refers to ProductCategory. You can't filter with productcategory_id because there is no such column/field in current model.
class SubCategory(models.Model):
country = models.ForeignKey(ProductCategory, on_delete=models.CASCADE)
name = models.CharField(max_length=30)
def __str__(self):
return self.name
So try to fix your queries like below in forms.py and views.py :
SubCategory.objects.filter(country_id=productcategory_id).order_by('name')
I am following this tutorial
I have gone back and written the code to match exactly. I have another form that works called category_add which is exactly the same as this form. But for the life of me I cannot figure out why bookmark_add doesn't update the database with the form entries.
Models.py
from django.db import models
from django.contrib.auth.models import User
class Category(models.Model):
title = models.CharField(max_length=255)
description = models.TextField(blank=True, null=True)
created_by = models.ForeignKey(User, related_name='categories', on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
verbose_name_plural = 'Categories'
def __str__(self):
return self.title
class Bookmark(models.Model):
category = models.ForeignKey(Category, related_name='bookmarks', on_delete=models.CASCADE)
title = models.CharField(max_length=255)
description = models.TextField(blank=True, null=True)
url = models.CharField(max_length=255, blank=True)
created_by = models.ForeignKey(User, related_name='bookmarks', on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
View.py
from django.shortcuts import render, redirect
from django.contrib.auth.decorators import login_required
from .forms import BookmarkForm
#login_required
def bookmark_add(request, category_id):
if request.method == 'POST':
form = BookmarkForm(request.POST)
if form.is_valid():
bookmark = form.save(commit=False)
bookmark.created_by = request.user
bookmark.category_id = category_id
bookmark.save()
return redirect('category', category_id=category_id)
else:
form = BookmarkForm()
context = {
'form': form
}
return render(request, 'bookmark/bookmark_add.html', context)
Forms.py
from django.forms import ModelForm
from .models import Bookmark
class BookmarkForm(ModelForm):
class Meta:
model = Bookmark
fields = ['title', 'description', 'url']
Urls.py
path('', dashboard, name='dashboard'),
path('categories/', categories, name='categories'),
path('categories/add/', category_add, name='category_add'),
path('categories/<int:category_id>/', category, name='category'),
path('categories/<int:category_id>/add_bookmark', bookmark_add, name='bookmark_add')
]
bookmark_add.html
{% extends 'core/base.html' %}
{% block content %}
<div class="container">
<h1 class="title">Add link</h1>
<form method="post" action=".">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="button is-primary">Submit</button>
</form>
</div>
{% endblock %}
Solved this!
This was a dumb issue and an oversight on my end. Thanks to the content creator on youtube. I just needed to append "/" to the url path for add_bookmark.
Problem:
path('categories/<int:category_id>/add_bookmark', bookmark_add, name='bookmark_add')
The Fix:
path('categories/<int:category_id>/add_bookmark/', bookmark_add, name='bookmark_add')
Im trying to let user create a post and select a category, but when it selects a category and I post it, it gives me the error: Exception Value: 'NoneType' object has no attribute 'add'.
If i go to my database, whatever I type, it is save except the category_id which is null.
This is my code
views.py:
#login_required(login_url='login')
def userposts_create_view(request):
form= UserPostForm(request.POST or None)
#print(request.POST)
if request.POST:# form.is_valid():
#data = form.cleaned_data
#print(data)
#categories = data.pop('categories', None)
categories = request.POST['categories']
content = request.POST['content']
title = request.POST['title']
public = False
if 'public' in request.POST and request.POST['public'] == 'on':
public = False
else:
public=True
#print(f"{categories}, {content}, {title}, {public}")
#user_post = UserPost.objects.create(**data, user=request.user)
user_post = UserPost.objects.create(content=content, title=title, public=public, user=request.user)#, categories=categories)
categories = Categories.objects.filter(name=Categories.name)
print(categories)
user_post.categories.get(categories)
return HttpResponseRedirect("/posted/")
context= {'form': form}
return render(request, 'posts/userposts-create-view.html', context)
#list view
#login_required(login_url='login')
def userposts_list_view(request):
allposts= UserPost.objects.all()
context= {'allposts': allposts,
}
return render(request, 'posts/userposts-list-view.html', context)
#detail view
#login_required(login_url='login')
def userposts_detail_view(request, id=None):
post= get_object_or_404(UserPost, id=id)
context= {'post': post,
}
return render(request, 'posts/userposts-detail-view.html', context)
I assume the error must be in the models but not sure...
Models.py
class Categories(models.Model):
name = models.CharField(max_length=100, verbose_name='Nombre')
def __str__(self):
return self.name
User= settings.AUTH_USER_MODEL
class UserPost(models.Model):
user= models.ForeignKey(User, null=False, editable=False, verbose_name='Usuario', on_delete=models.CASCADE)
title= models.CharField(max_length=500, null=False)
content= models.TextField(null=False)
categories = models.ForeignKey(Categories, null=True, blank=True, on_delete=models.CASCADE)
public = models.BooleanField(verbose_name='Privada?',default=True)
created_at = models.DateTimeField(auto_now_add=True, verbose_name='Creado el ')
updated_at = models.DateTimeField(auto_now=True, verbose_name='Actualizado el ')
def __str__(self):
return self.title + ' | ' + str(self.user)
def save(self, *args, **kwargs):
super(UserPost, self).save(*args, **kwargs)
Forms.py
from django import forms
from .models import UserPost
class UserPostForm(forms.ModelForm):
class Meta:
model= UserPost
fields= ["title", "content","categories","public"]
create_view.html
{%extends 'layouts/layout.html'%}
<html>
<head>
<meta charset="UTF-8">
{% block title %}
Post
{% endblock title %}
</head>
<body>
<h1>Create Post</h1>
{% block content %}
<form enctype="multipart/form-data" method="POST" action="">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Post"/>
</form>
{% endblock content %}
urls.py
from django.urls import path
from . import views
from django.conf.urls import url, include
urlpatterns = [
path('articulos/', views.list, name = "list_articles"), #se utilizaria page para el menu
path('categoria/<int:category_id>', views.category, name="category"),
path('articulo/<int:article_id>', views.article, name="article"),
path('create/', views.userposts_create_view, name='create'),
path('posted/', views.userposts_list_view, name='list_view'),
path('posted/<int:id>', views.userposts_detail_view, name='one_posts'),
path('tus_ideas', views.index_page, name='tus_ideas')
]
please try this:
#login_required(login_url='login')
def userposts_create_view(request):
form= UserPostForm(request.POST or None)
if request.POST:
categories = request.POST['categories']
content = request.POST['content']
title = request.POST['title']
public = False
if 'public' in request.POST and request.POST['public'] == 'on':
public = False
else:
public=True
user_post = UserPost.objects.create(content=content, title=title, public=public, user=request.user,categories_id=categories)
return HttpResponseRedirect("/posted/")
context= {'form': form}
return render(request, 'posts/userposts-create-view.html', context)
user_post.categories.add(*categories) would work if categories is a ManyToMany field in your UserPost model whereas it it a ForeignKey in your code.
Change your categories fields to :
class UserPost(models.Model):
...
categories = models.ManyToManyField('Categories', null=True, blank=True)
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.