Hi, i hope somebody can give me an advice or help.
I am creating a web shop with django.
What do we need?
To have all category slugs in the url for any child category, not just a child but any sub sub category.
Example:
I created one category:
http://127.0.0.1:8000/shop/category/main/
Then i want to create a new category “sub1” and to have this “main category” as a parent, so it looks like this:
http://127.0.0.1:8000/shop/category/main/sub1/
We have this setup:
These are the models
from django.db import models
from django.conf import settings
from django.urls import reverse
from django.utils.safestring import mark_safe
import uuid
from django.urls import reverse
def file_upload_path(instance, filename):
return f'user-{instance.id}/shop/images/{filename}'
# Product Category
class ProductCategory(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
title = models.CharField(max_length=100, unique=True)
slug = models.SlugField(max_length=110, unique=True)
description = models.TextField(null=True, blank=True)
image = models.ImageField(upload_to=file_upload_path, default='default-images/demo.jpg')
#id = models.UUIDField(default=uuid.uuid4, unique=True, primary_key=True, editable=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
# relations
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='shop_categories')
# parent category
parent_category = models.ForeignKey('self', blank=True, null=True, related_name='children_categories', on_delete=models.CASCADE)
def __str__(self):
return self.title
'''def get_absolute_url(self):
return reverse('shop:view_product_category', args=[str(self.slug)])'''
def get_absolute_url(self):
if self.parent_category:
return reverse('front:category_detail_child', kwargs={'parent_slug': self.parent_category.slug, 'child_slug': self.slug})
else:
return reverse('front:category_detail_parent', kwargs={'parent_slug': self.slug})
def clean(self):
self.title = self.title.capitalize()
self.slug = self.slug.lower()
def category_image(self):
return mark_safe(f"<img src='{self.image.url}' width='50' height='50'/>")
class Meta:
verbose_name_plural = 'Categories'
# Product
class Product(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
STATUS_CHOICES = (
('draft','Draft'),
('published','Published'),
)
title = models.CharField(max_length=100, unique=True)
slug = models.SlugField(max_length=110, unique=True)
price = models.DecimalField(max_digits=8, decimal_places=2)
image = models.ImageField(upload_to=file_upload_path, default='default-images/demo.jpg')
tizer = models.TextField(max_length=200, null=True, blank=True)
content = models.TextField(null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
is_featured = models.BooleanField(default=False)
is_new = models.BooleanField(default=False)
status = models.CharField(max_length=15, choices=STATUS_CHOICES, default='published')
#sku = ShortUUIDField(length=12, max_length=20, prefix='sku', alphabet='abcdefgh1234567')
# relations
category = models.ForeignKey(ProductCategory, on_delete=models.CASCADE, related_name='products')
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='products')
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('front:view_product', args=[str(self.slug)])
def product_image(self):
return mark_safe(f'<img src="{self.image.url}" width="50" height="50"/>')
def clean(self):
self.title = self.title.capitalize()
self.slug = self.slug.lower()
class Meta:
ordering = ('-created_at',)
# Order Model
class Order(models.Model):
class OrderStatus(models.TextChoices):
CONFIRMED = 'CONFIRMED', 'Confirmed'
CANCELED = 'CANCELED', 'Canceled'
IN_PROCESS = 'IN_PROCESS', 'In process'
# ID
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
# ID for tracking
order_id = models.CharField(max_length=8, unique=True, editable=False)
#order_id = models.UUIDField(default=uuid.uuid4, max_length=10, editable=False, unique=True)
full_name = models.CharField(max_length=100)
email = models.EmailField(max_length=100)
shipping_address = models.CharField(max_length=200)
amount_paid = models.DecimalField(max_digits=7, decimal_places=2)
date_ordered = models.DateTimeField(auto_now_add=True)
# relation to user
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=True, blank=True)
# order status - trigger emails
status = models.CharField(max_length=30, choices=OrderStatus.choices, default=OrderStatus.IN_PROCESS)
# relation to delivery option
delivery_option = models.ForeignKey('DeliveryOption', on_delete=models.CASCADE)
# delivery message
delivery_message = models.ForeignKey('OrderMessage', on_delete=models.CASCADE, null=True, blank=True)
def __str__(self):
return f'Order {self.id}'
class Meta:
ordering = ['-date_ordered']
def save(self, *args, **kwargs):
if not self.order_id:
self.order_id = str(uuid.uuid4())[:8]
super().save(*args, **kwargs)
class OrderItem(models.Model):
quantity = models.PositiveBigIntegerField(default=1)
price = models.DecimalField(max_digits=7, decimal_places=2)
# relations
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=True, blank=True)
order = models.ForeignKey(Order, on_delete=models.CASCADE, related_name='items')
product = models.ForeignKey(Product, on_delete=models.CASCADE)
def get_item_total(self):
total = self.quantity * self.price
return total
def __str__(self):
return f'Order item: #{self.id}'
# Shipping address model - for logged in users who made a purchase or filled in this form, shipping info will be automatically filled in on the checkout page
class ShippingAddress(models.Model):
full_name = models.CharField(max_length=100)
email_address = models.EmailField(max_length=100)
#phone_number = models.CharField(max_length=20)
address1 = models.CharField(max_length=200)
city = models.CharField(max_length=100)
zipcode = models.CharField(max_length=10)
state = models.CharField(max_length=100, null=True, blank=True)
# relation to user
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=True, blank=True)
# relation to delivery option
#delivery_option = models.ForeignKey('DeliveryOption', on_delete=models.CASCADE, null=True, blank=True)
def __str__(self):
return f'Shipping Address: {str(self.id)}'
class Meta:
verbose_name_plural = 'Shipping Address'
# Delivery options
class DeliveryOption(models.Model):
name = models.CharField(max_length=100)
price = models.DecimalField(max_digits=7, decimal_places=2)
note = models.TextField(max_length=300)
# relation to Order
def __str__(self):
return self.name
# Order Message
class OrderMessage(models.Model):
content = models.TextField(max_length=300, null=True, blank=True)
def __str__(self):
return self.content
I have this view for rendering one product category and it checks for parent child:
def category_detail(request, parent_slug, child_slug=None):
if child_slug:
# Ako postoji child_slug, prikaži detinjsku kategoriju unutar roditeljske kategorije
parent_category = get_object_or_404(ProductCategory, slug=parent_slug)
category = get_object_or_404(ProductCategory, slug=child_slug, parent_category=parent_category)
else:
# Ako nema child_slug, prikaži roditeljsku kategoriju
category = get_object_or_404(ProductCategory, slug=parent_slug, parent_category=None)
products = Product.objects.filter(category=category)
context = {
'products':products
}
return render(request, 'shop/frontend/categories/view.html', context)
Urls are as follows:
# If child shop category
path('shop/category/<slug:parent_slug>/<slug:child_slug>/', shop_views.category_detail, name='category_detail_child'),
# If parent shop category
path('shop/category/<slug:parent_slug>/', shop_views.category_detail, name='category_detail_parent'),
That is all working fine.
But the problem is when i want to add deeper category relation. So for a child category i want to assign as a parent to some category.
So i add a new category named “Sub2” and we need this url:
http://127.0.0.1:8000/shop/category/main/sub1/sub2
But currently it shows (with this setup that is):
http://127.0.0.1:8000/shop/category/sub1/sub2/
So it doesn’t show all parent categories before that.
I hope you can understand the problem and maybe help.
I tried with chatgpt for days now, but it wasn’t able to do it.
If i need to change anything i will, i am open to all advices and help, this was just my effort with my current Django knowlegde
Thank you