added endpoints for back office and refactored a little

This commit is contained in:
Anatoly 2019-11-13 12:07:02 +03:00
parent 94f7bda03a
commit 8cf50ff2cd
7 changed files with 171 additions and 47 deletions

View File

@ -290,6 +290,9 @@ class NewsBackOfficeGallerySerializer(serializers.ModelSerializer):
news = news_qs.first() news = news_qs.first()
image = image_qs.first() image = image_qs.first()
if image in news.gallery.all():
raise serializers.ValidationError({'detail': _('Image is already added.')})
attrs['news'] = news attrs['news'] = news
attrs['image'] = image attrs['image'] = image

View File

@ -9,6 +9,7 @@ from gallery.tasks import delete_image
from news import filters, models, serializers from news import filters, models, serializers
from rating.tasks import add_rating from rating.tasks import add_rating
from utils.permissions import IsCountryAdmin, IsContentPageManager from utils.permissions import IsCountryAdmin, IsContentPageManager
from utils.views import CreateDestroyGalleryViewMixin
class NewsMixinView: class NewsMixinView:
@ -84,8 +85,7 @@ class NewsBackOfficeLCView(NewsBackOfficeMixinView,
class NewsBackOfficeGalleryCreateDestroyView(NewsBackOfficeMixinView, class NewsBackOfficeGalleryCreateDestroyView(NewsBackOfficeMixinView,
generics.CreateAPIView, CreateDestroyGalleryViewMixin):
generics.DestroyAPIView):
"""Resource for a create gallery for news for back-office users.""" """Resource for a create gallery for news for back-office users."""
serializer_class = serializers.NewsBackOfficeGallerySerializer serializer_class = serializers.NewsBackOfficeGallerySerializer
@ -103,24 +103,6 @@ class NewsBackOfficeGalleryCreateDestroyView(NewsBackOfficeMixinView,
return gallery return gallery
def create(self, request, *args, **kwargs):
"""Overridden create method"""
super().create(request, *args, **kwargs)
return Response(status=status.HTTP_201_CREATED)
def destroy(self, request, *args, **kwargs):
"""Override destroy method."""
gallery_obj = self.get_object()
if settings.USE_CELERY:
on_commit(lambda: delete_image.delay(image_id=gallery_obj.image.id,
completely=False))
else:
on_commit(lambda: delete_image(image_id=gallery_obj.image.id,
completely=False))
# Delete an instances of NewsGallery model
gallery_obj.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
class NewsBackOfficeGalleryListView(NewsBackOfficeMixinView, generics.ListAPIView): class NewsBackOfficeGalleryListView(NewsBackOfficeMixinView, generics.ListAPIView):
"""Resource for returning gallery for news for back-office users.""" """Resource for returning gallery for news for back-office users."""

View File

@ -2,9 +2,10 @@
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from rest_framework import serializers from rest_framework import serializers
from product import models
from product.serializers import ProductDetailSerializer
from gallery.models import Image from gallery.models import Image
from product import models
from product.serializers import ProductDetailSerializer, ProductTypeBaseSerializer
from tag.models import TagCategory
class ProductBackOfficeGallerySerializer(serializers.ModelSerializer): class ProductBackOfficeGallerySerializer(serializers.ModelSerializer):
@ -33,12 +34,16 @@ class ProductBackOfficeGallerySerializer(serializers.ModelSerializer):
if not product_qs.exists(): if not product_qs.exists():
raise serializers.ValidationError({'detail': _('Product not found')}) raise serializers.ValidationError({'detail': _('Product not found')})
if not image_qs.exists(): if not image_qs.exists():
raise serializers.ValidationError({'detail': _('Image not found')}) raise serializers.ValidationError({'detail': _('Image not found')})
product = product_qs.first() product = product_qs.first()
image = image_qs.first() image = image_qs.first()
if image in product.gallery.all():
raise serializers.ValidationError({'detail': _('Image is already added.')})
attrs['product'] = product attrs['product'] = product
attrs['image'] = image attrs['image'] = image
@ -46,8 +51,10 @@ class ProductBackOfficeGallerySerializer(serializers.ModelSerializer):
class ProductBackOfficeDetailSerializer(ProductDetailSerializer): class ProductBackOfficeDetailSerializer(ProductDetailSerializer):
"""Product back-office detail serializer."""
class Meta(ProductDetailSerializer.Meta): class Meta(ProductDetailSerializer.Meta):
"""Meta class."""
fields = ProductDetailSerializer.Meta.fields + [ fields = ProductDetailSerializer.Meta.fields + [
'description', 'description',
'available', 'available',
@ -68,3 +75,58 @@ class ProductBackOfficeDetailSerializer(ProductDetailSerializer):
'wine_village': {'write_only': True}, 'wine_village': {'write_only': True},
'state': {'write_only': True}, 'state': {'write_only': True},
} }
class ProductTypeBackOfficeDetailSerializer(ProductTypeBaseSerializer):
"""Product type back-office detail serializer."""
class Meta(ProductTypeBaseSerializer.Meta):
"""Meta class."""
fields = ProductTypeBaseSerializer.Meta.fields + [
'name',
'index_name',
'use_subtypes',
]
extra_kwargs = {
'name': {'write_only': True},
'index_name': {'write_only': True},
'use_subtypes': {'write_only': True},
}
class ProductTypeTagCategorySerializer(serializers.ModelSerializer):
"""Serializer for attaching tag category to product type."""
product_type_id = serializers.PrimaryKeyRelatedField(
queryset=models.ProductType.objects.all(),
write_only=True)
tag_category_id = serializers.PrimaryKeyRelatedField(
queryset=TagCategory.objects.all(),
write_only=True)
class Meta(ProductTypeBaseSerializer.Meta):
"""Meta class."""
fields = ProductTypeBaseSerializer.Meta.fields + [
'product_type_id',
'tag_category_id',
]
def validate(self, attrs):
"""Validation method."""
product_type = attrs.pop('product_type_id')
tag_category = attrs.get('tag_category_id')
if tag_category in product_type.tag_categories.all():
raise serializers.ValidationError({
'detail': _('Tag category is already attached.')})
attrs['product_type'] = product_type
attrs['tag_category'] = tag_category
return attrs
def create(self, validated_data):
"""Overridden create method."""
product_type = validated_data.get('product_type')
tag_category = validated_data.get('tag_category')
product_type.tag_categories.add(tag_category)
return product_type

View File

@ -32,7 +32,8 @@ class ProductSubTypeBaseSerializer(serializers.ModelSerializer):
class ProductTypeBaseSerializer(serializers.ModelSerializer): class ProductTypeBaseSerializer(serializers.ModelSerializer):
"""ProductType base serializer""" """ProductType base serializer"""
name_translated = TranslatedField() name_translated = TranslatedField()
index_name_display = serializers.CharField(source='get_index_name_display') index_name_display = serializers.CharField(source='get_index_name_display',
read_only=True)
class Meta: class Meta:
model = models.ProductType model = models.ProductType

View File

@ -1,14 +1,27 @@
"""Product backoffice url patterns.""" """Product backoffice url patterns."""
from django.urls import path from django.urls import path
from product.urls.common import urlpatterns as common_urlpatterns
from product import views from product import views
urlpatterns = [ urlpatterns = [
path('', views.ProductListCreateBackOfficeView.as_view(), name='list-create'),
path('<int:pk>/', views.ProductDetailBackOfficeView.as_view(), name='rud'), path('<int:pk>/', views.ProductDetailBackOfficeView.as_view(), name='rud'),
path('<int:pk>/gallery/', views.ProductBackOfficeGalleryListView.as_view(), path('<int:pk>/gallery/', views.ProductBackOfficeGalleryListView.as_view(),
name='gallery-list'), name='gallery-list'),
path('<int:pk>/gallery/<int:image_id>/', views.ProductBackOfficeGalleryCreateDestroyView.as_view(), path('<int:pk>/gallery/<int:image_id>/', views.ProductBackOfficeGalleryCreateDestroyView.as_view(),
name='gallery-create-destroy'), name='gallery-create-destroy'),
] # product types
path('types/', views.ProductTypeListCreateBackOfficeView.as_view(), name='type-list-create'),
path('types/<int:pk>/', views.ProductTypeRUDBackOfficeView.as_view(),
name='type-retrieve-update-destroy'),
path('types/attach-tag-category/', views.ProductTypeTagCategoryCreateBackOfficeView.as_view(),
name='type-tag-category-create'),
# product sub types
# path('subtypes/', views.ProductSubTypeListCreateBackOfficeView.as_view(),
# name='subtype-list-create'),
# path('subtypes/<int:pk>/', views.ProductSubTypeRUDBackOfficeView.as_view(),
# name='subtype-retrieve-update-destroy'),
# path('subtypes/attach-tag-category/', views.ProductSubTypeTagCategoryCreateBackOfficeView.as_view(),
# name='subtype-tag-category-create'),
urlpatterns.extend(common_urlpatterns) ]

View File

@ -2,11 +2,12 @@
from django.conf import settings from django.conf import settings
from django.db.transaction import on_commit from django.db.transaction import on_commit
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from rest_framework import generics, status, permissions from rest_framework import generics, status, permissions, views
from rest_framework.response import Response from rest_framework.response import Response
from gallery.tasks import delete_image from gallery.tasks import delete_image
from product import serializers, models from product import serializers, models
from utils.views import CreateDestroyGalleryViewMixin
class ProductBackOfficeMixinView: class ProductBackOfficeMixinView:
@ -17,9 +18,34 @@ class ProductBackOfficeMixinView:
.order_by('-created', ) .order_by('-created', )
class ProductTypeBackOfficeMixinView:
"""Product type back-office mixin view."""
permission_classes = (permissions.IsAuthenticated,)
queryset = models.ProductType.objects.all()
class ProductSubTypeBackOfficeMixinView:
"""Product sub type back-office mixin view."""
permission_classes = (permissions.IsAuthenticated,)
queryset = models.ProductSubType.objects.all()
class BackOfficeListCreateMixin(views.APIView):
"""Back-office list-create mixin view."""
def check_permissions(self, request):
"""
Check if the request should be permitted.
Raises an appropriate exception if the request is not permitted.
"""
if self.request.method != 'GET':
super().check_permissions(request)
class ProductBackOfficeGalleryCreateDestroyView(ProductBackOfficeMixinView, class ProductBackOfficeGalleryCreateDestroyView(ProductBackOfficeMixinView,
generics.CreateAPIView, CreateDestroyGalleryViewMixin):
generics.DestroyAPIView):
"""Resource for a create gallery for product for back-office users.""" """Resource for a create gallery for product for back-office users."""
serializer_class = serializers.ProductBackOfficeGallerySerializer serializer_class = serializers.ProductBackOfficeGallerySerializer
@ -37,24 +63,6 @@ class ProductBackOfficeGalleryCreateDestroyView(ProductBackOfficeMixinView,
return gallery return gallery
def create(self, request, *args, **kwargs):
"""Overridden create method"""
super().create(request, *args, **kwargs)
return Response(status=status.HTTP_201_CREATED)
def destroy(self, request, *args, **kwargs):
"""Override destroy method."""
gallery_obj = self.get_object()
if settings.USE_CELERY:
on_commit(lambda: delete_image.delay(image_id=gallery_obj.image.id,
completely=False))
else:
on_commit(lambda: delete_image(image_id=gallery_obj.image.id,
completely=False))
# Delete an instances of ProductGallery model
gallery_obj.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
class ProductBackOfficeGalleryListView(ProductBackOfficeMixinView, generics.ListAPIView): class ProductBackOfficeGalleryListView(ProductBackOfficeMixinView, generics.ListAPIView):
"""Resource for returning gallery for product for back-office users.""" """Resource for returning gallery for product for back-office users."""
@ -78,3 +86,32 @@ class ProductBackOfficeGalleryListView(ProductBackOfficeMixinView, generics.List
class ProductDetailBackOfficeView(ProductBackOfficeMixinView, generics.RetrieveUpdateDestroyAPIView): class ProductDetailBackOfficeView(ProductBackOfficeMixinView, generics.RetrieveUpdateDestroyAPIView):
"""Product back-office R/U/D view.""" """Product back-office R/U/D view."""
serializer_class = serializers.ProductBackOfficeDetailSerializer serializer_class = serializers.ProductBackOfficeDetailSerializer
class ProductListCreateBackOfficeView(BackOfficeListCreateMixin, ProductBackOfficeMixinView,
generics.ListCreateAPIView):
"""Product back-office list-create view."""
serializer_class = serializers.ProductBackOfficeDetailSerializer
class ProductTypeListCreateBackOfficeView(BackOfficeListCreateMixin, ProductTypeBackOfficeMixinView,
generics.ListCreateAPIView):
"""Product type back-office list-create view."""
serializer_class = serializers.ProductTypeBackOfficeDetailSerializer
class ProductTypeRUDBackOfficeView(BackOfficeListCreateMixin, ProductTypeBackOfficeMixinView,
generics.RetrieveUpdateDestroyAPIView):
"""Product type back-office retrieve-update-destroy view."""
serializer_class = serializers.ProductTypeBackOfficeDetailSerializer
class ProductTypeTagCategoryCreateBackOfficeView(ProductTypeBackOfficeMixinView,
generics.CreateAPIView):
"""View for attaching tag category to product type."""
serializer_class = serializers.ProductTypeTagCategorySerializer
def create(self, request, *args, **kwargs):
super().create(request, *args, **kwargs)
return Response(status=status.HTTP_201_CREATED)

View File

@ -1,10 +1,13 @@
from collections import namedtuple from collections import namedtuple
from django.conf import settings from django.conf import settings
from django.db.transaction import on_commit
from rest_framework import generics from rest_framework import generics
from rest_framework import status from rest_framework import status
from rest_framework.response import Response from rest_framework.response import Response
from gallery.tasks import delete_image
# JWT # JWT
# Login base view mixins # Login base view mixins
@ -95,3 +98,26 @@ class JWTGenericViewMixin(generics.GenericAPIView):
http_only=self.REFRESH_TOKEN_HTTP_ONLY, http_only=self.REFRESH_TOKEN_HTTP_ONLY,
secure=self.REFRESH_TOKEN_SECURE, secure=self.REFRESH_TOKEN_SECURE,
max_age=_cookies.get('max_age'))] max_age=_cookies.get('max_age'))]
class CreateDestroyGalleryViewMixin(generics.CreateAPIView,
generics.DestroyAPIView):
"""Mixin for creating and destroying entity linked with gallery."""
def create(self, request, *args, **kwargs):
"""Overridden create method"""
super().create(request, *args, **kwargs)
return Response(status=status.HTTP_201_CREATED)
def destroy(self, request, *args, **kwargs):
"""Override destroy method."""
gallery_obj = self.get_object()
if settings.USE_CELERY:
on_commit(lambda: delete_image.delay(image_id=gallery_obj.image.id,
completely=False))
else:
on_commit(lambda: delete_image(image_id=gallery_obj.image.id,
completely=False))
# Delete an instances of Gallery model
gallery_obj.delete()
return Response(status=status.HTTP_204_NO_CONTENT)