Merge branch 'develop' into feature/establishment

This commit is contained in:
evgeniy-st 2019-08-28 15:35:21 +03:00
commit ba00bf1b00
58 changed files with 447 additions and 215 deletions

View File

@ -6,6 +6,6 @@ from account.views import common as views
app_name = 'account' app_name = 'account'
urlpatterns = [ urlpatterns = [
path('user/', views.UserView.as_view(), name='user_get_update'), path('user/', views.UserView.as_view(), name='user-get-update'),
path('device/', views.FCMDeviceViewSet.as_view(), name='fcm_device_create'),
] ]

View File

@ -6,5 +6,5 @@ from advertisement.views import web as views
app_name = 'advertisement' app_name = 'advertisement'
urlpatterns = [ urlpatterns = [
path('list/', views.AdvertisementListView.as_view(), name='advertisements_list') path('', views.AdvertisementListView.as_view(), name='list')
] ]

View File

@ -1,20 +1,18 @@
"""Common serializer for application authorization""" """Common serializer for application authorization"""
from django.conf import settings
from django.contrib.auth import authenticate
from django.contrib.auth import password_validation as password_validators from django.contrib.auth import password_validation as password_validators
from django.db.models import Q
from rest_framework import serializers from rest_framework import serializers
from rest_framework import validators as rest_validators from rest_framework import validators as rest_validators
from django.contrib.auth import authenticate # JWT
from django.db.models import Q from rest_framework_simplejwt import tokens
from django.conf import settings
from account import models as account_models from account import models as account_models
from authorization.models import Application, BlacklistedAccessToken from authorization.models import Application, BlacklistedAccessToken
from utils import exceptions as utils_exceptions from utils import exceptions as utils_exceptions
from utils import methods as utils_methods from utils import methods as utils_methods
# JWT
from rest_framework_simplejwt import tokens
JWT_SETTINGS = settings.SIMPLE_JWT JWT_SETTINGS = settings.SIMPLE_JWT
@ -202,8 +200,8 @@ class LogoutSerializer(serializers.ModelSerializer):
def validate(self, attrs): def validate(self, attrs):
"""Override validated data""" """Override validated data"""
request = self.context.get('request') request = self.context.get('request')
token = request._request.headers.get('Authorization') \ token = request.headers.get('Authorization') \
.split(' ')[::-1][0] .split(' ')[::-1][0]
access_token = tokens.AccessToken(token) access_token = tokens.AccessToken(token)
# Prepare validated data # Prepare validated data
attrs['user'] = request.user attrs['user'] = request.user

View File

