Add search to city/neighborhood endpoints; document filters for swagger
Neighborhood filtering was UUID-only; add name-based search on both GET /api/v1/cities/ and GET /api/v1/neighborhoods/ (the latter also matching by city name), and document all params for drf-spectacular. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
661129d0a1
commit
fa9a69fff3
3 changed files with 75 additions and 0 deletions
|
|
@ -7,5 +7,8 @@
|
|||
- `GET /api/v1/products/` — same issue: documented the existing `store`, `category`, and `search` query params for swagger (`apps/catalog/views.py`).
|
||||
|
||||
### Added
|
||||
- `GET /api/v1/cities/?search=` — search cities by name (`apps/locations/views.py`); previously no text search existed.
|
||||
- `GET /api/v1/neighborhoods/?search=` — search neighborhoods by neighborhood name or city name, alongside the existing `city` UUID filter (`apps/locations/views.py`).
|
||||
- Test coverage for `StoreViewSet` list filtering (`apps/stores/tests/test_store_list.py`) — previously untested.
|
||||
- Test coverage for `ProductViewSet` list filtering (`apps/catalog/tests/test_product_list.py`) — previously untested.
|
||||
- Test coverage for `CityViewSet`/`NeighborhoodViewSet` list filtering (`apps/locations/tests/test_location_list.py`) — previously untested.
|
||||
|
|
|
|||
42
apps/locations/tests/test_location_list.py
Normal file
42
apps/locations/tests/test_location_list.py
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
from rest_framework.test import APITestCase
|
||||
|
||||
from apps.locations.models import City, Neighborhood
|
||||
|
||||
|
||||
class LocationListFilterTests(APITestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
cls.tehran = City.objects.create(name='تهران', slug='tehran')
|
||||
cls.mashhad = City.objects.create(name='مشهد', slug='mashhad')
|
||||
|
||||
cls.vanak = Neighborhood.objects.create(city=cls.tehran, name='ونک', slug='vanak')
|
||||
cls.tajrish = Neighborhood.objects.create(city=cls.tehran, name='تجریش', slug='tajrish')
|
||||
cls.mashhad_hood = Neighborhood.objects.create(city=cls.mashhad, name='احمدآباد', slug='ahmadabad')
|
||||
|
||||
def test_search_city_by_name(self):
|
||||
response = self.client.get('/api/v1/cities/', {'search': 'مشهد'})
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
uuids = {city['uuid'] for city in response.data['results']}
|
||||
self.assertEqual(uuids, {str(self.mashhad.uuid)})
|
||||
|
||||
def test_filter_neighborhood_by_city(self):
|
||||
response = self.client.get('/api/v1/neighborhoods/', {'city': str(self.tehran.uuid)})
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
uuids = {n['uuid'] for n in response.data['results']}
|
||||
self.assertEqual(uuids, {str(self.vanak.uuid), str(self.tajrish.uuid)})
|
||||
|
||||
def test_search_neighborhood_by_name(self):
|
||||
response = self.client.get('/api/v1/neighborhoods/', {'search': 'ونک'})
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
uuids = {n['uuid'] for n in response.data['results']}
|
||||
self.assertEqual(uuids, {str(self.vanak.uuid)})
|
||||
|
||||
def test_search_neighborhood_by_city_name(self):
|
||||
response = self.client.get('/api/v1/neighborhoods/', {'search': 'مشهد'})
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
uuids = {n['uuid'] for n in response.data['results']}
|
||||
self.assertEqual(uuids, {str(self.mashhad_hood.uuid)})
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
from django.db.models import Q
|
||||
from drf_spectacular.utils import OpenApiParameter, extend_schema, extend_schema_view
|
||||
from rest_framework import mixins, viewsets
|
||||
from rest_framework.permissions import AllowAny, IsAuthenticated
|
||||
|
||||
|
|
@ -7,13 +9,35 @@ from .models import Address, City, Neighborhood
|
|||
from .serializers import AddressSerializer, CitySerializer, NeighborhoodSerializer
|
||||
|
||||
|
||||
@extend_schema_view(
|
||||
list=extend_schema(
|
||||
parameters=[
|
||||
OpenApiParameter('search', str, description='Search by city name.'),
|
||||
],
|
||||
),
|
||||
)
|
||||
class CityViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
|
||||
schema_tags = ['Locations']
|
||||
permission_classes = [AllowAny]
|
||||
serializer_class = CitySerializer
|
||||
queryset = City.objects.filter(is_active=True)
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = super().get_queryset()
|
||||
search = self.request.query_params.get('search')
|
||||
if search:
|
||||
queryset = queryset.filter(name__icontains=search)
|
||||
return queryset
|
||||
|
||||
|
||||
@extend_schema_view(
|
||||
list=extend_schema(
|
||||
parameters=[
|
||||
OpenApiParameter('city', str, description='Filter by city UUID.'),
|
||||
OpenApiParameter('search', str, description='Search by neighborhood or city name.'),
|
||||
],
|
||||
),
|
||||
)
|
||||
class NeighborhoodViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
|
||||
schema_tags = ['Locations']
|
||||
permission_classes = [AllowAny]
|
||||
|
|
@ -22,9 +46,15 @@ class NeighborhoodViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, view
|
|||
|
||||
def get_queryset(self):
|
||||
queryset = super().get_queryset()
|
||||
|
||||
city_uuid = self.request.query_params.get('city')
|
||||
if city_uuid:
|
||||
queryset = queryset.filter(city__uuid=city_uuid)
|
||||
|
||||
search = self.request.query_params.get('search')
|
||||
if search:
|
||||
queryset = queryset.filter(Q(name__icontains=search) | Q(city__name__icontains=search))
|
||||
|
||||
return queryset
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue