207 lines
8.0 KiB
Python
207 lines
8.0 KiB
Python
from collections import namedtuple
|
|
|
|
from django.conf import settings
|
|
from django.db.transaction import on_commit
|
|
from django.shortcuts import get_object_or_404
|
|
from rest_framework import generics, status
|
|
from rest_framework.decorators import action
|
|
from rest_framework.response import Response
|
|
|
|
from gallery.tasks import delete_image
|
|
from search_indexes.documents import es_update
|
|
from news.models import News
|
|
|
|
|
|
# JWT
|
|
# Login base view mixins
|
|
class JWTGenericViewMixin:
|
|
"""JWT view mixin"""
|
|
|
|
ACCESS_TOKEN_HTTP_ONLY = False
|
|
ACCESS_TOKEN_SECURE = False
|
|
|
|
REFRESH_TOKEN_HTTP_ONLY = False
|
|
REFRESH_TOKEN_SECURE = False
|
|
|
|
LOCALE_HTTP_ONLY = False
|
|
LOCALE_SECURE = False
|
|
|
|
COUNTRY_CODE_HTTP_ONLY = False
|
|
COUNTRY_CODE_SECURE = False
|
|
|
|
COOKIE = namedtuple('COOKIE', ['key', 'value', 'http_only', 'secure', 'max_age'])
|
|
|
|
def _put_data_in_cookies(self,
|
|
access_token: str = None,
|
|
refresh_token: str = None,
|
|
permanent: bool = None):
|
|
"""
|
|
cookies it is list that contain namedtuples
|
|
cookies would contain key, value and secure parameters.
|
|
"""
|
|
COOKIES = []
|
|
|
|
if hasattr(self, 'request'):
|
|
if hasattr(self.request, 'locale'):
|
|
COOKIES.append(self.COOKIE(key='locale',
|
|
value=self.request.locale,
|
|
http_only=self.ACCESS_TOKEN_HTTP_ONLY,
|
|
secure=self.LOCALE_SECURE,
|
|
max_age=settings.COOKIES_MAX_AGE if permanent else None))
|
|
if hasattr(self.request, 'country_code'):
|
|
COOKIES.append(self.COOKIE(key='country_code',
|
|
value=self.request.country_code,
|
|
http_only=self.COUNTRY_CODE_HTTP_ONLY,
|
|
secure=self.COUNTRY_CODE_SECURE,
|
|
max_age=settings.COOKIES_MAX_AGE if permanent else None))
|
|
if access_token:
|
|
COOKIES.append(self.COOKIE(key='access_token',
|
|
value=access_token,
|
|
http_only=self.ACCESS_TOKEN_HTTP_ONLY,
|
|
secure=self.ACCESS_TOKEN_SECURE,
|
|
max_age=settings.COOKIES_MAX_AGE if permanent else None))
|
|
if refresh_token:
|
|
COOKIES.append(self.COOKIE(key='refresh_token',
|
|
value=refresh_token,
|
|
http_only=self.REFRESH_TOKEN_HTTP_ONLY,
|
|
secure=self.REFRESH_TOKEN_SECURE,
|
|
max_age=settings.COOKIES_MAX_AGE if permanent else None))
|
|
return COOKIES
|
|
|
|
def _put_cookies_in_response(self, cookies: list, response: Response):
|
|
"""Update COOKIES in response from namedtuple"""
|
|
for cookie in cookies:
|
|
response.set_cookie(key=cookie.key,
|
|
value=cookie.value,
|
|
secure=cookie.secure,
|
|
httponly=cookie.http_only,
|
|
max_age=cookie.max_age,
|
|
domain=settings.COOKIE_DOMAIN)
|
|
return response
|
|
|
|
def _get_tokens_from_cookies(self, request, cookies: dict = None):
|
|
"""Get user tokens from cookies and put in namedtuple"""
|
|
_cookies = request.COOKIES or cookies
|
|
return [self.COOKIE(key='access_token',
|
|
value=_cookies.get('access_token'),
|
|
http_only=self.ACCESS_TOKEN_HTTP_ONLY,
|
|
secure=self.ACCESS_TOKEN_SECURE,
|
|
max_age=_cookies.get('max_age')),
|
|
self.COOKIE(key='refresh_token',
|
|
value=_cookies.get('refresh_token'),
|
|
http_only=self.REFRESH_TOKEN_HTTP_ONLY,
|
|
secure=self.REFRESH_TOKEN_SECURE,
|
|
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)
|
|
|
|
|
|
class BaseCreateDestroyMixinView(generics.CreateAPIView, generics.DestroyAPIView):
|
|
"""Base Create Destroy mixin."""
|
|
|
|
_model = None
|
|
serializer_class = None
|
|
lookup_field = 'slug'
|
|
|
|
def get_base_object(self):
|
|
if isinstance(self._model, News):
|
|
get_object_or_404(self._model, slugs__values__contains=[self.kwargs['slug']])
|
|
return get_object_or_404(self._model, slug=self.kwargs['slug'])
|
|
|
|
def es_update_base_object(self):
|
|
es_update(self.get_base_object())
|
|
|
|
def perform_create(self, serializer):
|
|
serializer.save()
|
|
self.es_update_base_object()
|
|
|
|
def perform_destroy(self, instance):
|
|
instance.delete()
|
|
self.es_update_base_object()
|
|
|
|
|
|
class FavoritesCreateDestroyMixinView(BaseCreateDestroyMixinView):
|
|
"""Favorites Create Destroy mixin."""
|
|
|
|
def get_object(self):
|
|
"""
|
|
Returns the object the view is displaying.
|
|
"""
|
|
obj = self.get_base_object()
|
|
favorites = get_object_or_404(obj.favorites.filter(user=self.request.user))
|
|
# May raise a permission denied
|
|
self.check_object_permissions(self.request, favorites)
|
|
return favorites
|
|
|
|
|
|
class CarouselCreateDestroyMixinView(BaseCreateDestroyMixinView):
|
|
"""Carousel Create Destroy mixin."""
|
|
|
|
lookup_field = 'id'
|
|
|
|
def get_base_object(self):
|
|
establishment_pk = self.kwargs.get('pk')
|
|
establishment_slug = self.kwargs.get('slug')
|
|
|
|
search_kwargs = {'id': establishment_pk} if establishment_pk else {'slug': establishment_slug}
|
|
return get_object_or_404(self._model, **search_kwargs)
|
|
|
|
def get_object(self):
|
|
"""
|
|
Returns the object the view is displaying.
|
|
"""
|
|
obj = self.get_base_object()
|
|
carousels = get_object_or_404(obj.carousels.all())
|
|
# May raise a permission denied
|
|
# TODO: возможно нужны пермишены
|
|
# self.check_object_permissions(self.request, carousels)
|
|
return carousels
|
|
|
|
|
|
# BackOffice user`s views & viewsets
|
|
class BindObjectMixin:
|
|
"""Bind object mixin."""
|
|
|
|
def get_serializer_class(self):
|
|
if self.action == 'bind_object':
|
|
return self.bind_object_serializer_class
|
|
return self.serializer_class
|
|
|
|
def perform_binding(self, serializer):
|
|
raise NotImplemented
|
|
|
|
def perform_unbinding(self, serializer):
|
|
raise NotImplemented
|
|
|
|
@action(methods=['post', 'delete'], detail=True, url_path='bind-object')
|
|
def bind_object(self, request, pk=None):
|
|
serializer = self.get_serializer(data=request.data)
|
|
serializer.is_valid(raise_exception=True)
|
|
if request.method == 'POST':
|
|
self.perform_binding(serializer)
|
|
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
|
elif request.method == 'DELETE':
|
|
self.perform_unbinding(serializer)
|
|
return Response(status=status.HTTP_204_NO_CONTENT)
|