@ -23,27 +23,18 @@ urlpatterns_social_django = [
urlpatterns_oauth2 = [ urlpatterns_oauth2 = [
path('oauth2/signup/facebook/', views.OAuth2SignUpView.as_view(), path('oauth2/signup/facebook/', views.OAuth2SignUpView.as_view(),
name='oauth2-signup-facebook'), name='oauth2-signup-facebook'),
# for admin sign in page # for sign up via facebook
path('oauth2/token/', drf_social_oauth2_views .TokenView.as_view(), path('oauth2/token/', drf_social_oauth2_views .TokenView.as_view(), name="token"),
name="token"),
] ]
urlpatterns_jwt = [ urlpatterns_jwt = [
path('signup/', views.SignUpView.as_view(), path('signup/', views.SignUpView.as_view(), name='signup'),
name='signup'), path('login/', views.LoginByUsernameOrEmailView.as_view(), name='login'),
# sign in path('refresh-token/', views.RefreshTokenView.as_view(), name="refresh-token"),
path('login/', views.LoginByUsernameOrEmailView.as_view(), path('logout/', views.LogoutView.as_view(), name="logout"),
name='login'),
# refresh token
path('refresh-token/', views.RefreshTokenView.as_view(),
name="refresh-token"),
# logout
path('logout/', views.LogoutView.as_view(),
name="logout"),
] ]
urlpatterns = urlpatterns_jwt + \ urlpatterns = urlpatterns_jwt + \
urlpatterns_oauth2 + \ urlpatterns_oauth2 + \
urlpatterns_social_django # for social oauth2 urlpatterns_social_django # for social oauth2

View File

@ -210,9 +210,9 @@ class LogoutView(JWTGenericViewMixin):
"""Logout user""" """Logout user"""
serializer_class = serializers.LogoutSerializer serializer_class = serializers.LogoutSerializer
def create(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
"""Override create method""" """Override create method"""
serializer = self.get_serializer(data=request.data) serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True) serializer.is_valid(raise_exception=True)
serializer.save() serializer.save()
return Response(status=status.HTTP_200_OK) return Response(status=status.HTTP_204_NO_CONTENT)

View File

@ -1,28 +1,62 @@
from rest_framework import serializers from rest_framework import serializers
from collection import models from collection import models
from gallery import models as gallery_models
from location import models as location_models
class CollectionSerializer(serializers.ModelSerializer): class CollectionSerializer(serializers.ModelSerializer):
"""Collection serializer""" """Collection serializer"""
image = serializers.URLField(source='image.get_image_url') # RESPONSE
image_url = serializers.SerializerMethodField()
# COMMON
block_size = serializers.JSONField()
is_publish = serializers.BooleanField()
on_top = serializers.BooleanField()
# REQUEST
start = serializers.DateTimeField(write_only=True)
end = serializers.DateTimeField(write_only=True)
filters = serializers.JSONField(write_only=True)
selectors = serializers.JSONField(write_only=True)
targets = serializers.JSONField(write_only=True)
country = serializers.PrimaryKeyRelatedField(
queryset=location_models.Country.objects.all(),
write_only=True)
image = serializers.PrimaryKeyRelatedField(
queryset=gallery_models.Image.objects.all(),
write_only=True)
class Meta: class Meta:
model = models.Collection model = models.Collection
fields = [ fields = [
'id', 'id',
'name', 'name',
'block_size', 'start',
'end',
'image', 'image',
'on_top' 'image_url',
'is_publish',
'on_top',
'filters',
'selectors',
'targets',
'country',
'block_size',
] ]
def get_image_url(self, obj):
"""Return absolute image URL"""
return obj.image.get_full_image_url(request=self.context.get('request'))
class CollectionItemSerializer(serializers.ModelSerializer): class CollectionItemSerializer(serializers.ModelSerializer):
"""CollectionItem serializer""" """CollectionItem serializer"""
class Meta: class Meta:
model = models.CollectionItem model = models.CollectionItem
fields = [ fields = [
'id',
'collection', 'collection',
'item_type', 'item_type',
'item_ids' 'item_ids'
@ -34,6 +68,7 @@ class GuideSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = models.Guide model = models.Guide
fields = [ fields = [
'id',
'name', 'name',
'start', 'start',
'end', 'end',

View File

@ -6,36 +6,13 @@ from collection.views import common as views
app_name = 'collection' app_name = 'collection'
urlpatterns = [ urlpatterns = [
path('list/', views.CollectionListView.as_view(), path('', views.CollectionListView.as_view(), name='list'),
name='collections_list'), path('<int:pk>/', views.CollectionRetrieveView.as_view(), name='detail'),
# path('create/', views.CollectionCreateView.as_view(),
# name='collection_create'),
# path('<int:pk>/', views.CollectionRetrieveView.as_view(),
# name='collection_retrieve'),
# path('<int:pk>/delete/', views.CollectionDestroyView.as_view(),
# name='collection_destroy'),
# path('<int:pk>/update/', views.CollectionUpdateView.as_view(),
# name='collection_update'),
path('collection_items/', views.CollectionItemListView.as_view(), path('items/', views.CollectionItemListView.as_view(), name='collection-items-list'),
name='collection_items_list'), path('items/<int:pk>/', views.CollectionItemRetrieveView.as_view(),
# path('collection_items/create/', views.CollectionItemCreateView.as_view(), name='collection-items-detail'),
# name='collection_items_create'),
# path('collection_items/<int:pk>/', views.CollectionItemRetrieveView.as_view(),
# name='collection_items_retrieve'),
# path('collection_items/<int:pk>/delete/', views.CollectionDestroyView.as_view(),
# name='collection_items_destroy'),
# path('collection_items/<int:pk>/update/', views.CollectionItemUpdateView.as_view(),
# name='collection_items_update'),
path('guides/', views.GuideListView.as_view(), path('guides/', views.GuideListView.as_view(), name='guides-list'),
name='guides_list'), path('guides/<int:pk>/', views.GuideRetrieveView.as_view(), name='guides-detail'),
# path('guides/create/', views.GuideCreateView.as_view(),
# name='guide_create'),
# path('guides/<int:pk>/', views.GuideRetrieveView.as_view(),
# name='guide_retrieve'),
# path('guides/<int:pk>/delete/', views.GuideDestroyView.as_view(),
# name='guide_destroy'),
# path('guides/<int:pk>/update/', views.GuideUpdateView.as_view(),
# name='guide_update'),
] ]

View File

@ -25,6 +25,7 @@ class GuideViewMixin(generics.GenericAPIView):
# Views # Views
# Collections
class CollectionListView(CollectionViewMixin, generics.ListAPIView): class CollectionListView(CollectionViewMixin, generics.ListAPIView):
"""List Collection view""" """List Collection view"""
pagination_class = None pagination_class = None
@ -40,24 +41,11 @@ class CollectionListView(CollectionViewMixin, generics.ListAPIView):
class CollectionRetrieveView(CollectionViewMixin, generics.RetrieveAPIView): class CollectionRetrieveView(CollectionViewMixin, generics.RetrieveAPIView):
"""Retrieve Collection view""" """Retrieve Collection view"""
permission_classes = (permissions.AllowAny,)
serializer_class = serializers.CollectionSerializer serializer_class = serializers.CollectionSerializer
class CollectionCreateView(CollectionViewMixin, generics.CreateAPIView): # CollectionItem
"""Create Collection view"""
serializer_class = serializers.CollectionSerializer
class CollectionDestroyView(CollectionViewMixin, generics.DestroyAPIView):
"""Destroy Collection view"""
serializer_class = serializers.CollectionSerializer
class CollectionUpdateView(CollectionViewMixin, generics.UpdateAPIView):
"""Update Collection view"""
serializer = serializers.CollectionSerializer
class CollectionItemListView(CollectionItemViewMixin, generics.ListAPIView): class CollectionItemListView(CollectionItemViewMixin, generics.ListAPIView):
"""List CollectionItem view""" """List CollectionItem view"""
permission_classes = (permissions.AllowAny,) permission_classes = (permissions.AllowAny,)
@ -66,24 +54,11 @@ class CollectionItemListView(CollectionItemViewMixin, generics.ListAPIView):
class CollectionItemRetrieveView(CollectionItemViewMixin, generics.RetrieveAPIView): class CollectionItemRetrieveView(CollectionItemViewMixin, generics.RetrieveAPIView):
"""Retrieve CollectionItem view""" """Retrieve CollectionItem view"""
permission_classes = (permissions.AllowAny,)
serializer_class = serializers.CollectionItemSerializer serializer_class = serializers.CollectionItemSerializer
class CollectionItemCreateView(CollectionItemViewMixin, generics.CreateAPIView): # Guide
"""Create CollectionItem view"""
serializer_class = serializers.CollectionItemSerializer
class CollectionItemDestroyView(CollectionItemViewMixin, generics.DestroyAPIView):
"""Destroy CollectionItem view"""
serializer_class = serializers.CollectionItemSerializer
class CollectionItemUpdateView(CollectionItemViewMixin, generics.UpdateAPIView):
"""Update CollectionItem view"""
serializer = serializers.CollectionItemSerializer
class GuideListView(GuideViewMixin, generics.ListAPIView): class GuideListView(GuideViewMixin, generics.ListAPIView):
"""List Guide view""" """List Guide view"""
permission_classes = (permissions.AllowAny,) permission_classes = (permissions.AllowAny,)
@ -92,20 +67,5 @@ class GuideListView(GuideViewMixin, generics.ListAPIView):
class GuideRetrieveView(GuideViewMixin, generics.RetrieveAPIView): class GuideRetrieveView(GuideViewMixin, generics.RetrieveAPIView):
"""Retrieve Guide view""" """Retrieve Guide view"""
permission_classes = (permissions.AllowAny,)
serializer_class = serializers.GuideSerializer serializer_class = serializers.GuideSerializer
class GuideCreateView(GuideViewMixin, generics.CreateAPIView):
"""Create Guide view"""
serializer_class = serializers.GuideSerializer
class GuideDestroyView(GuideViewMixin, generics.DestroyAPIView):
"""Destroy Guide view"""
serializer_class = serializers.GuideSerializer
class GuideUpdateView(GuideViewMixin, generics.UpdateAPIView):
"""Update Guide view"""
serializer = serializers.GuideSerializer

View File

@ -1,4 +1,26 @@
"""Establishment admin conf.""" """Establishment admin conf."""
from django.contrib import admin from django.contrib import admin
from establishment import models
from django.contrib.contenttypes.admin import GenericTabularInline
from main.models import Award
# Register your models here.
@admin.register(models.EstablishmentType)
class EstablishmentTypeAdmin(admin.ModelAdmin):
"""EstablishmentType admin."""
@admin.register(models.EstablishmentSubType)
class EstablishmentSubTypeAdmin(admin.ModelAdmin):
"""EstablishmentSubType admin."""
class AwardInline(GenericTabularInline):
model = Award
extra = 0
@admin.register(models.Establishment)
class EstablishmentAdmin(admin.ModelAdmin):
"""Establishment admin."""
inlines = [AwardInline, ]

View File

@ -4,6 +4,7 @@ from django.db import models
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from location.models import Address from location.models import Address
from utils.models import ProjectBaseMixin, ImageMixin, TJSONField, TraslatedFieldsMixin from utils.models import ProjectBaseMixin, ImageMixin, TJSONField, TraslatedFieldsMixin
from django.contrib.contenttypes import fields as generic
# todo: establishment type&subtypes check # todo: establishment type&subtypes check
@ -84,6 +85,7 @@ class Establishment(ProjectBaseMixin, ImageMixin, TraslatedFieldsMixin):
price_level = models.PositiveIntegerField(blank=True, null=True, price_level = models.PositiveIntegerField(blank=True, null=True,
default=None, default=None,
verbose_name=_('Price level')) verbose_name=_('Price level'))
awards = generic.GenericRelation(to='main.Award')
class Meta: class Meta:
"""Meta class.""" """Meta class."""

View File

@ -7,5 +7,5 @@ app_name = 'establishment'
urlpatterns = [ urlpatterns = [
path('establishments/', views.EstablishmentListView.as_view(), name='establishment-list'), path('', views.EstablishmentListView.as_view(), name='list'),
] ]

View File

@ -10,5 +10,6 @@ class ImageSerializer(serializers.ModelSerializer):
"""Meta class""" """Meta class"""
model = models.Image model = models.Image
fields = ( fields = (
'id',
'image', 'image',
) )

View File

@ -6,6 +6,5 @@ from . import views
app_name = 'gallery' app_name = 'gallery'
urlpatterns = [ urlpatterns = [
path('upload/', views.ImageUploadView.as_view(), path('upload/', views.ImageUploadView.as_view(), name='upload-image')
name='upload_image')
] ]

View File

@ -6,25 +6,15 @@ from location import views
app_name = 'location' app_name = 'location'
urlpatterns = [ urlpatterns = [
path('country/', views.CountryListView.as_view(), name='country-list'), path('addresses/', views.AddressListView.as_view(), name='address-list'),
path('country/<int:pk>/', views.CountryRetrieveView.as_view(), path('addresses/<int:pk>/', views.AddressRetrieveView.as_view(), name='address-retrieve'),
name='country-retrieve'),
path('region/', views.RegionListView.as_view(), name='region_list'), path('cities/', views.CityListView.as_view(), name='city-list'),
path('region/create/', views.RegionCreateView.as_view(), name='region_create'), path('cities/<int:pk>/', views.CityRetrieveView.as_view(), name='city-retrieve'),
path('region/<int:pk>/', views.RegionRetrieveView.as_view(), name='region_retrieve'),
path('region/<int:pk>/delete/', views.RegionDestroyView.as_view(), name='region_destroy'),
path('region/<int:pk>/update/', views.RegionUpdateView.as_view(), name='region_update'),
path('city/', views.CityListView.as_view(), name='city_list'), path('countries/', views.CountryListView.as_view(), name='country-list'),
path('city/create/', views.CityCreateView.as_view(), name='city_create'), path('countries/<int:pk>/', views.CountryRetrieveView.as_view(), name='country-retrieve'),
path('city/<int:pk>/', views.CityRetrieveView.as_view(), name='city_retrieve'),
path('city/<int:pk>/delete/', views.CityDestroyView.as_view(), name='city_destroy'),
path('city/<int:pk>/update/', views.CityUpdateView.as_view(), name='city_update'),
path('address/', views.AddressListView.as_view(), name='address_list'), path('regions/', views.RegionListView.as_view(), name='region-list'),
path('address/create/', views.AddressCreateView.as_view(), name='address_create'), path('regions/<int:pk>/', views.RegionRetrieveView.as_view(), name='region-retrieve'),
path('address/<int:pk>/', views.AddressRetrieveView.as_view(), name='address_retrieve'),
path('address/<int:pk>/delete/', views.AddressDestroyView.as_view(), name='address_destroy'),
path('address/<int:pk>/update/', views.AddressUpdateView.as_view(), name='address_update'),
] ]

View File

@ -13,9 +13,6 @@ class CountryViewMixin(JWTGenericViewMixin, generics.GenericAPIView):
serializer_class = serializers.CountrySerializer serializer_class = serializers.CountrySerializer
permission_classes = (permissions.AllowAny, ) permission_classes = (permissions.AllowAny, )
def get_queryset(self):
return models.Country.objects.annotate_localized_fields(locale=self.request.locale)
class RegionViewMixin(generics.GenericAPIView): class RegionViewMixin(generics.GenericAPIView):
"""View Mixin for model Region""" """View Mixin for model Region"""
@ -41,10 +38,16 @@ class CountryListView(CountryViewMixin, generics.ListAPIView):
pagination_class = None pagination_class = None
def get_queryset(self):
return models.Country.objects.annotate_localized_fields(locale=self.request.locale)
class CountryRetrieveView(CountryViewMixin, generics.RetrieveAPIView): class CountryRetrieveView(CountryViewMixin, generics.RetrieveAPIView):
"""Retrieve view for model Country.""" """Retrieve view for model Country."""
def get_queryset(self):
return models.Country.objects.annotate_localized_fields(locale=self.request.locale)
# Region # Region
class RegionCreateView(RegionViewMixin, generics.CreateAPIView): class RegionCreateView(RegionViewMixin, generics.CreateAPIView):

View File

@ -11,3 +11,15 @@ class SiteSettingsAdmin(admin.ModelAdmin):
@admin.register(models.Feature) @admin.register(models.Feature)
class FeatureAdmin(admin.ModelAdmin): class FeatureAdmin(admin.ModelAdmin):
"""Feature admin conf.""" """Feature admin conf."""
@admin.register(models.AwardType)
class AwardTypeAdmin(admin.ModelAdmin):
"""Award type admin conf."""
@admin.register(models.Award)
class AwardAdmin(admin.ModelAdmin):
"""Award admin conf."""
# list_display = ['id', '__str__']
# list_display_links = ['id', '__str__']

View File

@ -0,0 +1,35 @@
# Generated by Django 2.2.4 on 2019-08-27 13:46
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('location', '0005_auto_20190822_1144'),
('contenttypes', '0002_remove_content_type_name'),
('main', '0006_auto_20190822_1118'),
]
operations = [
migrations.CreateModel(
name='AwardType',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=255, verbose_name='name')),
('country', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='location.Country', verbose_name='country')),
],
),
migrations.CreateModel(
name='Award',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(default='', max_length=255, verbose_name='title')),
('vintage_year', models.CharField(default='', max_length=255, verbose_name='vintage year')),
('object_id', models.PositiveIntegerField()),
('award_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='main.AwardType')),
('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType')),
],
),
]

