From 0a8db7e3adb9432ce3d0eeb092d98371b6d91432 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Tue, 10 Dec 2019 18:36:18 +0300 Subject: [PATCH] permission --- apps/product/migrations/0021_product_site.py | 20 +++++ apps/product/migrations/0021_product_sites.py | 19 ----- .../migrations/0022_auto_20191210_1517.py | 18 +++++ apps/product/models.py | 11 ++- apps/product/serializers/back.py | 6 +- apps/product/serializers/common.py | 2 +- apps/product/tests.py | 23 ++++-- apps/product/views/back.py | 1 - apps/utils/permissions.py | 74 ++++++++++++------- 9 files changed, 116 insertions(+), 58 deletions(-) create mode 100644 apps/product/migrations/0021_product_site.py delete mode 100644 apps/product/migrations/0021_product_sites.py create mode 100644 apps/product/migrations/0022_auto_20191210_1517.py diff --git a/apps/product/migrations/0021_product_site.py b/apps/product/migrations/0021_product_site.py new file mode 100644 index 00000000..5c92d3b1 --- /dev/null +++ b/apps/product/migrations/0021_product_site.py @@ -0,0 +1,20 @@ +# Generated by Django 2.2.7 on 2019-12-10 14:13 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('main', '0040_footer'), + ('product', '0020_merge_20191209_0911'), + ] + + operations = [ + migrations.AddField( + model_name='product', + name='site', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='main.SiteSettings'), + ), + ] diff --git a/apps/product/migrations/0021_product_sites.py b/apps/product/migrations/0021_product_sites.py deleted file mode 100644 index c68229fb..00000000 --- a/apps/product/migrations/0021_product_sites.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2.7 on 2019-12-10 07:40 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('main', '0039_sitefeature_old_id'), - ('product', '0020_merge_20191209_0911'), - ] - - operations = [ - migrations.AddField( - model_name='product', - name='sites', - field=models.ManyToManyField(to='main.SiteSettings'), - ), - ] diff --git a/apps/product/migrations/0022_auto_20191210_1517.py b/apps/product/migrations/0022_auto_20191210_1517.py new file mode 100644 index 00000000..30aa083c --- /dev/null +++ b/apps/product/migrations/0022_auto_20191210_1517.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.7 on 2019-12-10 15:17 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('product', '0021_product_site'), + ] + + operations = [ + migrations.AlterField( + model_name='producttype', + name='index_name', + field=models.CharField(choices=[('food', 'food'), ('wine', 'wine'), ('liquor', 'liquor'), ('souvenir', 'souvenir'), ('book', 'book')], db_index=True, max_length=50, unique=True, verbose_name='Index name'), + ), + ] diff --git a/apps/product/models.py b/apps/product/models.py index 902073ab..15ddfb2e 100644 --- a/apps/product/models.py +++ b/apps/product/models.py @@ -25,10 +25,17 @@ class ProductType(TranslatedFieldsMixin, ProjectBaseMixin): SOUVENIR = 'souvenir' BOOK = 'book' + INDEX_CHOICES = ( + (FOOD, 'food'), + (WINE, 'wine'), + (LIQUOR, 'liquor'), + (SOUVENIR, 'souvenir'), + (BOOK, 'book') + ) name = TJSONField(blank=True, null=True, default=None, verbose_name=_('Name'), help_text='{"en-GB":"some text"}') index_name = models.CharField(max_length=50, unique=True, db_index=True, - verbose_name=_('Index name')) + verbose_name=_('Index name'), choices=INDEX_CHOICES) use_subtypes = models.BooleanField(_('Use subtypes'), default=True) tag_categories = models.ManyToManyField('tag.TagCategory', related_name='product_types', @@ -222,7 +229,7 @@ class Product(GalleryModelMixin, TranslatedFieldsMixin, BaseAttributes, default=None, null=True, verbose_name=_('Serial number')) - sites = models.ManyToManyField(to='main.SiteSettings') + site = models.ForeignKey(to='main.SiteSettings', null=True, blank=True, on_delete=models.CASCADE) objects = ProductManager.from_queryset(ProductQuerySet)() diff --git a/apps/product/serializers/back.py b/apps/product/serializers/back.py index 55dc5ebc..575a64c4 100644 --- a/apps/product/serializers/back.py +++ b/apps/product/serializers/back.py @@ -8,7 +8,7 @@ from product.serializers import ProductDetailSerializer, ProductTypeBaseSerializ ProductSubTypeBaseSerializer from tag.models import TagCategory from account.serializers.common import UserShortSerializer - +from main.serializers import SiteSettingsSerializer class ProductBackOfficeGallerySerializer(serializers.ModelSerializer): """Serializer class for model ProductGallery.""" @@ -54,6 +54,7 @@ class ProductBackOfficeGallerySerializer(serializers.ModelSerializer): class ProductBackOfficeDetailSerializer(ProductDetailSerializer): """Product back-office detail serializer.""" + in_favorites = serializers.BooleanField(allow_null=True, read_only=True) class Meta(ProductDetailSerializer.Meta): """Meta class.""" @@ -67,9 +68,10 @@ class ProductBackOfficeDetailSerializer(ProductDetailSerializer): # 'wine_sub_region', 'wine_village', 'state', + 'site', + 'product_type' ] - class ProductTypeBackOfficeDetailSerializer(ProductTypeBaseSerializer): """Product type back-office detail serializer.""" diff --git a/apps/product/serializers/common.py b/apps/product/serializers/common.py index e0617e63..c964a047 100644 --- a/apps/product/serializers/common.py +++ b/apps/product/serializers/common.py @@ -95,7 +95,7 @@ class ProductBaseSerializer(serializers.ModelSerializer): wine_colors = TagBaseSerializer(many=True, read_only=True) preview_image_url = serializers.URLField(allow_null=True, read_only=True) - in_favorites = serializers.BooleanField(allow_null=True) + in_favorites = serializers.BooleanField(allow_null=True, read_only=True) wine_origins = EstablishmentWineOriginBaseSerializer(many=True, read_only=True) class Meta: diff --git a/apps/product/tests.py b/apps/product/tests.py index 82fa8708..d2d9ebd0 100644 --- a/apps/product/tests.py +++ b/apps/product/tests.py @@ -9,7 +9,7 @@ from translation.models import Language from account.models import Role, UserRole from location.models import Country, Address, City, Region from main.models import SiteSettings -from product.models import Product +from product.models import Product, ProductType class BaseTestCase(APITestCase): def setUp(self): @@ -71,12 +71,19 @@ class BaseTestCase(APITestCase): self.user_role.save() - self.product = Product.objects.create() + self.product_type = ProductType.objects.create(index_name=ProductType.LIQUOR) + self.product_type.save() + + self.product = Product.objects.create(name='Product') self.product.save() + class LiquorReviewerTests(BaseTestCase): def test_get(self): + self.product.product_type = self.product_type + self.product.save() + url = reverse("back:product:list-create") response = self.client.get(url, format='json') self.assertEqual(response.status_code, status.HTTP_200_OK) @@ -86,8 +93,6 @@ class LiquorReviewerTests(BaseTestCase): self.assertEqual(response.status_code, status.HTTP_200_OK) def test_post_patch_put_delete(self): - # POST - data_post = { "slug": None, "public_mark": None, @@ -97,13 +102,13 @@ class LiquorReviewerTests(BaseTestCase): "available": False, "establishment": None, "wine_village": None, - "in_favorites": 'false', - "state": Product.PUBLISHED + "state": Product.PUBLISHED, + "site_id": self.site.id, + "product_type_id": self.product_type.id } - url = reverse("back:product:list-create") response = self.client.post(url, data=data_post, format='json') - self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.status_code, status.HTTP_201_CREATED) data_patch = { 'name': 'Test product' @@ -112,3 +117,5 @@ class LiquorReviewerTests(BaseTestCase): response = self.client.patch(url, data=data_patch, format='json') self.assertEqual(response.status_code, status.HTTP_200_OK) + + diff --git a/apps/product/views/back.py b/apps/product/views/back.py index 504172a0..836f9d36 100644 --- a/apps/product/views/back.py +++ b/apps/product/views/back.py @@ -102,7 +102,6 @@ class ProductListCreateBackOfficeView(BackOfficeListCreateMixin, ProductBackOffi permission_classes = [IsLiquorReviewer] - class ProductTypeListCreateBackOfficeView(BackOfficeListCreateMixin, ProductTypeBackOfficeMixinView, generics.ListCreateAPIView): diff --git a/apps/utils/permissions.py b/apps/utils/permissions.py index 63571314..1f30a4c6 100644 --- a/apps/utils/permissions.py +++ b/apps/utils/permissions.py @@ -9,7 +9,7 @@ from authorization.models import JWTRefreshToken from utils.tokens import GMRefreshToken from establishment.models import EstablishmentSubType from location.models import Address -from product.models import Product +from product.models import Product, ProductType class IsAuthenticatedAndTokenIsValid(permissions.BasePermission): @@ -448,38 +448,62 @@ class IsLiquorReviewer(IsStandardUser): ] pk_object = None - product = None + roles = None permission = False + + if 'site_id' in request.data and 'product_type_id' in request.data: + if request.data['site_id'] is not None \ + and request.data['product_type_id'] is not None: + + product_types = ProductType.objects. \ + filter(index_name=ProductType.LIQUOR, + id=request.data['product_type_id']) + + if product_types.exists(): + roles = Role.objects.filter(role=Role.LIQUOR_REVIEWER, + site_id=request.data['site_id']) + if 'pk' in view.kwargs: pk_object = view.kwargs['pk'] if pk_object is not None: product = Product.objects.get(pk=pk_object) + if product.site_id is not None \ + and product.product_type_id is not None: - if hasattr(product, 'sites') and product.sites.exists(): - role = Role.objects.filter(role=Role.LIQUOR_REVIEWER, site__in=[site for site in product.sites]) - permission = UserRole.objects.filter(user=request.user, role=role).exists() + product_types = ProductType.objects. \ + filter(index_name=ProductType.LIQUOR, + id=product.product_type_id) + + if product_types.exists(): + roles = Role.objects.filter(role=Role.LIQUOR_REVIEWER, + site_id=product.site_id) + + if roles is not None: + permission = UserRole.objects.filter(user=request.user, role__in=[role for role in roles])\ + .exists() rules.append(permission) return any(rules) - def has_object_permission(self, request, view, obj): - rules = [ - super().has_object_permission(request, view, obj) - ] - pk_object = None - product = None - permission = False - - if 'pk' in view.kwargs: - pk_object = view.kwargs['pk'] - - if pk_object is not None: - product = Product.objects.get(pk=pk_object) - - if product.sites.exists(): - role = Role.objects.filter(role=Role.LIQUOR_REVIEWER, site__in=[site for site in product.sites]) - permission = UserRole.objects.filter(user=request.user, role=role).exists() - - rules.append(permission) - return any(rules) \ No newline at end of file + # + # def has_object_permission(self, request, view, obj): + # rules = [ + # super().has_object_permission(request, view, obj) + # ] + # # pk_object = None + # # product = None + # # permission = False + # # + # # if 'pk' in view.kwargs: + # # pk_object = view.kwargs['pk'] + # # + # # if pk_object is not None: + # # product = Product.objects.get(pk=pk_object) + # # + # # if product.sites.exists(): + # # role = Role.objects.filter(role=Role.LIQUOR_REVIEWER, site__in=[site for site in product.sites]) + # # permission = UserRole.objects.filter(user=request.user, role=role).exists() + # # + # # rules.append(permission) + # return any(rules) \ No newline at end of file