From 58797d297225c7cb4f7617dccab30cbcd3205ab3 Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 11 Feb 2021 13:51:05 +0000 Subject: [PATCH 1/4] added local TagTreeModel for testing --- products/models.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/products/models.py b/products/models.py index 8fce915..d3468dc 100644 --- a/products/models.py +++ b/products/models.py @@ -1,11 +1,18 @@ from django.contrib.gis.db import models -from tagulous.models import SingleTagField, TagField +from tagulous.models import SingleTagField, TagField, TagTreeModel from companies.models import Company # Create your models here. +class MyTreeTags(TagTreeModel): + class TagMeta: + initial = "colors/blue, colors/red, colors/green" + force_lowercase = True + # autocomplete_view = 'myapp.views.hobbies_autocomplete' + + class Product(models.Model): SYNCHRONIZED = 'SYNCHRONIZED' From 77b1660fc3091ad2dce59c67cb94990b7c786311 Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 12 Feb 2021 10:49:09 +0000 Subject: [PATCH 2/4] testing tag filtering --- products/serializers.py | 1 + products/tests.py | 35 +++++++++++++++++++++++++++++++++++ products/views.py | 5 +++++ utils/tag_filters.py | 11 +++++++++++ 4 files changed, 52 insertions(+) create mode 100644 utils/tag_filters.py diff --git a/products/serializers.py b/products/serializers.py index c20e5b7..0f09d36 100644 --- a/products/serializers.py +++ b/products/serializers.py @@ -1,4 +1,5 @@ from rest_framework import serializers +from tagulous.serializers.json import Serializer from products.models import Product diff --git a/products/tests.py b/products/tests.py index 12eee20..07339f2 100644 --- a/products/tests.py +++ b/products/tests.py @@ -80,6 +80,41 @@ class ProductViewSetTest(APITestCase): # Assert access is forbidden self.assertEqual(response.status_code, status.HTTP_200_OK) + def test_anon_user_can_filter_name(self): + # create instances + self.factory(name='product1', tags="zapatos, verdes") + self.factory(name='product2', tags="rojos") + + url = f"{self.endpoint}?name=product1" + + # Request list + response = self.client.get(url) + payload = response.json() + + # Assert access is granted + self.assertEqual(response.status_code, status.HTTP_200_OK) + # Assert number of instnaces in response + self.assertEquals(1, len(payload)) + + def test_anon_user_can_filter_tags(self): + # create instances + self.factory(name='product1', tags="zapatos, verdes") + self.factory(name='product2', tags="rojos") + + url = f"{self.endpoint}?tags=rojos" + + # Request list + response = self.client.get(url) + payload = response.json() + + # import ipdb; ipdb.set_trace() + + # Assert access is granted + self.assertEqual(response.status_code, status.HTTP_200_OK) + # Assert number of instnaces in response + self.assertEquals(1, len(payload)) + + # authenticated user def test_auth_user_can_list_instances(self): """Regular logged-in user can list instance diff --git a/products/views.py b/products/views.py index 947d1c8..2b82e88 100644 --- a/products/views.py +++ b/products/views.py @@ -14,6 +14,7 @@ from rest_framework import viewsets from rest_framework.response import Response from rest_framework.permissions import IsAuthenticatedOrReadOnly, IsAdminUser, IsAuthenticated from rest_framework.decorators import api_view, permission_classes, action +import django_filters.rest_framework import requests @@ -25,6 +26,8 @@ from history.models import HistorySync from back_latienda.permissions import IsCreator from .utils import extract_search_filters from utils.tag_serializers import TaggitSerializer +from utils.tag_filters import ProductTagFilter + logging.basicConfig( filename='logs/product-load.log', @@ -38,6 +41,8 @@ class ProductViewSet(viewsets.ModelViewSet): queryset = Product.objects.all() serializer_class = ProductSerializer permission_classes = [IsAuthenticatedOrReadOnly, IsCreator] + filter_backends = ProductTagFilter + filterset_fields = ['name', 'tags'] def perform_create(self, serializer): serializer.save(creator=self.request.user) diff --git a/utils/tag_filters.py b/utils/tag_filters.py new file mode 100644 index 0000000..f1f7abd --- /dev/null +++ b/utils/tag_filters.py @@ -0,0 +1,11 @@ +import django_filters +from products.models import Product + + +class ProductTagFilter(django_filters.FilterSet): + tags = django_filters.CharFilter(field_name='tags.name', lookup_expr='iexact') + + class Meta: + model = Product + fields = ['name',] + From 9a6a30553a8b06f7e110c9fe89729a3789ecf200 Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 12 Feb 2021 11:09:32 +0000 Subject: [PATCH 3/4] added custom filter with tag support to products --- products/tests.py | 15 ++++++++++----- products/views.py | 3 +-- utils/tag_filters.py | 10 ++++++++-- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/products/tests.py b/products/tests.py index 07339f2..cbdff57 100644 --- a/products/tests.py +++ b/products/tests.py @@ -98,8 +98,15 @@ class ProductViewSetTest(APITestCase): def test_anon_user_can_filter_tags(self): # create instances - self.factory(name='product1', tags="zapatos, verdes") - self.factory(name='product2', tags="rojos") + expected_instance = [ + self.factory(name='product1', tags="zapatos, rojos"), + self.factory(name='product2', tags="rojos") + ] + unexpected_instance = [ + self.factory(name='sadfdsa', tags="zapatos, azules"), + self.factory(name='qwerw', tags="xxl") + ] + url = f"{self.endpoint}?tags=rojos" @@ -107,12 +114,10 @@ class ProductViewSetTest(APITestCase): response = self.client.get(url) payload = response.json() - # import ipdb; ipdb.set_trace() - # Assert access is granted self.assertEqual(response.status_code, status.HTTP_200_OK) # Assert number of instnaces in response - self.assertEquals(1, len(payload)) + self.assertEquals(len(expected_instance), len(payload)) # authenticated user diff --git a/products/views.py b/products/views.py index 2b82e88..6521908 100644 --- a/products/views.py +++ b/products/views.py @@ -14,7 +14,6 @@ from rest_framework import viewsets from rest_framework.response import Response from rest_framework.permissions import IsAuthenticatedOrReadOnly, IsAdminUser, IsAuthenticated from rest_framework.decorators import api_view, permission_classes, action -import django_filters.rest_framework import requests @@ -41,7 +40,7 @@ class ProductViewSet(viewsets.ModelViewSet): queryset = Product.objects.all() serializer_class = ProductSerializer permission_classes = [IsAuthenticatedOrReadOnly, IsCreator] - filter_backends = ProductTagFilter + filterset_class = ProductTagFilter filterset_fields = ['name', 'tags'] def perform_create(self, serializer): diff --git a/utils/tag_filters.py b/utils/tag_filters.py index f1f7abd..997dbf9 100644 --- a/utils/tag_filters.py +++ b/utils/tag_filters.py @@ -3,9 +3,15 @@ from products.models import Product class ProductTagFilter(django_filters.FilterSet): - tags = django_filters.CharFilter(field_name='tags.name', lookup_expr='iexact') + tags = django_filters.CharFilter(method='tag_filter') class Meta: model = Product - fields = ['name',] + fields = ['name', 'tags'] + + def tag_filter(self, queryset, name, value): + if name == 'tags': + return queryset.filter(tags=value) + return [] + From 0a61cea599ea95b457904ef7f86f821cd097d6f7 Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 12 Feb 2021 11:28:55 +0000 Subject: [PATCH 4/4] query params filter working with tags in Product --- core/management/commands/loadgisdata.py | 4 --- products/tests.py | 46 +++++++++++++++++++++++-- products/views.py | 2 +- utils/tag_filters.py | 13 +++---- 4 files changed, 52 insertions(+), 13 deletions(-) diff --git a/core/management/commands/loadgisdata.py b/core/management/commands/loadgisdata.py index 3ea9381..efdd312 100644 --- a/core/management/commands/loadgisdata.py +++ b/core/management/commands/loadgisdata.py @@ -61,7 +61,6 @@ class Command(BaseCommand): logging.info(f"Country instance created for: {name}") except Exception as e: logging.error(f"[{name}][{type(e)}] {str(e)}") - # import ipdb; ipdb.set_trace() # region instances logging.info("loading region instances") @@ -83,7 +82,6 @@ class Command(BaseCommand): logging.info(f"Region instance created for: {name}") except Exception as e: logging.error(f"[{name}][{type(e)}] {str(e)}") - # import ipdb; ipdb.set_trace() # province instances logging.info("loading province instances") @@ -106,7 +104,6 @@ class Command(BaseCommand): logging.info(f"Province instance created for: {name}") except Exception as e: logging.error(f"[{name}][{type(e)}] {str(e)}") - import ipdb; ipdb.set_trace() # city instances logging.info("loading city instances") @@ -129,7 +126,6 @@ class Command(BaseCommand): logging.debug(f"City instance created for: {name}") except Exception as e: logging.error(f"[{type(e)}] {str(e)}") - # import ipdb; ipdb.set_trace() logging.info(f"Country instances created: {country_counter}") logging.info(f"Region instances created: {region_counter}") diff --git a/products/tests.py b/products/tests.py index cbdff57..9f9d471 100644 --- a/products/tests.py +++ b/products/tests.py @@ -106,8 +106,7 @@ class ProductViewSetTest(APITestCase): self.factory(name='sadfdsa', tags="zapatos, azules"), self.factory(name='qwerw', tags="xxl") ] - - + # prepare url url = f"{self.endpoint}?tags=rojos" # Request list @@ -119,6 +118,49 @@ class ProductViewSetTest(APITestCase): # Assert number of instnaces in response self.assertEquals(len(expected_instance), len(payload)) + def test_anon_user_can_filter_attributes(self): + # create instances + expected_instance = [ + self.factory(attributes='xxl', tags="zapatos, rojos"), + self.factory(attributes='blue, xxl', tags="rojos") + ] + unexpected_instance = [ + self.factory(name='sadfdsa', tags="zapatos, azules"), + self.factory(name='qwerw', tags="xxl") + ] + # prepare url + url = f"{self.endpoint}?attributes=xxl" + + # Request list + response = self.client.get(url) + payload = response.json() + + # Assert access is granted + self.assertEqual(response.status_code, status.HTTP_200_OK) + # Assert number of instnaces in response + self.assertEquals(len(expected_instance), len(payload)) + + def test_anon_user_can_filter_category(self): + # create instances + expected_instance = [ + self.factory(category='ropa', tags="zapatos, rojos"), + self.factory(category='ropa', tags="rojos") + ] + unexpected_instance = [ + self.factory(category='roperos', tags="zapatos, azules"), + self.factory(category='enropados', tags="xxl") + ] + # prepare url + url = f"{self.endpoint}?category=ropa" + + # Request list + response = self.client.get(url) + payload = response.json() + + # Assert access is granted + self.assertEqual(response.status_code, status.HTTP_200_OK) + # Assert number of instnaces in response + self.assertEquals(len(expected_instance), len(payload)) # authenticated user def test_auth_user_can_list_instances(self): diff --git a/products/views.py b/products/views.py index 6521908..790f785 100644 --- a/products/views.py +++ b/products/views.py @@ -41,7 +41,7 @@ class ProductViewSet(viewsets.ModelViewSet): serializer_class = ProductSerializer permission_classes = [IsAuthenticatedOrReadOnly, IsCreator] filterset_class = ProductTagFilter - filterset_fields = ['name', 'tags'] + filterset_fields = ['name', 'tags', 'category', 'attributes'] def perform_create(self, serializer): serializer.save(creator=self.request.user) diff --git a/utils/tag_filters.py b/utils/tag_filters.py index 997dbf9..7394ac0 100644 --- a/utils/tag_filters.py +++ b/utils/tag_filters.py @@ -3,15 +3,16 @@ from products.models import Product class ProductTagFilter(django_filters.FilterSet): + tags = django_filters.CharFilter(method='tag_filter') + attributes = django_filters.CharFilter(method='tag_filter') + category = django_filters.CharFilter(method='tag_filter') class Meta: model = Product - fields = ['name', 'tags'] + fields = ['name', 'tags', 'category', 'attributes'] def tag_filter(self, queryset, name, value): - if name == 'tags': - return queryset.filter(tags=value) - return [] - - + return queryset.filter(**{ + name: value, + })