View File

@ -0,0 +1,14 @@
# Generated by Django 2.2.4 on 2019-08-27 14:33
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('main', '0007_award_awardtype'),
('main', '0007_sitefeature_main'),
]
operations = [
]

View File

@ -0,0 +1,17 @@
# Generated by Django 2.2.4 on 2019-08-28 07:03
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('main', '0008_merge_20190827_1433'),
]
operations = [
migrations.RemoveField(
model_name='award',
name='title',
),
]

View File

@ -0,0 +1,19 @@
# Generated by Django 2.2.4 on 2019-08-28 07:05
import django.contrib.postgres.fields.jsonb
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('main', '0009_remove_award_title'),
]
operations = [
migrations.AddField(
model_name='award',
name='title',
field=django.contrib.postgres.fields.jsonb.JSONField(blank=True, default=None, help_text='{"en":"some text"}', null=True, verbose_name='title'),
),
]

View File

@ -1,9 +1,10 @@
"""Main app models.""" """Main app models."""
from django.conf import settings from django.conf import settings
from django.contrib.contenttypes import fields as generic
from django.contrib.postgres.fields import JSONField from django.contrib.postgres.fields import JSONField
from django.db import models from django.db import models
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.contrib.contenttypes.models import ContentType
from location.models import Country from location.models import Country
from main import methods from main import methods
from utils.models import ProjectBaseMixin from utils.models import ProjectBaseMixin
@ -182,3 +183,32 @@ class SiteFeature(ProjectBaseMixin):
verbose_name = _('Site feature') verbose_name = _('Site feature')
verbose_name_plural = _('Site features') verbose_name_plural = _('Site features')
unique_together = ('site_settings', 'feature') unique_together = ('site_settings', 'feature')
class Award(models.Model):
"""Award model."""
award_type = models.ForeignKey('main.AwardType', on_delete=models.CASCADE)
title = JSONField(
_('title'), null=True, blank=True,
default=None, help_text='{"en":"some text"}')
vintage_year = models.CharField(_('vintage year'), max_length=255, default='')
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey('content_type', 'object_id')
def __str__(self):
title = 'None'
if self.title and 'en' in self.title:
title = self.title['en']
return f'id:{self.id}-{title}'
class AwardType(models.Model):
"""AwardType model."""
country = models.ForeignKey(
'location.Country', verbose_name=_('country'), on_delete=models.CASCADE)
name = models.CharField(_('name'), max_length=255)
def __str__(self):
return self.name

