diff --git a/backend/blog/urls.py b/backend/blog/urls.py index ca90f37..07d635b 100644 --- a/backend/blog/urls.py +++ b/backend/blog/urls.py @@ -3,5 +3,5 @@ from . import views urlpatterns = [ path('all', views.AllBlogView.as_view(), name='product-chat-view'), - path('', views.BlogView.as_view(), name='product-chat-view'), + path('', views.BlogView.as_view(), name='product-chat-view'), ] \ No newline at end of file diff --git a/backend/blog/views.py b/backend/blog/views.py index f450023..fb503cb 100644 --- a/backend/blog/views.py +++ b/backend/blog/views.py @@ -64,12 +64,12 @@ class BlogView(APIView): return ip - def get(self, request, pk): - blog = get_object_or_404(BlogModel, pk=pk) + def get(self, request, slug): + blog = get_object_or_404(BlogModel, slug=slug) if blog.is_published: # Track views using session client_ip = self.get_client_ip(request) - session_key = f'viewed_blog_{pk}_{client_ip}' + session_key = f'viewed_blog_{slug}_{client_ip}' if not request.session.get(session_key): blog.views += 1 diff --git a/backend/core/settings/unfold_conf.py b/backend/core/settings/unfold_conf.py index a143954..0055b12 100644 --- a/backend/core/settings/unfold_conf.py +++ b/backend/core/settings/unfold_conf.py @@ -160,8 +160,12 @@ UNFOLD = { "title": _("زیر دسته بندی"), "icon": "category", "link": reverse_lazy("admin:product_subcategorymodel_changelist"), - } - + }, + { + "title": _("دسته بندی پورسانتی"), + "icon": "devices", + "link": reverse_lazy("admin:home_showcaseslider_changelist"), + }, ], }, { @@ -186,12 +190,7 @@ UNFOLD = { "icon": "newsmode", "link": reverse_lazy("admin:blog_blogmodel_changelist"), }, - - { - "title": _("نمایش کیس ها"), - "icon": "devices", - "link": reverse_lazy("admin:home_showcaseslider_changelist"), - }, + ], }, diff --git a/backend/home/migrations/0015_alter_slidermodel_options_remove_slidermodel_link.py b/backend/home/migrations/0015_alter_slidermodel_options_remove_slidermodel_link.py new file mode 100644 index 0000000..e89c7af --- /dev/null +++ b/backend/home/migrations/0015_alter_slidermodel_options_remove_slidermodel_link.py @@ -0,0 +1,21 @@ +# Generated by Django 5.1.2 on 2025-05-18 10:45 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('home', '0014_homeimagemodel_video1_homeimagemodel_video2_and_more'), + ] + + operations = [ + migrations.AlterModelOptions( + name='slidermodel', + options={'verbose_name': 'دسته بندی پورسانت', 'verbose_name_plural': 'دسته بندی پورسانت'}, + ), + migrations.RemoveField( + model_name='slidermodel', + name='link', + ), + ] diff --git a/backend/home/migrations/0016_alter_slidermodel_options_slidermodel_link.py b/backend/home/migrations/0016_alter_slidermodel_options_slidermodel_link.py new file mode 100644 index 0000000..db6124c --- /dev/null +++ b/backend/home/migrations/0016_alter_slidermodel_options_slidermodel_link.py @@ -0,0 +1,23 @@ +# Generated by Django 5.1.2 on 2025-05-18 11:45 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('home', '0015_alter_slidermodel_options_remove_slidermodel_link'), + ] + + operations = [ + migrations.AlterModelOptions( + name='slidermodel', + options={'verbose_name': 'اسلایدر', 'verbose_name_plural': 'اسلایدر ها'}, + ), + migrations.AddField( + model_name='slidermodel', + name='link', + field=models.URLField(default='', verbose_name='لینک'), + preserve_default=False, + ), + ] diff --git a/backend/home/migrations/0017_remove_showcaseslider_link.py b/backend/home/migrations/0017_remove_showcaseslider_link.py new file mode 100644 index 0000000..ae6a399 --- /dev/null +++ b/backend/home/migrations/0017_remove_showcaseslider_link.py @@ -0,0 +1,17 @@ +# Generated by Django 5.1.2 on 2025-05-18 11:47 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('home', '0016_alter_slidermodel_options_slidermodel_link'), + ] + + operations = [ + migrations.RemoveField( + model_name='showcaseslider', + name='link', + ), + ] diff --git a/backend/home/models.py b/backend/home/models.py index d59f71a..d90318d 100644 --- a/backend/home/models.py +++ b/backend/home/models.py @@ -1,5 +1,4 @@ from django.db import models -from product.models import ProductModel from django.urls import reverse class SliderModel(models.Model): @@ -42,7 +41,6 @@ class HomeImageModel(models.Model): class ShowCaseSlider(models.Model): title = models.CharField(max_length=30, verbose_name='عنوان') description = models.CharField(max_length=150, verbose_name='توضیحات') - link = models.URLField(verbose_name='لینک') image = models.ImageField(upload_to='show_case/', verbose_name='عکس') def __str__(self): return self.title diff --git a/backend/home/views.py b/backend/home/views.py index 28241e8..eeb8e40 100644 --- a/backend/home/views.py +++ b/backend/home/views.py @@ -1,7 +1,7 @@ from django.shortcuts import render, get_object_or_404, redirect from rest_framework.views import APIView, Response -from product.models import ProductModel, SubCategoryModel, DollorModel -from product.serializers import SubCategorySerializer, DynamicProductSerializer +from product.models import ProductModel, SubCategoryModel, MainCategoryModel +from product.serializers import SubCategorySerializer, DynamicProductSerializer, MainCategorySerializer from .serializers import * from .models import * from rest_framework import status @@ -24,8 +24,8 @@ class HomeView(APIView): sliders = SliderModel.objects.all() slider_ser = SliderSerializer(instance=sliders, many=True, context={'request': request}) - sub_categories = SubCategoryModel.objects.filter(show=True) - sub_category_ser = SubCategorySerializer(instance=sub_categories, many=True, context={'request': request}) + main_categories = MainCategoryModel.objects.all() + main_category_ser = MainCategorySerializer(instance=main_categories, many=True, context={'request': request}) products_to_show = ProductModel.objects.filter(show=True) product_ser = DynamicProductSerializer(instance=products_to_show, many=True, context={'request': request, 'view_type': 'list'}) @@ -38,7 +38,7 @@ class HomeView(APIView): response = { 'sliders': slider_ser.data, - 'sub_categories': sub_category_ser.data, + 'main_categories': main_category_ser.data, 'products': product_ser.data, 'difreance_section': home_image_ser.data, 'show_case_slider': show_cases_ser.data diff --git a/backend/product/admin.py b/backend/product/admin.py index 1f1b709..9fe5c5f 100644 --- a/backend/product/admin.py +++ b/backend/product/admin.py @@ -153,7 +153,7 @@ class ProductVariantInLine(StackedInline): readonly_fields = ['price'] # inlines = [DetailModelInLine] autocomplete_fields = ['product_attributes', 'in_pack_items', 'images', 'details'] - fields = ['images', 'video','input_price', 'min_price', 'currency', 'price', 'discount','in_stock', 'color', 'product_attributes', 'in_pack_items', 'details','sell'] + fields = ['images', 'video','input_price', 'min_price', 'currency', 'price', 'discount','in_stock', 'color', 'product_attributes', 'in_pack_items', 'details', 'sell', 'slider_category'] # search_fields = [''] @@ -260,10 +260,10 @@ class MainCategoryModelAdmin(ModelAdmin, ImportExportModelAdmin): @admin.register(SubCategoryModel) class SubCategoryModelAdmin(ModelAdmin, ImportExportModelAdmin): - list_display = ['name', 'parent', 'show'] + list_display = ['name', 'parent'] search_fields = ['name', 'slug'] - list_filter = ['parent', 'show', ] + list_filter = ['parent', ] import_form_class = ImportForm export_form_class = ExportForm diff --git a/backend/product/migrations/0041_alter_detailmodel_detail_model.py b/backend/product/migrations/0041_alter_detailmodel_detail_model.py new file mode 100644 index 0000000..4a71a7b --- /dev/null +++ b/backend/product/migrations/0041_alter_detailmodel_detail_model.py @@ -0,0 +1,19 @@ +# Generated by Django 5.1.2 on 2025-04-24 22:25 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('product', '0040_remove_detailmodel_name'), + ] + + operations = [ + migrations.AlterField( + model_name='detailmodel', + name='detail_model', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='details', to='product.productdetailmodel', verbose_name='دسته بندی جزيات'), + ), + ] diff --git a/backend/product/migrations/0042_productvariant_porsant_category.py b/backend/product/migrations/0042_productvariant_porsant_category.py new file mode 100644 index 0000000..7643dd7 --- /dev/null +++ b/backend/product/migrations/0042_productvariant_porsant_category.py @@ -0,0 +1,20 @@ +# Generated by Django 5.1.2 on 2025-05-18 10:56 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('home', '0015_alter_slidermodel_options_remove_slidermodel_link'), + ('product', '0041_alter_detailmodel_detail_model'), + ] + + operations = [ + migrations.AddField( + model_name='productvariant', + name='porsant_category', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='home.slidermodel', verbose_name='دسته بندی پورسانتی'), + ), + ] diff --git a/backend/product/migrations/0043_rename_porsant_category_productvariant_slider_category.py b/backend/product/migrations/0043_rename_porsant_category_productvariant_slider_category.py new file mode 100644 index 0000000..a67d07a --- /dev/null +++ b/backend/product/migrations/0043_rename_porsant_category_productvariant_slider_category.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1.2 on 2025-05-18 11:01 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('product', '0042_productvariant_porsant_category'), + ] + + operations = [ + migrations.RenameField( + model_name='productvariant', + old_name='porsant_category', + new_name='slider_category', + ), + ] diff --git a/backend/product/migrations/0044_alter_productvariant_slider_category.py b/backend/product/migrations/0044_alter_productvariant_slider_category.py new file mode 100644 index 0000000..5bf8022 --- /dev/null +++ b/backend/product/migrations/0044_alter_productvariant_slider_category.py @@ -0,0 +1,20 @@ +# Generated by Django 5.1.2 on 2025-05-18 11:47 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('home', '0017_remove_showcaseslider_link'), + ('product', '0043_rename_porsant_category_productvariant_slider_category'), + ] + + operations = [ + migrations.AlterField( + model_name='productvariant', + name='slider_category', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='home.showcaseslider', verbose_name='دسته بندی پورسانتی'), + ), + ] diff --git a/backend/product/migrations/0045_remove_subcategorymodel_show_maincategorymodel_image.py b/backend/product/migrations/0045_remove_subcategorymodel_show_maincategorymodel_image.py new file mode 100644 index 0000000..4dd979a --- /dev/null +++ b/backend/product/migrations/0045_remove_subcategorymodel_show_maincategorymodel_image.py @@ -0,0 +1,22 @@ +# Generated by Django 5.1.2 on 2025-05-21 10:37 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('product', '0044_alter_productvariant_slider_category'), + ] + + operations = [ + migrations.RemoveField( + model_name='subcategorymodel', + name='show', + ), + migrations.AddField( + model_name='maincategorymodel', + name='image', + field=models.ImageField(blank=True, null=True, upload_to='category_model/', verbose_name='عکس'), + ), + ] diff --git a/backend/product/migrations/0046_maincategorymodel_video.py b/backend/product/migrations/0046_maincategorymodel_video.py new file mode 100644 index 0000000..eeb22f1 --- /dev/null +++ b/backend/product/migrations/0046_maincategorymodel_video.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1.2 on 2025-05-21 11:19 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('product', '0045_remove_subcategorymodel_show_maincategorymodel_image'), + ] + + operations = [ + migrations.AddField( + model_name='maincategorymodel', + name='video', + field=models.FileField(blank=True, null=True, upload_to='product_videos/', verbose_name='ویدیو'), + ), + ] diff --git a/backend/product/migrations/0047_alter_maincategorymodel_video.py b/backend/product/migrations/0047_alter_maincategorymodel_video.py new file mode 100644 index 0000000..9085cd4 --- /dev/null +++ b/backend/product/migrations/0047_alter_maincategorymodel_video.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1.2 on 2025-05-21 11:20 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('product', '0046_maincategorymodel_video'), + ] + + operations = [ + migrations.AlterField( + model_name='maincategorymodel', + name='video', + field=models.FileField(blank=True, null=True, upload_to='category_videos/', verbose_name='ویدیو'), + ), + ] diff --git a/backend/product/models.py b/backend/product/models.py index 7d8c160..fa404df 100644 --- a/backend/product/models.py +++ b/backend/product/models.py @@ -5,14 +5,15 @@ from django.urls import reverse import requests from django.utils.translation import gettext_lazy as _ from django.core.exceptions import ValidationError - +from home.models import ShowCaseSlider class MainCategoryModel(models.Model): name = models.CharField(max_length=50, verbose_name='نام دسته بندی') slug = models.SlugField(max_length=50, unique=True, help_text="اسم دسته را برای مسیر به انگلیسی و بدون فاصله وارد کنید") icon = models.ImageField(upload_to='category_model/',verbose_name='آیکون', blank=True, null=True) + image = models.ImageField(upload_to='category_model/',verbose_name='عکس', blank=True, null=True) meta_title = models.CharField(max_length=60, verbose_name="عنوان متا", help_text="عنوان متا برای SEO", blank=True, null=True) meta_description = models.TextField(max_length=160, verbose_name="توضیحات متا", help_text="توضیحات متا برای SEO", blank=True, null=True) - + video = models.FileField(upload_to='category_videos/', blank=True, null=True, verbose_name='ویدیو') class Meta: verbose_name = "دسته‌بندی اصلی" verbose_name_plural = "دسته‌بندی‌هااصلی" @@ -34,7 +35,6 @@ class SubCategoryModel(models.Model): meta_title = models.CharField(max_length=60, verbose_name="عنوان متا", help_text="عنوان متا برای SEO", blank=True, null=True) meta_description = models.TextField(max_length=160, verbose_name="توضیحات متا", help_text="توضیحات متا برای SEO", blank=True, null=True) parent = models.ForeignKey(MainCategoryModel, on_delete=models.CASCADE, related_name='subcategorys', verbose_name='دسته‌بندی والد') - show = models.BooleanField(default=False, verbose_name='نمایش در خانه') class Meta: verbose_name = "زیر دسته‌بندی" @@ -243,6 +243,7 @@ class ProductVariant(models.Model): images = models.ManyToManyField(ProductImageModel, verbose_name='عکس ها') video = models.FileField(upload_to='product_videos/', blank=True, null=True, verbose_name='ویدیو') details = models.ManyToManyField(ProductDetailModel, verbose_name='جزییات محصول', related_name='product') + slider_category = models.ForeignKey(ShowCaseSlider, verbose_name='دسته بندی پورسانتی', blank=True, null=True, on_delete=models.CASCADE) class Meta: verbose_name = 'تنوع محصول' verbose_name_plural = 'تنوع‌های محصول' diff --git a/backend/product/serializers.py b/backend/product/serializers.py index 4d03b07..d6058dc 100644 --- a/backend/product/serializers.py +++ b/backend/product/serializers.py @@ -92,7 +92,7 @@ class SubCategorySerializer(serializers.ModelSerializer): parent = serializers.SerializerMethodField() class Meta: model = SubCategoryModel - fields = ['id', 'name', 'slug','icon', 'meta_title', 'meta_description', 'product_count', 'show', 'parent', 'image'] + fields = ['id', 'name', 'slug','icon', 'meta_title', 'meta_description', 'product_count', 'parent', 'image'] def get_product_count(self, obj): return obj.products.count() def get_parent(self, obj): @@ -103,7 +103,7 @@ class MainCategorySerializer(serializers.ModelSerializer): subcategorys = SubCategorySerializer(many=True) class Meta: model = MainCategoryModel - fields = ['id', 'name', 'slug', 'icon', 'meta_title', 'meta_description', 'subcategorys'] + fields = ['id', 'name', 'slug', 'icon', 'meta_title', 'meta_description', 'subcategorys', 'image', 'video'] class DynamicProductSerializer(serializers.ModelSerializer): @@ -130,12 +130,17 @@ class DynamicProductSerializer(serializers.ModelSerializer): fields = "__all__" view_type = { 'list': ['id','name', 'rating', 'slug', 'category', 'variants', 'colors'], + 'slider': ['id','name', 'rating', 'slug', 'category', 'variants', 'colors'], 'instance': ['id', 'name', 'description', 'rating', 'slug', 'meta_description', 'meta_keywords', 'meta_rating', 'category', 'related_products', 'in_pack_items', 'variants', 'colors'], 'chat': ['id', 'name', 'description', 'variants'] } def get_variants(self, obj): - varients = obj.variants.all() + view_type = self.context.get('view_type') + if view_type == 'slider': + varients = obj.variants.filter(slider_category__isnull=False) + else: + varients = obj.variants.all() colors = set(varient.color for varient in varients) return ProductVariantSerialzier(instance=varients, many=True, context=self.context).data diff --git a/backend/product/urls.py b/backend/product/urls.py index faad234..59cc686 100644 --- a/backend/product/urls.py +++ b/backend/product/urls.py @@ -1,9 +1,11 @@ from django.urls import path -from .views import AllCategories, ProductView, AllProductsView, CommentView +from .views import AllCategories, ProductView, AllProductsView, CommentView, ShowCaseProductsView, ShowCaseCategoryListView urlpatterns = [ path('', AllProductsView.as_view(), name='category-products'), + path('slider_category', ShowCaseProductsView.as_view(), name='category-products'), path('categories', AllCategories.as_view(), name='all-categories'), + path('slider_categories', ShowCaseCategoryListView.as_view(), name='all-categories'), path('', ProductView.as_view(), name='product-detail'), path('comments/', CommentView.as_view(), name='comment-views'), ] \ No newline at end of file diff --git a/backend/product/views.py b/backend/product/views.py index 797b97e..1a17238 100644 --- a/backend/product/views.py +++ b/backend/product/views.py @@ -13,6 +13,8 @@ from rest_framework.permissions import AllowAny from order.serializers import OrderItemSerailzier from order.models import OrderModel from django.db.models import Min, Max +from home.models import ShowCaseSlider +from home.serializers import ShowCaseSliderSerialzier # class APIView(APIView): # def __init__(self, *args, **kwargs): # super().__init__(*args, **kwargs) @@ -53,8 +55,8 @@ class ProductView(APIView): serializer_class = DynamicProductSerializer permission_classes = [AllowAny] # authentication_classes = [] - def get(self, request, pk): - product = get_object_or_404(ProductModel, id=pk) + def get(self, request, slug): + product = get_object_or_404(ProductModel, slug=slug) if request.user.is_authenticated: cart_obj, _ = OrderModel.objects.get_or_create(user=request.user, status='CART') cart_items = cart_obj.items.all() @@ -89,6 +91,182 @@ class AllProductsView(APIView): # ), OpenApiParameter( name="category", + type=OpenApiTypes.STR, + description="slug category (send it with category type)", + required=False, + ), + OpenApiParameter( + name="category_type", + type=OpenApiTypes.STR, + required=False, + enum=['sub', 'main'], + ), + OpenApiParameter( + name="price_gte", + description="Filter products with price greater than or equal to this value.", + required=False, + type=OpenApiTypes.FLOAT, + ), + OpenApiParameter( + name="price_lte", + description="Filter products with price less than or equal to this value.", + required=False, + type=OpenApiTypes.FLOAT, + ), + OpenApiParameter( + name="sort", + description=( + "Sort results by one of the following fields:\n" + "`name`, `-name`, `price`, `-price`, `created_at`, `-created_at`." + "\nPrefix with `-` for descending order." + "remove the price form sorting templory " + ), + required=False, + type=OpenApiTypes.STR, + ), + OpenApiParameter( + name="limit", + description="Number of results to return per page (pagination).", + required=False, + type=OpenApiTypes.INT, + ), + OpenApiParameter( + name="offset", + description="The starting position of the results (pagination).", + required=False, + type=OpenApiTypes.INT, + ), + OpenApiParameter( + name="in_stock", + description="Filter products that are in stock (positive stock).", + required=False, + type=OpenApiTypes.BOOL, + ), + OpenApiParameter( + name="has_discount", + description="Filter products that have a discount.", + required=False, + type=OpenApiTypes.BOOL, + ) + ], + description=( + "Retrieve products with optional filters and sorting. " + "Provide a list of category IDs to filter products by those categories and their subcategories." + ), + responses={ + 200: DynamicProductSerializer(many=True, context={'view_type': 'list'}), + 404: OpenApiTypes.OBJECT, + }, + ) + def get(self, request): + try: + category_slug = request.query_params.get('category') + category_type = request.query_params.get('category_type') + + VALID_CATEGORY_TYPES = {'sub', 'main'} + products = ProductModel.objects.all() + + if category_slug: + if category_type not in VALID_CATEGORY_TYPES: + return Response( + {"detail": "category_type must be one of ['sub', 'main']"}, + status=status.HTTP_400_BAD_REQUEST + ) + + if category_type == 'sub': + sub_category = get_object_or_404(SubCategoryModel, slug=category_slug) + products = ProductModel.objects.filter(category=sub_category) + else: # category_type == 'main' + main_category = get_object_or_404(MainCategoryModel, slug=category_slug) + sub_categories = main_category.subcategorys.all() + products = ProductModel.objects.filter(category__in=sub_categories) + in_stock = request.query_params.get('in_stock') + if in_stock is not None: + if in_stock.lower() == 'true': + products = products.filter(variants__in_stock__gt=0) + elif in_stock.lower() != 'false': + return Response({'detail': 'in_stock must be "true" or "false".'}, status=status.HTTP_400_BAD_REQUEST) + + # Filter by discount + has_discount = request.query_params.get('has_discount') + if has_discount is not None: + if has_discount.lower() == 'true': + products = products.filter(variants__discount__gt=0) + elif has_discount.lower() != 'false': + return Response({'detail': 'has_discount must be "true" or "false".'}, status=status.HTTP_400_BAD_REQUEST) + + # Search filter + search_query = request.query_params.get('search') + if search_query: + products = products.filter(Q(name__icontains=search_query) | Q(description__icontains=search_query)) + + # Price filters + price_gte = request.query_params.get('price_gte') + price_lte = request.query_params.get('price_lte') + + products = products.annotate(min_price=Min('variants__price'), max_price=Max('variants__price')) + + if price_gte: + try: + price_gte = float(price_gte) + products = products.filter(max_price__gte=price_gte) + except ValueError: + return Response({'detail': 'price_gte must be a number.'}, status=status.HTTP_400_BAD_REQUEST) + + if price_lte: + try: + price_lte = float(price_lte) + products = products.filter(min_price__lte=price_lte) + except ValueError: + return Response({'detail': 'price_lte must be a number.'}, status=status.HTTP_400_BAD_REQUEST) + + # Sorting + sort_by = request.query_params.get('sort') + if sort_by in ['name', '-name', 'created_at', '-created_at']: + products = products.order_by(sort_by) + else: + products = products.order_by('name') + + # Pagination + paginator = self.pagination_class() + paginated_products = paginator.paginate_queryset(products, request) + serializer = self.serializer_class( + paginated_products, many=True, + context={'request': request, 'view_type': 'list'} + ) + return paginator.get_paginated_response(serializer.data) + + except MainCategoryModel.DoesNotExist: + return Response({"detail": "Main Category not found."}, status=status.HTTP_404_NOT_FOUND) + + except SubCategoryModel.DoesNotExist: + return Response({"detail": "Sub Category not found."}, status=status.HTTP_404_NOT_FOUND) + +class ShowCaseCategoryListView(APIView): + serializer_class = ShowCaseSliderSerialzier + permission_classes = [AllowAny] + def get(self, request): + categoryes = ShowCaseSlider.objects.all() + categoryes_ser = self.serializer_class(instance=categoryes, many=True, context={'request': request}) + return Response(categoryes_ser.data, status=status.HTTP_200_OK) + + + + +class ShowCaseProductsView(APIView): + serializer_class = DynamicProductSerializer + pagination_class = StructurePagination + authentication_classes = [] + @extend_schema( + parameters=[ + OpenApiParameter( + name="search", + description="Search by product name or description.", + required=False, + type=OpenApiTypes.STR, + ), + OpenApiParameter( + name="slider_category", type=OpenApiTypes.INT, required=False, ), @@ -151,17 +329,18 @@ class AllProductsView(APIView): ) def get(self, request): try: - category_id = request.query_params.get('category', None) + category_id = request.query_params.get('slider_category', None) if category_id: try: category_id = int(category_id) except ValueError: return Response({'detail': 'value error category id should be a number'}, status=status.HTTP_400_BAD_REQUEST) - sub_category = get_object_or_404(SubCategoryModel, pk=category_id) - products = ProductModel.objects.filter(category=sub_category) + slider_category = get_object_or_404(ShowCaseSlider, pk=category_id) + + products = ProductModel.objects.filter(variants__slider_category=slider_category).distinct() else: - products = ProductModel.objects.all() + products = ProductModel.objects.filter(variants__slider_category__isnull=False).distinct() # Filter by stock status if `in_stock` is specified in_stock = request.query_params.get('in_stock', "false") == 'true' @@ -198,13 +377,18 @@ class AllProductsView(APIView): # Pagination paginator = self.pagination_class() paginated_products = paginator.paginate_queryset(products, request) - serializer = self.serializer_class(paginated_products, many=True, context={'request': request, 'view_type': 'list'}) + serializer = self.serializer_class(paginated_products, many=True, context={'request': request, 'view_type': 'slider'}) return paginator.get_paginated_response(serializer.data) except MainCategoryModel.DoesNotExist: return Response({"detail": "Category not found."}, status=status.HTTP_404_NOT_FOUND) + + + + + class CommentView(APIView): serializer_class = CommentSerializer permission_classes = [IsAuthenticatedOrReadOnly]