diff --git a/apps/authorization/views/common.py b/apps/authorization/views/common.py index 274a866d..60912337 100644 --- a/apps/authorization/views/common.py +++ b/apps/authorization/views/common.py @@ -20,10 +20,7 @@ from authorization.models import Application from authorization.serializers import common as serializers from utils import exceptions as utils_exceptions from utils.views import (JWTGenericViewMixin, - JWTCreateAPIView, - JWTDestroyAPIView, - JWTUpdateAPIView, - JWTRetrieveAPIView) + JWTCreateAPIView) # Mixins @@ -33,7 +30,7 @@ class JWTAuthViewMixin(JWTCreateAPIView): def post(self, request, *args, **kwargs): """Implement POST method""" - _locale = request.COOKIES.get('locale') + _locale = self._get_locale(request) try: locale = self._check_locale(locale=_locale) @@ -102,7 +99,7 @@ class OAuth2ViewMixin(CsrfExemptMixin, OAuthLibMixin, BaseOAuth2ViewMixin): # Sign in via Facebook -class OAuth2SignUpView(OAuth2ViewMixin, JWTAuthViewMixin): +class OAuth2SignUpView(OAuth2ViewMixin, JWTGenericViewMixin): """ Implements an endpoint to convert a provider token to an access token @@ -130,7 +127,7 @@ class OAuth2SignUpView(OAuth2ViewMixin, JWTAuthViewMixin): def post(self, request, *args, **kwargs): - _locale = request.COOKIES.get('locale') + _locale = self._get_locale(request) try: locale = self._check_locale(locale=_locale) # Preparing request data @@ -187,7 +184,7 @@ class SignUpView(JWTCreateAPIView): def post(self, request, *args, **kwargs): """Implement POST-method""" - _locale = request.COOKIES.get('locale') + _locale = self._get_locale(request) try: locale = self._check_locale(locale=_locale) @@ -224,7 +221,7 @@ class RefreshTokenView(JWTGenericViewMixin): serializer_class = serializers.RefreshTokenSerializer def post(self, request, *args, **kwargs): - _locale = request.COOKIES.get('locale') + _locale = self._get_locale(request) try: locale = self._check_locale(locale=_locale) @@ -245,13 +242,13 @@ class RefreshTokenView(JWTGenericViewMixin): # Logout -class LogoutView(JWTAuthViewMixin): +class LogoutView(JWTGenericViewMixin): """Logout user""" serializer_class = serializers.LogoutSerializer def create(self, request, *args, **kwargs): """Override create method""" - _locale = request.COOKIES.get('locale') + _locale = self._get_locale(request) try: locale = self._check_locale(locale=_locale) diff --git a/apps/news/models.py b/apps/news/models.py index a0f0cc97..e30142f0 100644 --- a/apps/news/models.py +++ b/apps/news/models.py @@ -1,14 +1,47 @@ +from django.contrib.postgres.fields import JSONField +from django.contrib.postgres.fields.jsonb import KeyTextTransform from django.db import models -from utils.models import ProjectBaseMixin, BaseAttributes -from django.contrib.postgres.fields import JSONField, ArrayField from django.utils.translation import gettext_lazy as _ +from utils.models import ProjectBaseMixin, BaseAttributes + class NewsType(models.Model): """NewsType model.""" name = models.CharField(_('name'), max_length=250) +class NewsManager(models.Manager): + """Manager for model News""" + + def annotate_localized_fields(self, locale): + """Return queryset by locale""" + prefix = 'trans' + + # Prepare fields to localization (only JSONField can be localized) + fields = [field.name for field in self.model._meta.fields if isinstance(field, JSONField)] + + # Check filters to check if field has localization + filters = {f'{field}__has_key': locale for field in fields} + # Filter QuerySet by prepared filters + queryset = self.filter(**filters) + + # Prepare field for annotator + localized_fields = {f'{field}_{prefix}': KeyTextTransform(f'{locale}', field) for field in fields} + + # Annotate them + for _ in fields: + queryset = queryset.annotate(**localized_fields) + return queryset + + +class NewsQuerySet(models.QuerySet): + """QuerySet for model News""" + def by_type(self, news_type): + """Filter News by type""" + return self.filter(news_type__name=news_type) + + class News(BaseAttributes): """News model.""" news_type = models.ForeignKey( @@ -34,6 +67,8 @@ class News(BaseAttributes): # TODO: metadata_keys - описание ключей для динамического построения полей метаданных # TODO: metadata_values - Описание значений для динамических полей из MetadataKeys + objects = NewsManager.from_queryset(NewsQuerySet)() + class Meta: verbose_name = _('news') verbose_name_plural = _('news') diff --git a/apps/news/serializers/common.py b/apps/news/serializers/common.py index 453b7fe9..d43b16cc 100644 --- a/apps/news/serializers/common.py +++ b/apps/news/serializers/common.py @@ -8,18 +8,23 @@ class NewsSerializer(serializers.ModelSerializer): """News serializer.""" address = AddressSerializer() + # Localized fields + title_trans = serializers.CharField() + subtitle_trans = serializers.CharField() + description_trans = serializers.CharField() + class Meta: model = models.News fields = [ 'id', 'news_type', - 'title', - 'subtitle', - 'description', 'start', 'end', 'playlist', - 'address' + 'address', + 'title_trans', + 'subtitle_trans', + 'description_trans', ] diff --git a/apps/news/views/common.py b/apps/news/views/common.py index f3c2d7d1..78a74bee 100644 --- a/apps/news/views/common.py +++ b/apps/news/views/common.py @@ -1,12 +1,22 @@ from rest_framework import generics, permissions from news.models import News from news.serializers import common as serializers -from utils.views import JWTGenericViewMixin +from utils.views import (JWTGenericViewMixin, + JWTListAPIView) -class NewsList(generics.ListAPIView): +# Mixins +class NewsViewMixin(JWTGenericViewMixin): + """View mixin for News model""" + + def get_queryset(self, *args, **kwargs): + """Override get_queryset method""" + return News.objects.annotate_localized_fields( + locale=self._get_locale(request=self.request)) + + +class NewsList(NewsViewMixin, JWTListAPIView): """News list view.""" - queryset = News.objects.all() permission_classes = (permissions.AllowAny, ) serializer_class = serializers.NewsSerializer diff --git a/apps/utils/views.py b/apps/utils/views.py index 2566f41f..2040d55a 100644 --- a/apps/utils/views.py +++ b/apps/utils/views.py @@ -17,6 +17,10 @@ class JWTGenericViewMixin(generics.GenericAPIView): REFRESH_TOKEN_HTTP_ONLY = False REFRESH_TOKEN_SECURE = False COOKIE = namedtuple('COOKIE', ['key', 'value', 'http_only', 'secure']) + + def _get_locale(self, request): + """Get locale from request""" + return request.COOKIES.get('locale') def _check_locale(self, locale: str): @@ -77,12 +81,42 @@ class JWTGenericViewMixin(generics.GenericAPIView): secure=self.REFRESH_TOKEN_SECURE)] +class JWTListAPIView(JWTGenericViewMixin, generics.ListAPIView): + """ + Concrete view for creating a model instance. + """ + def get(self, request, *args, **kwargs): + _locale = self._get_locale(request=request) + try: + locale = self._check_locale(locale=_locale) + + queryset = self.filter_queryset(self.get_queryset()) + + page = self.paginate_queryset(queryset) + if page is not None: + serializer = self.get_serializer(page, many=True) + response = self.get_paginated_response(serializer.data) + else: + serializer = self.get_serializer(queryset, many=True) + response = Response(serializer.data) + + access_token, refresh_token = self._get_tokens_from_cookies(request) + except exceptions.LocaleNotExisted: + raise exceptions.LocaleNotExisted(locale=_locale) + else: + return self._put_cookies_in_response( + cookies=self._put_data_in_cookies(locale=locale, + access_token=access_token.value, + refresh_token=refresh_token.value), + response=response) + + class JWTCreateAPIView(JWTGenericViewMixin, generics.CreateAPIView): """ Concrete view for creating a model instance. """ def post(self, request, *args, **kwargs): - _locale = request.COOKIES.get('locale') + _locale = self._get_locale(request=request) try: locale = self._check_locale(locale=_locale) @@ -108,7 +142,7 @@ class JWTRetrieveAPIView(JWTGenericViewMixin, generics.RetrieveAPIView): """ def get(self, request, *args, **kwargs): """Implement GET method""" - _locale = request.COOKIES.get('locale') + _locale = self._get_locale(request=request) try: locale = self._check_locale(locale=_locale) @@ -138,7 +172,7 @@ class JWTDestroyAPIView(JWTGenericViewMixin, generics.DestroyAPIView): Concrete view for deleting a model instance. """ def delete(self, request, *args, **kwargs): - _locale = request.COOKIES.get('locale') + _locale = self._get_locale(request=request) try: locale = self._check_locale(locale=_locale) instance = self.get_object() @@ -161,7 +195,7 @@ class JWTUpdateAPIView(JWTGenericViewMixin, generics.UpdateAPIView): Concrete view for updating a model instance. """ def put(self, request, *args, **kwargs): - _locale = request.COOKIES.get('locale') + _locale = self._get_locale(request=request) try: locale = self._check_locale(locale=_locale) partial = kwargs.pop('partial', False)