View File

@ -68,3 +68,15 @@ class SiteSettingsSerializer(serializers.ModelSerializer):
# 'site_settings', # 'site_settings',
# 'feature', # 'feature',
# ) # )
class AwardSerializer(serializers.ModelSerializer):
"""Award serializer."""
class Meta:
model = models.Award
fields = [
# 'title',
'award_type',
'vintage_year',
]

View File

@ -5,16 +5,8 @@ from main import views
app = 'main' app = 'main'
urlpatterns = [ urlpatterns = [
path('determine-site/', views.DetermineSiteView.as_view(), path('determine-site/', views.DetermineSiteView.as_view(), name='determine-site'),
name='determine-site'), path('site-settings/<subdomain>/', views.SiteSettingsView.as_view(), name='site-settings'),
path('site-settings/<subdomain>/', views.SiteSettingsView.as_view(), path('awards/', views.AwardView.as_view(), name='awards_list'),
name='site-settings', ), path('awards/<int:pk>/', views.AwardRetrieveView.as_view(), name='awards_retrieve'),
# path('features/', views.FeaturesLCView.as_view(),
# name='features-lc'),
# path('features/<int:pk>/', views.FeaturesRUDView.as_view(),
# name='features-rud'),
# path('site-features/', views.SiteFeaturesLCView.as_view(),
# name='site-features-lc'),
# path('site-features/<int:pk>/', views.SiteFeaturesRUDView.as_view(),
# name='site-features-rud'),
] ]

