diff --git a/core/apps/api/migrations/0014_category_name_en_category_name_ru_category_name_uz.py b/core/apps/api/migrations/0014_category_name_en_category_name_ru_category_name_uz.py new file mode 100644 index 0000000..142d2f3 --- /dev/null +++ b/core/apps/api/migrations/0014_category_name_en_category_name_ru_category_name_uz.py @@ -0,0 +1,28 @@ +# Generated by Django 5.2.7 on 2025-11-27 07:55 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0013_alter_feedback_comment'), + ] + + operations = [ + migrations.AddField( + model_name='category', + name='name_en', + field=models.CharField(max_length=255, null=True, verbose_name='Category Name'), + ), + migrations.AddField( + model_name='category', + name='name_ru', + field=models.CharField(max_length=255, null=True, verbose_name='Category Name'), + ), + migrations.AddField( + model_name='category', + name='name_uz', + field=models.CharField(max_length=255, null=True, verbose_name='Category Name'), + ), + ] diff --git a/core/apps/api/serializers/search/__init__.py b/core/apps/api/serializers/search/__init__.py index afeb2e5..6287097 100644 --- a/core/apps/api/serializers/search/__init__.py +++ b/core/apps/api/serializers/search/__init__.py @@ -1 +1,2 @@ from .search import * # noqa +from .search_ads import * # noqa diff --git a/core/apps/api/serializers/search/search_ads.py b/core/apps/api/serializers/search/search_ads.py new file mode 100644 index 0000000..d27a8c1 --- /dev/null +++ b/core/apps/api/serializers/search/search_ads.py @@ -0,0 +1,58 @@ +from rest_framework import serializers +from core.apps.api.models import AdModel + + +class BaseSearchAdsSerializer(serializers.ModelSerializer): + category = serializers.SerializerMethodField() + + class Meta: + model = AdModel + fields = [ + "id", + "name", + "image", + "category" + ] + + def get_category(self, obj): + request = self.context.get("request") + + lang = request.headers.get("Accept-Language", "uz") + lang = lang.split(",")[0].split("-")[0] + + if lang not in ["uz", "ru", "en"]: + lang = "uz" + + category = obj.category + if not category: + return None + + chain = [] + current = category + while current: + chain.append(current) + current = current.parent + + chain = list(reversed(chain)) + + result = None + for cat in reversed(chain): + result = { + "id": cat.id, + "name": getattr(cat, f"name_{lang}"), + "children": result + } + + return result + + +class ListSearchAdsSerializer(BaseSearchAdsSerializer): + class Meta(BaseSearchAdsSerializer.Meta): ... + + +class RetrieveSearchAdsSerializer(BaseSearchAdsSerializer): + class Meta(BaseSearchAdsSerializer.Meta): ... + + +class CreateSearchAdsSerializer(BaseSearchAdsSerializer): + class Meta(BaseSearchAdsSerializer.Meta): ... diff --git a/core/apps/api/tests/search/__init__.py b/core/apps/api/tests/search/__init__.py index 9b1bf91..fd0bd42 100644 --- a/core/apps/api/tests/search/__init__.py +++ b/core/apps/api/tests/search/__init__.py @@ -1 +1,2 @@ from .test_search_history import * # noqa +from .test_search_ads import * # noqa diff --git a/core/apps/api/tests/search/test_search_ads.py b/core/apps/api/tests/search/test_search_ads.py new file mode 100644 index 0000000..f585c75 --- /dev/null +++ b/core/apps/api/tests/search/test_search_ads.py @@ -0,0 +1,38 @@ +import pytest +from django.urls import reverse +from rest_framework.test import APIClient + +from core.apps.api.models import AdModel + + +@pytest.fixture +def instance(db): + return AdModel._baker() + + +@pytest.fixture +def api_client(instance): + client = APIClient() + ##client.force_authenticate(user=instance.user) + return client, instance + + +@pytest.fixture +def data(api_client): + client, instance = api_client + return ( + { + "list": reverse("search-ads-list"), + }, + client, + instance, + ) + + +@pytest.mark.django_db +def test_list(data): + urls, client, _ = data + response = client.get(urls["list"]) + data_resp = response.json() + assert response.status_code == 200 + assert data_resp["status"] is True diff --git a/core/apps/api/translation/__init__.py b/core/apps/api/translation/__init__.py new file mode 100644 index 0000000..d63c50f --- /dev/null +++ b/core/apps/api/translation/__init__.py @@ -0,0 +1 @@ +from .category import * # noqa diff --git a/core/apps/api/translation/category.py b/core/apps/api/translation/category.py new file mode 100644 index 0000000..9089256 --- /dev/null +++ b/core/apps/api/translation/category.py @@ -0,0 +1,10 @@ +from modeltranslation.translator import TranslationOptions, register + +from core.apps.api.models import Category + + +@register(Category) +class CategoryTranslation(TranslationOptions): + fields = [ + "name", + ] diff --git a/core/apps/api/urls.py b/core/apps/api/urls.py index f07350d..8d01b39 100644 --- a/core/apps/api/urls.py +++ b/core/apps/api/urls.py @@ -1,10 +1,19 @@ from django.urls import include, path from rest_framework.routers import DefaultRouter -from core.apps.api.views import CategoryHomeApiViewSet, CategoryViewSet, HomeAdApiView, SearchHistoryViewSet, \ - UserLikeViewSet, NotificationViewSet, BannerViewSet +from core.apps.api.views import ( + BannerViewSet, + CategoryHomeApiViewSet, + CategoryViewSet, + HomeAdApiView, + NotificationViewSet, + SearchAdsViewSet, + SearchHistoryViewSet, + UserLikeViewSet, +) router = DefaultRouter() +router.register("search-ads", SearchAdsViewSet, basename="search-ads") router.register("banner", BannerViewSet, basename="banner") router.register("notification", NotificationViewSet, basename="notification") router.register("user-like", UserLikeViewSet, basename="user-like") diff --git a/core/apps/api/views/search/__init__.py b/core/apps/api/views/search/__init__.py index afeb2e5..6287097 100644 --- a/core/apps/api/views/search/__init__.py +++ b/core/apps/api/views/search/__init__.py @@ -1 +1,2 @@ from .search import * # noqa +from .search_ads import * # noqa diff --git a/core/apps/api/views/search/search_ads.py b/core/apps/api/views/search/search_ads.py new file mode 100644 index 0000000..58264e2 --- /dev/null +++ b/core/apps/api/views/search/search_ads.py @@ -0,0 +1,48 @@ +from rest_framework import mixins +from rest_framework.viewsets import GenericViewSet +from django_core.mixins.base import BaseViewSetMixin +from drf_spectacular.utils import extend_schema +from rest_framework.permissions import AllowAny +from core.apps.api.models import AdModel +from django_filters.rest_framework import DjangoFilterBackend +from rest_framework.filters import SearchFilter +from core.apps.api.serializers.search import ( + ListSearchAdsSerializer, + +) + + +@extend_schema(tags=['Search Ads']) +class SearchAdsViewSet(BaseViewSetMixin, mixins.ListModelMixin, GenericViewSet): + queryset = AdModel.objects.all().order_by('-created_at') + serializer_class = ListSearchAdsSerializer + permission_classes = [AllowAny] + http_method_names = ['get'] + filter_backends = [DjangoFilterBackend, SearchFilter] + search_fields = ["name"] + + action_permission_classes = {} + action_serializer_class = { + 'list': ListSearchAdsSerializer, + } + + def get_queryset(self): + queryset = super().get_queryset() + + search_text = self.request.query_params.get('search') + + if search_text: + field = f"name" + queryset = queryset.filter(**{f"{field}__icontains": search_text}) + + return queryset + + def list(self, request, *args, **kwargs): + response = super().list(request, *args, **kwargs) + + if isinstance(response.data, dict) and "results" in response.data: + response.data["results"] = response.data["results"][:5] + else: + response.data = response.data[:5] + + return response diff --git a/core/utils/__init__.py b/core/utils/__init__.py index 4074af2..7a01889 100644 --- a/core/utils/__init__.py +++ b/core/utils/__init__.py @@ -1,3 +1,4 @@ from .cache import * # noqa from .console import * # noqa from .core import * # noqa +from .language import * # noqa diff --git a/core/utils/language.py b/core/utils/language.py new file mode 100644 index 0000000..d82c794 --- /dev/null +++ b/core/utils/language.py @@ -0,0 +1,8 @@ +def get_request_lang(request): + lang = request.headers.get("Accept-Language", "uz") + lang = lang.split(",")[0].split("-")[0].lower() + + if lang not in ["uz", "ru", "en"]: + lang = "uz" + + return lang