I wanna be able to search a product with the category that isnt directly linked to it but the category is grandfather or great_great grandfather and so on above on the category tree of the category that is actually linked to the product itself. The only way i could implement was with the following which is ofcourse not scalable and proper way to do it:
def get_queryset(self, request):
queryset = super().get_queryset(request).select_related('brand', 'category', 'category__parent')
category_name = request.GET.get('category__name', None)
if category_name:
# Filter the queryset to include products with the specified category or its ancestors
queryset = queryset.filter(
Q(category__name=category_name) |
Q(category__parent__name=category_name) |
Q(category__parent__parent__name=category_name) |
# Add more levels as needed based on the depth of your category tree
)
return queryset
And here are the code as of now:
Product``models.py
class Product(models.Model):
name = models.CharField(max_length=255)
slug = models.SlugField(blank=True, null=True)
description = models.TextField()
price = models.DecimalField(max_digits=10, decimal_places=2)
image = models.ImageField(upload_to='product_images/')
category = models.ForeignKey(Category, on_delete=models.SET_NULL,related_name='product_category', null=True, blank=True)
brand = models.ForeignKey(Brand, on_delete=models.SET_NULL, related_name='product_brand', null=True, blank=True)
tags = GenericRelation(TaggedItem, related_query_name='product')
ratings = GenericRelation(Rating, related_query_name='product')
active = models.BooleanField(default=True)
timestamp = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
state = models.CharField(max_length=2, choices=PublishStateOptions.choices, default=PublishStateOptions.DRAFT)
publish_timestamp = models.DateTimeField(
# means that the publish_timestamp field will not automatically be set to the current date and time when a new instance of this model is created.
auto_now_add=False,
# means that the publish_timestamp field will not automatically be updated to the current date and time every time the model is saved.
auto_now=False,
blank=True,
null=True
)
def __str__(self):
return self.name
Category``models.py
class Category(models.Model):
name = models.CharField(max_length=255)
parent = models.ForeignKey('self', on_delete=models.SET_NULL, related_name='subcategories', null=True, blank=True)
class Meta:
verbose_name_plural = 'Categories'
def __str__(self):
return self.name
Product``admin.py
class ProductAdmin(admin.ModelAdmin):
list_display = ['name', 'price', 'brand', 'category']
inlines = [TaggedItemInline]
list_filter = [
'brand__name',
'category__name'
]
search_fields = ['name', 'price', 'brand__name', 'category__name', 'category__parent__name']
def get_queryset(self, request):
return super().get_queryset(request).select_related('brand', 'category', 'category__parent')
def get_brand_name(self, obj):
return obj.brand.name if obj.brand else None
def get_category_name(self, obj):
return obj.category.name if obj.category else None
get_brand_name.short_description = 'Brand'
get_category_name.short_description = 'Category'
# Define the labels for list filters
list_filter = (
('brand', admin.RelatedOnlyFieldListFilter),
('category', admin.RelatedOnlyFieldListFilter),
)
admin.site.register(Product, ProductAdmin)
After an hour, I figured it out.
When users search for products, they can now use the names of the product's category's ascendants. The get_search_results() method is customized to achieve this. When a user searches, the code identifies categories that match the search term and then recursively collects all ancestors of these categories. This ensures that products from categories and their ascendants are included in the search results.