View File

@ -2,12 +2,14 @@
from rest_framework import generics, permissions from rest_framework import generics, permissions
from rest_framework.response import Response from rest_framework.response import Response
from main import methods, models, serializers from main import methods, models, serializers
from utils.serializers import EmptySerializer
class DetermineSiteView(generics.GenericAPIView): class DetermineSiteView(generics.GenericAPIView):
"""Determine user's site.""" """Determine user's site."""
permission_classes = (permissions.AllowAny,) permission_classes = (permissions.AllowAny,)
serializer_class = EmptySerializer
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
user_ip = methods.get_user_ip(request) user_ip = methods.get_user_ip(request)
@ -60,3 +62,17 @@ class SiteSettingsView(generics.RetrieveAPIView):
# class SiteFeaturesRUDView(SiteFeaturesViewMixin, # class SiteFeaturesRUDView(SiteFeaturesViewMixin,
# generics.RetrieveUpdateDestroyAPIView): # generics.RetrieveUpdateDestroyAPIView):
# """Site features RUD.""" # """Site features RUD."""
class AwardView(generics.ListAPIView):
"""Awards list view."""
serializer_class = serializers.AwardSerializer
queryset = models.Award.objects.all()
permission_classes = (permissions.AllowAny, )
class AwardRetrieveView(generics.RetrieveAPIView):
"""Award retrieve view."""
serializer_class = serializers.AwardSerializer
queryset = models.Award.objects.all()
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)

View File

@ -1,6 +1,7 @@
from django.contrib.postgres.fields import JSONField from django.contrib.postgres.fields import JSONField
from django.db import models from django.db import models
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.utils.translation import get_language
from utils.models import (ProjectBaseMixin, BaseAttributes, from utils.models import (ProjectBaseMixin, BaseAttributes,
LocaleManagerMixin, ImageMixin) LocaleManagerMixin, ImageMixin)
@ -80,3 +81,8 @@ class News(BaseAttributes):
def __str__(self): def __str__(self):
return f'news: {self.id}' return f'news: {self.id}'
@property
def test_trans(self):
language = get_language()
return self.title.get(language)

View File

@ -1,5 +1,6 @@
from rest_framework import serializers from rest_framework import serializers
from gallery import models as gallery_models
from location.models import Address from location.models import Address
from location.serializers import AddressSerializer from location.serializers import AddressSerializer
from news import models from news import models
@ -26,7 +27,7 @@ class NewsLocalizationMixinSerializer(serializers.ModelSerializer):
class NewsSerializer(NewsLocalizationMixinSerializer): class NewsSerializer(NewsLocalizationMixinSerializer):
"""News serializer.""" """News serializer."""
address = AddressSerializer() address = AddressSerializer()
image = serializers.URLField(source='image.get_image_url') image_url = serializers.SerializerMethodField()
class Meta: class Meta:
model = models.News model = models.News
@ -38,20 +39,25 @@ class NewsSerializer(NewsLocalizationMixinSerializer):
'playlist', 'playlist',
'address', 'address',
'is_highlighted', 'is_highlighted',
'image', 'image_url',
# Localized fields # Localized fields
'title_trans', 'title_trans',
'subtitle_trans', 'subtitle_trans',
'description_trans', 'description_trans',
] ]
def get_image_url(self, obj):
"""Return absolute image URL"""
return obj.image.get_full_image_url(request=self.context.get('request'))
class NewsCreateUpdateSerializer(NewsSerializer): class NewsCreateUpdateSerializer(NewsSerializer):
"""News update serializer.""" """News update serializer."""
title = serializers.JSONField() title = serializers.JSONField()
subtitle = serializers.JSONField() subtitle = serializers.JSONField()
description = serializers.JSONField() description = serializers.JSONField()
image = serializers.ImageField(required=True) image = serializers.PrimaryKeyRelatedField(
queryset=gallery_models.Image.objects.all(), required=True,)
news_type = serializers.PrimaryKeyRelatedField( news_type = serializers.PrimaryKeyRelatedField(
queryset=models.NewsType.objects.all(), write_only=True) queryset=models.NewsType.objects.all(), write_only=True)
address = serializers.PrimaryKeyRelatedField( address = serializers.PrimaryKeyRelatedField(

View File

@ -6,10 +6,7 @@ from news.views import common
app_name = 'news' app_name = 'news'
urlpatterns = [ urlpatterns = [
path('', common.NewsList.as_view(), name='news_list'), path('', common.NewsListView.as_view(), name='list'),
path('create/', common.NewsCreate.as_view(), name='news_create'), path('<int:pk>/', common.NewsDetailView.as_view(), name='rud'),
path('<int:pk>/', common.NewsDetail.as_view(), name='news_detail'), path('type/', common.NewsTypeListView.as_view(), name='type'),
path('<int:pk>/update/', common.NewsUpdate.as_view(), name='news_update'),
path('<int:pk>/delete/', common.NewsDelete.as_view(), name='news_delete'),
path('type/', common.NewsTypeList.as_view(), name='news_type'),
] ]

View File

@ -16,7 +16,7 @@ class NewsViewMixin(JWTGenericViewMixin):
return News.objects.annotate_localized_fields(locale=self.request.locale) return News.objects.annotate_localized_fields(locale=self.request.locale)
class NewsList(NewsViewMixin, JWTListAPIView): class NewsListView(NewsViewMixin, JWTListAPIView):
"""News list view.""" """News list view."""
permission_classes = (permissions.AllowAny, ) permission_classes = (permissions.AllowAny, )
serializer_class = serializers.NewsSerializer serializer_class = serializers.NewsSerializer
@ -30,32 +30,20 @@ class NewsList(NewsViewMixin, JWTListAPIView):
.order_by('-is_highlighted', '-created') .order_by('-is_highlighted', '-created')
class NewsCreate(generics.CreateAPIView): # class NewsCreateView(generics.CreateAPIView):
"""News list view.""" # """News list view."""
queryset = News.objects.all() # queryset = News.objects.all()
permission_classes = (permissions.IsAuthenticated, ) # permission_classes = (permissions.IsAuthenticated, )
serializer_class = serializers.NewsCreateUpdateSerializer # serializer_class = serializers.NewsCreateUpdateSerializer
class NewsDetail(NewsViewMixin, generics.RetrieveAPIView): class NewsDetailView(NewsViewMixin, generics.RetrieveAPIView):
"""News detail view.""" """News detail view."""
permission_classes = (permissions.AllowAny, ) permission_classes = (permissions.IsAuthenticatedOrReadOnly, )
serializer_class = serializers.NewsSerializer serializer_class = serializers.NewsSerializer
class NewsDelete(generics.DestroyAPIView): class NewsTypeListView(generics.ListAPIView):
"""News delete view."""
queryset = News.objects.all()
permission_classes = (permissions.IsAuthenticated, )
class NewsUpdate(NewsViewMixin, generics.UpdateAPIView):
"""News update view."""
permission_classes = (permissions.IsAuthenticated, )
serializer_class = serializers.NewsCreateUpdateSerializer
class NewsTypeList(generics.ListAPIView):
"""NewsType list view.""" """NewsType list view."""
serializer_class = serializers.NewsTypeSerializer serializer_class = serializers.NewsTypeSerializer
permission_classes = (permissions.AllowAny, ) permission_classes = (permissions.AllowAny, )

View File

@ -6,6 +6,5 @@ from partner.views import common as views
app_name = 'partner' app_name = 'partner'
urlpatterns = [ urlpatterns = [
path('list/', views.PartnerListView.as_view(), path('', views.PartnerListView.as_view(), name='list')
name='partner_list')
] ]

View File

3
apps/products/admin.py Normal file
View File

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

8
apps/products/apps.py Normal file
View File

@ -0,0 +1,8 @@
from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class ProductsConfig(AppConfig):
"""Products model."""
name = 'products'
verbose_name = _('products')

View File

63
apps/products/models.py Normal file
View File

@ -0,0 +1,63 @@
# from django.contrib.postgres.fields import JSONField
# from django.db import models
# from django.utils.translation import gettext_lazy as _
#
# from utils.models import BaseAttributes
#
#
# class ProductManager(models.Manager):
# """Product manager."""
#
#
# class ProductQuerySet(models.QuerySet):
# """Product queryset."""
#
#
# class Product(BaseAttributes):
# """Product models."""
# name = models.CharField(_('name'), max_length=255)
# country = models.ForeignKey('location.Country', on_delete=models.CASCADE)
# region = models.ForeignKey('location.Region', on_delete=models.CASCADE)
# # ASK: What is the "subregion"
#
# description = JSONField(_('description'))
# characteristics = JSONField(_('characteristics'))
# metadata_values = JSONField(_('metadata_values'))
# # common_relations_id
# # product_region_id
# code = models.CharField(_('code'), max_length=255)
# available = models.BooleanField(_('available'))
#
# # dealer_type
# # target_scope
# # target_type
# # rank
# # excluding_tax_unit_price
# # column_21
# # currencies_id
# # vintage
# # producer_price
# # producer_description
# # annual_produced_quantity
# # production_method_description
# # unit_name
# # unit
# # unit_values
# # organic_source
# # certificates
# # establishments_id
# # restrictions
# #
# objects = ProductManager.from_queryset(ProductQuerySet)()
#
# class Meta:
# verbose_name = _('product')
# verbose_name_plural = _('products')
#
#
# class ProductType(models.Model):
# """ProductType model."""
#
# class Meta:
# verbose_name_plural = _('product types')
# verbose_name = _('product type')

View File

View File

@ -0,0 +1 @@
from rest_framework import serializers

View File

View File

3
apps/products/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

View File

View File

View File

View File

View File

View File

View File

View File

@ -0,0 +1 @@
# Create your views here.

View File

View File

@ -5,12 +5,9 @@ from translation import views
app_name = 'translation' app_name = 'translation'
urlpatterns = [ urlpatterns = [
path('languages/', views.LanguageListCreateView.as_view(), path('languages/', views.LanguageListView.as_view(), name='languages'),
name='languages'), path('languages/<int:pk>/', views.LanguageRetrieveView.as_view(), name='language'),
path('languages/<int:pk>/', views.LanguageRetrieveUpdateDestroyView.as_view(), path('site/', views.SiteInterfaceDictionaryView.as_view(), name='site-interface'),
name='language'), path('site/<int:pk>/', views.SiteInterfaceDictionaryRetrieveView.as_view(),
path('site/', views.SiteInterfaceDictionaryView.as_view(),
name='site-interface'),
path('site/<int:pk>/', views.SiteInterfaceDictionaryRUDView.as_view(),
name='site-interface-rud'), name='site-interface-rud'),
] ]

View File

@ -14,15 +14,14 @@ class LanguageViewMixin(generics.GenericAPIView):
# Views # Views
class LanguageListCreateView(LanguageViewMixin, generics.ListCreateAPIView): class LanguageListView(LanguageViewMixin, generics.ListAPIView):
"""List view for model Language""" """List view for model Language"""
pagination_class = None pagination_class = None
permission_classes = (permissions.IsAuthenticatedOrReadOnly, ) permission_classes = (permissions.IsAuthenticatedOrReadOnly, )
class LanguageRetrieveUpdateDestroyView(LanguageViewMixin, class LanguageRetrieveView(LanguageViewMixin, generics.RetrieveAPIView):
generics.RetrieveUpdateDestroyAPIView):
"""Retrieve, Update, Destroy view for model Language""" """Retrieve, Update, Destroy view for model Language"""
@ -37,7 +36,7 @@ class SiteInterfaceDictionaryMixin:
class SiteInterfaceDictionaryView(JWTGenericViewMixin, class SiteInterfaceDictionaryView(JWTGenericViewMixin,
SiteInterfaceDictionaryMixin, SiteInterfaceDictionaryMixin,
generics.ListCreateAPIView): generics.ListAPIView):
"""Site interface dictionary LC view.""" """Site interface dictionary LC view."""
pagination_class = None pagination_class = None
@ -46,9 +45,9 @@ class SiteInterfaceDictionaryView(JWTGenericViewMixin,
filter_fields = ['page', 'keywords'] filter_fields = ['page', 'keywords']
class SiteInterfaceDictionaryRUDView(JWTGenericViewMixin, class SiteInterfaceDictionaryRetrieveView(JWTGenericViewMixin,
SiteInterfaceDictionaryMixin, SiteInterfaceDictionaryMixin,
generics.RetrieveUpdateDestroyAPIView): generics.RetrieveAPIView):
"""Site interface dictionary RUD view.""" """Site interface dictionary RUD view."""
permission_classes = (permissions.AllowAny,) permission_classes = (permissions.AllowAny,)

View File

@ -24,7 +24,7 @@ class ServiceError(ProjectBaseException):
class UserNotFoundError(ProjectBaseException): class UserNotFoundError(ProjectBaseException):
"""The exception should be thrown when the user cannot get""" """The exception should be thrown when the user cannot get"""
status_code = status.HTTP_400_BAD_REQUEST status_code = status.HTTP_401_UNAUTHORIZED
default_detail = _('User not found') default_detail = _('User not found')

View File

@ -0,0 +1,6 @@
"""Utils app serializer."""
from rest_framework import serializers
class EmptySerializer(serializers.Serializer):
"""Empty Serializer"""

View File

@ -12,6 +12,8 @@ from rest_framework_simplejwt import tokens
class JWTGenericViewMixin(generics.GenericAPIView): class JWTGenericViewMixin(generics.GenericAPIView):
"""JWT view mixin""" """JWT view mixin"""
JWT_SETTINGS = settings.SIMPLE_JWT
ACCESS_TOKEN_HTTP_ONLY = False ACCESS_TOKEN_HTTP_ONLY = False
ACCESS_TOKEN_SECURE = False ACCESS_TOKEN_SECURE = False
@ -38,18 +40,26 @@ class JWTGenericViewMixin(generics.GenericAPIView):
""" """
COOKIES = list() COOKIES = list()
# Set max_age for tokens
if permanent:
access_token_max_age = self.JWT_SETTINGS.get('ACCESS_TOKEN_LIFETIME_SECONDS')
refresh_token_max_age = self.JWT_SETTINGS.get('REFRESH_TOKEN_LIFETIME_SECONDS')
else:
access_token_max_age = settings.COOKIES_MAX_AGE
refresh_token_max_age = settings.COOKIES_MAX_AGE
# Write to cookie access and refresh token with secure flag # Write to cookie access and refresh token with secure flag
if access_token and refresh_token: if access_token and refresh_token:
_access_token = self.COOKIE(key='access_token', _access_token = self.COOKIE(key='access_token',
value=access_token, value=access_token,
http_only=self.ACCESS_TOKEN_HTTP_ONLY, http_only=self.ACCESS_TOKEN_HTTP_ONLY,
secure=self.ACCESS_TOKEN_SECURE, secure=self.ACCESS_TOKEN_SECURE,
max_age=None if permanent else settings.COOKIES_MAX_AGE) max_age=access_token_max_age)
_refresh_token = self.COOKIE(key='refresh_token', _refresh_token = self.COOKIE(key='refresh_token',
value=refresh_token, value=refresh_token,
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=None if permanent else settings.COOKIES_MAX_AGE) max_age=refresh_token_max_age)
COOKIES.extend((_access_token, _refresh_token)) COOKIES.extend((_access_token, _refresh_token))
return COOKIES return COOKIES

View File

@ -334,8 +334,10 @@ GEOIP_PATH = os.path.join(PROJECT_ROOT, 'geoip_db')
# JWT # JWT
SIMPLE_JWT = { SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5), 'ACCESS_TOKEN_LIFETIME': timedelta(hours=6),
'REFRESH_TOKEN_LIFETIME': timedelta(days=1), 'ACCESS_TOKEN_LIFETIME_SECONDS': 21600, # 6 hours in seconds
'REFRESH_TOKEN_LIFETIME': timedelta(days=30),
'REFRESH_TOKEN_LIFETIME_SECONDS': 2592000, # 30 days in seconds
'ROTATE_REFRESH_TOKENS': True, 'ROTATE_REFRESH_TOKENS': True,
'BLACKLIST_AFTER_ROTATION': True, 'BLACKLIST_AFTER_ROTATION': True,

View File

@ -20,7 +20,7 @@ from django.urls import path, include, re_path
from drf_yasg import openapi from drf_yasg import openapi
from drf_yasg.views import get_schema_view from drf_yasg.views import get_schema_view
from rest_framework import permissions from rest_framework import permissions
from account.views.common import FCMDeviceViewSet
# Docs urls # Docs urls
schema_view = get_schema_view( schema_view = get_schema_view(
@ -48,22 +48,13 @@ urlpatterns_doc = [
name='schema-redoc'), name='schema-redoc'),
] ]
# API urls
urlpatterns_auth = [
path('api/auth/', include('authorization.urls.common')),
]
api_urlpatterns = [ api_urlpatterns = [
path('gallery/', include(('gallery.urls', 'gallery'), path('auth/', include('authorization.urls.common')),
namespace='gallery')), path('device/', FCMDeviceViewSet.as_view(), name='fcm-device-create'),
path('location/', include(('location.urls', 'location'), path('web/', include(('project.urls.web', 'web'), namespace='web')),
namespace='location')), path('back/', include(('project.urls.back', 'back'), namespace='back')),
path('main/', include(('main.urls', 'main'), path('mobile/', include(('project.urls.mobile', 'mobile'), namespace='mobile')),
namespace='main')),
path('translation/', include(('translation.urls', 'translation'),
namespace='translation')),
path('web/', include(('project.urls.web', 'web'),
namespace='web')),
] ]
urlpatterns = [ urlpatterns = [
@ -72,9 +63,7 @@ urlpatterns = [
] ]
urlpatterns = urlpatterns + \ urlpatterns = urlpatterns + \
urlpatterns_auth + \
static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
if settings.DEBUG: if settings.DEBUG:
urlpatterns.extend(urlpatterns_doc) urlpatterns.extend(urlpatterns_doc)

14
project/urls/back.py Normal file
View File

@ -0,0 +1,14 @@
from django.urls import path, include
app_name = 'back'
urlpatterns = [
path('gallery/', include(('gallery.urls', 'gallery'),
namespace='gallery')),
# path('account/', include('account.urls.web')),
# path('advertisement/', include('advertisement.urls.web')),
# path('collection/', include('collection.urls.web')),
# path('establishments/', include('establishment.urls.web')),
# path('news/', include('news.urls.web')),
# path('partner/', include('partner.urls.web')),
]

12
project/urls/mobile.py Normal file
View File

@ -0,0 +1,12 @@
from django.urls import path, include
app_name = 'mobile'
urlpatterns = [
# path('account/', include('account.urls.web')),
# path('advertisement/', include('advertisement.urls.web')),
# path('collection/', include('collection.urls.web')),
# path('establishments/', include('establishment.urls.web')),
# path('news/', include('news.urls.web')),
# path('partner/', include('partner.urls.web')),
]

View File

@ -18,10 +18,13 @@ from django.urls import path, include
app_name = 'web' app_name = 'web'
urlpatterns = [ urlpatterns = [
path('', include('establishment.urls.web')),
path('account/', include('account.urls.web')), path('account/', include('account.urls.web')),
path('advertisement/', include('advertisement.urls.web')), path('advertisement/', include('advertisement.urls.web')),
path('collection/', include('collection.urls.web')), path('collection/', include('collection.urls.web')),
path('establishments/', include('establishment.urls.web')),
path('news/', include('news.urls.web')), path('news/', include('news.urls.web')),
path('partner/', include('partner.urls.web')), path('partner/', include('partner.urls.web')),
path('location/', include('location.urls')),
path('main/', include('main.urls')),
path('translation/', include('translation.urls')),
] ]