From c2e111f75b0e72e6ed398009c8c452f69720446a Mon Sep 17 00:00:00 2001 From: littlewolf Date: Wed, 27 Nov 2019 18:12:22 +0300 Subject: [PATCH 01/14] Fix queryset --- apps/partner/views/common.py | 2 +- docker-compose.mysql.yml | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/partner/views/common.py b/apps/partner/views/common.py index a5d0338c..3c45fb6b 100644 --- a/apps/partner/views/common.py +++ b/apps/partner/views/common.py @@ -8,7 +8,7 @@ from partner.serializers import common as serializers # Mixins class PartnerViewMixin(generics.GenericAPIView): """View mixin for Partner views""" - queryset = models.Partner.objects.all() + queryset = models.Partner.objects.distinct("name") # Views diff --git a/docker-compose.mysql.yml b/docker-compose.mysql.yml index bd81ecb2..106cabb3 100644 --- a/docker-compose.mysql.yml +++ b/docker-compose.mysql.yml @@ -13,6 +13,9 @@ services: MYSQL_ROOT_PASSWORD: rootPassword volumes: - gm-mysql_db:/var/lib/mysql + - .:/code + + # PostgreSQL database From a81f3f079ae873da6795c75a7fd49b91e3be6635 Mon Sep 17 00:00:00 2001 From: dormantman Date: Wed, 4 Dec 2019 13:37:49 +0300 Subject: [PATCH 02/14] Changed url id to slug in back-office --- apps/establishment/serializers/back.py | 6 +++- apps/establishment/serializers/common.py | 4 ++- apps/establishment/tests.py | 36 ++++++++++++------------ apps/establishment/urls/back.py | 20 ++++++------- apps/establishment/views/back.py | 25 ++++++++++------ apps/timetable/serialziers.py | 8 +++++- apps/utils/serializers.py | 4 +++ apps/utils/views.py | 6 +++- 8 files changed, 69 insertions(+), 40 deletions(-) diff --git a/apps/establishment/serializers/back.py b/apps/establishment/serializers/back.py index dd16e861..a1b6cc4a 100644 --- a/apps/establishment/serializers/back.py +++ b/apps/establishment/serializers/back.py @@ -232,9 +232,13 @@ class EstablishmentBackOfficeGallerySerializer(serializers.ModelSerializer): def validate(self, attrs): """Override validate method.""" establishment_pk = self.get_request_kwargs().get('pk') + establishment_slug = self.get_request_kwargs().get('slug') + + search_kwargs = {'pk': establishment_pk} if establishment_pk else {'slug': establishment_slug} + image_id = self.get_request_kwargs().get('image_id') - establishment_qs = models.Establishment.objects.filter(pk=establishment_pk) + establishment_qs = models.Establishment.objects.filter(**search_kwargs) image_qs = Image.objects.filter(id=image_id) if not establishment_qs.exists(): diff --git a/apps/establishment/serializers/common.py b/apps/establishment/serializers/common.py index 7364d02c..e8a68cc0 100644 --- a/apps/establishment/serializers/common.py +++ b/apps/establishment/serializers/common.py @@ -486,7 +486,9 @@ class EstablishmentCarouselCreateSerializer(CarouselCreateSerializer): """Serializer to carousel object w/ model News.""" def validate(self, attrs): - establishment = models.Establishment.objects.filter(pk=self.pk).first() + search_kwargs = {'pk': self.pk} if self.pk else {'slug': self.slug} + + establishment = models.Establishment.objects.filter(**search_kwargs).first() if not establishment: raise serializers.ValidationError({'detail': _('Object not found.')}) diff --git a/apps/establishment/tests.py b/apps/establishment/tests.py index bd96b052..6bc23ccc 100644 --- a/apps/establishment/tests.py +++ b/apps/establishment/tests.py @@ -104,18 +104,18 @@ class EstablishmentBTests(BaseTestCase): response = self.client.post('/api/back/establishments/', data=data, format='json') self.assertEqual(response.status_code, status.HTTP_201_CREATED) - response = self.client.get(f'/api/back/establishments/{self.establishment.id}/', format='json') + response = self.client.get(f'/api/back/establishments/slug/{self.establishment.slug}/', format='json') self.assertEqual(response.status_code, status.HTTP_200_OK) update_data = { 'name': 'Test new establishment' } - response = self.client.patch(f'/api/back/establishments/{self.establishment.id}/', + response = self.client.patch(f'/api/back/establishments/slug/{self.establishment.slug}/', data=update_data) self.assertEqual(response.status_code, status.HTTP_200_OK) - response = self.client.delete(f'/api/back/establishments/{self.establishment.id}/', + response = self.client.delete(f'/api/back/establishments/slug/{self.establishment.slug}/', format='json') self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) @@ -372,22 +372,22 @@ class EstablishmentShedulerTests(ChildTestCase): 'weekday': 1 } - response = self.client.post(f'/api/back/establishments/{self.establishment.id}/schedule/', data=data) + response = self.client.post(f'/api/back/establishments/slug/{self.establishment.slug}/schedule/', data=data) self.assertEqual(response.status_code, status.HTTP_201_CREATED) schedule = response.data - response = self.client.get(f'/api/back/establishments/{self.establishment.id}/schedule/{schedule["id"]}/') + response = self.client.get(f'/api/back/establishments/slug/{self.establishment.slug}/schedule/{schedule["id"]}/') self.assertEqual(response.status_code, status.HTTP_200_OK) update_data = { 'weekday': 2 } - response = self.client.patch(f'/api/back/establishments/{self.establishment.id}/schedule/{schedule["id"]}/', + response = self.client.patch(f'/api/back/establishments/slug/{self.establishment.slug}/schedule/{schedule["id"]}/', data=update_data) self.assertEqual(response.status_code, status.HTTP_200_OK) - response = self.client.delete(f'/api/back/establishments/{self.establishment.id}/schedule/{schedule["id"]}/') + response = self.client.delete(f'/api/back/establishments/slug/{self.establishment.slug}/schedule/{schedule["id"]}/') self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) @@ -410,21 +410,21 @@ class EstablishmentWebTagTests(BaseTestCase): class EstablishmentWebSlugTests(ChildTestCase): def test_slug_Read(self): - response = self.client.get(f'/api/web/establishments/slug/{self.establishment.slug}/', format='json') + response = self.client.get(f'/api/web/establishments/{self.establishment.id}/', format='json') self.assertEqual(response.status_code, status.HTTP_200_OK) class EstablishmentWebSimilarTests(ChildTestCase): def test_similar_Read(self): - response = self.client.get(f'/api/web/establishments/slug/{self.establishment.slug}/similar/', format='json') + response = self.client.get(f'/api/web/establishments/{self.establishment.id}/similar/', format='json') self.assertEqual(response.status_code, status.HTTP_200_OK) class EstablishmentWebCommentsTests(ChildTestCase): def test_comments_CRUD(self): - response = self.client.get(f'/api/web/establishments/slug/{self.establishment.slug}/comments/', format='json') + response = self.client.get(f'/api/web/establishments/{self.establishment.id}/comments/', format='json') self.assertEqual(response.status_code, status.HTTP_200_OK) data = { @@ -433,13 +433,13 @@ class EstablishmentWebCommentsTests(ChildTestCase): 'mark': 4 } - response = self.client.post(f'/api/web/establishments/slug/{self.establishment.slug}/comments/create/', + response = self.client.post(f'/api/web/establishments/{self.establishment.id}/comments/create/', data=data) comment = response.json() self.assertEqual(response.status_code, status.HTTP_201_CREATED) - response = self.client.get(f'/api/web/establishments/slug/{self.establishment.slug}/comments/{comment["id"]}/', + response = self.client.get(f'/api/web/establishments/{self.establishment.id}/comments/{comment["id"]}/', format='json') self.assertEqual(response.status_code, status.HTTP_200_OK) @@ -448,12 +448,12 @@ class EstablishmentWebCommentsTests(ChildTestCase): } response = self.client.patch( - f'/api/web/establishments/slug/{self.establishment.slug}/comments/{comment["id"]}/', + f'/api/web/establishments/{self.establishment.id}/comments/{comment["id"]}/', data=update_data) self.assertEqual(response.status_code, status.HTTP_200_OK) response = self.client.delete( - f'/api/web/establishments/slug/{self.establishment.slug}/comments/{comment["id"]}/', + f'/api/web/establishments/{self.establishment.id}/comments/{comment["id"]}/', format='json') self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) @@ -466,13 +466,13 @@ class EstablishmentWebFavoriteTests(ChildTestCase): "object_id": self.establishment.id } - response = self.client.post(f'/api/web/establishments/slug/{self.establishment.slug}/favorites/', + response = self.client.post(f'/api/web/establishments/{self.establishment.id}/favorites/', data=data) self.assertEqual(response.status_code, status.HTTP_201_CREATED) response = self.client.delete( - f'/api/web/establishments/slug/{self.establishment.slug}/favorites/', + f'/api/web/establishments/{self.establishment.id}/favorites/', format='json') self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) @@ -484,8 +484,8 @@ class EstablishmentCarouselTests(ChildTestCase): "object_id": self.establishment.id } - response = self.client.post(f'/api/back/establishments/{self.establishment.id}/carousels/', data=data) + response = self.client.post(f'/api/back/establishments/slug/{self.establishment.slug}/carousels/', data=data) self.assertEqual(response.status_code, status.HTTP_201_CREATED) - response = self.client.delete(f'/api/back/establishments/{self.establishment.id}/carousels/') + response = self.client.delete(f'/api/back/establishments/slug/{self.establishment.slug}/carousels/') self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) diff --git a/apps/establishment/urls/back.py b/apps/establishment/urls/back.py index d9b2fbd7..ce1c7b27 100644 --- a/apps/establishment/urls/back.py +++ b/apps/establishment/urls/back.py @@ -8,25 +8,25 @@ app_name = 'establishment' urlpatterns = [ path('', views.EstablishmentListCreateView.as_view(), name='list'), - path('/', views.EstablishmentRUDView.as_view(), name='detail'), - path('/carousels/', views.EstablishmentCarouselCreateDestroyView.as_view(), + path('slug//', views.EstablishmentRUDView.as_view(), name='detail'), + path('slug//carousels/', views.EstablishmentCarouselCreateDestroyView.as_view(), name='create-destroy-carousels'), - path('/schedule//', views.EstablishmentScheduleRUDView.as_view(), + path('slug//schedule//', views.EstablishmentScheduleRUDView.as_view(), name='schedule-rud'), - path('/schedule/', views.EstablishmentScheduleCreateView.as_view(), + path('slug//schedule/', views.EstablishmentScheduleCreateView.as_view(), name='schedule-create'), - path('/gallery/', views.EstablishmentGalleryListView.as_view(), + path('slug//gallery/', views.EstablishmentGalleryListView.as_view(), name='gallery-list'), - path('/gallery//', + path('slug//gallery//', views.EstablishmentGalleryCreateDestroyView.as_view(), name='gallery-create-destroy'), - path('/companies/', views.EstablishmentCompanyListCreateView.as_view(), + path('slug//companies/', views.EstablishmentCompanyListCreateView.as_view(), name='company-list-create'), - path('/companies//', views.EstablishmentCompanyRUDView.as_view(), + path('slug//companies//', views.EstablishmentCompanyRUDView.as_view(), name='company-rud'), - path('/notes/', views.EstablishmentNoteListCreateView.as_view(), + path('slug//notes/', views.EstablishmentNoteListCreateView.as_view(), name='note-list-create'), - path('/notes//', views.EstablishmentNoteRUDView.as_view(), + path('slug//notes//', views.EstablishmentNoteRUDView.as_view(), name='note-rud'), path('menus/', views.MenuListCreateView.as_view(), name='menu-list'), path('menus//', views.MenuRUDView.as_view(), name='menu-rud'), diff --git a/apps/establishment/views/back.py b/apps/establishment/views/back.py index d3afbf2e..b92929c1 100644 --- a/apps/establishment/views/back.py +++ b/apps/establishment/views/back.py @@ -31,6 +31,7 @@ class EstablishmentListCreateView(EstablishmentMixinViews, generics.ListCreateAP class EstablishmentRUDView(generics.RetrieveUpdateDestroyAPIView): + lookup_field = 'slug' queryset = models.Establishment.objects.all() serializer_class = serializers.EstablishmentRUDSerializer permission_classes = [IsCountryAdmin | IsEstablishmentManager] @@ -38,6 +39,7 @@ class EstablishmentRUDView(generics.RetrieveUpdateDestroyAPIView): class EstablishmentScheduleRUDView(generics.RetrieveUpdateDestroyAPIView): """Establishment schedule RUD view""" + lookup_field = 'slug' serializer_class = ScheduleRUDSerializer permission_classes = [IsEstablishmentManager] @@ -45,11 +47,11 @@ class EstablishmentScheduleRUDView(generics.RetrieveUpdateDestroyAPIView): """ Returns the object the view is displaying. """ - establishment_pk = self.kwargs['pk'] + establishment_slug = self.kwargs['slug'] schedule_id = self.kwargs['schedule_id'] establishment = get_object_or_404(klass=models.Establishment.objects.all(), - pk=establishment_pk) + slug=establishment_slug) schedule = get_object_or_404(klass=establishment.schedule, id=schedule_id) @@ -62,6 +64,7 @@ class EstablishmentScheduleRUDView(generics.RetrieveUpdateDestroyAPIView): class EstablishmentScheduleCreateView(generics.CreateAPIView): """Establishment schedule Create view""" + lookup_field = 'slug' serializer_class = ScheduleCreateSerializer queryset = Timetable.objects.all() permission_classes = [IsEstablishmentManager] @@ -210,6 +213,7 @@ class EstablishmentSubtypeRUDView(generics.RetrieveUpdateDestroyAPIView): class EstablishmentGalleryCreateDestroyView(EstablishmentMixinViews, CreateDestroyGalleryViewMixin): """Resource for a create|destroy gallery for establishment for back-office users.""" + lookup_field = 'slug' serializer_class = serializers.EstablishmentBackOfficeGallerySerializer def get_object(self): @@ -218,7 +222,7 @@ class EstablishmentGalleryCreateDestroyView(EstablishmentMixinViews, """ establishment_qs = self.filter_queryset(self.get_queryset()) - establishment = get_object_or_404(establishment_qs, pk=self.kwargs.get('pk')) + establishment = get_object_or_404(establishment_qs, slug=self.kwargs.get('slug')) gallery = get_object_or_404(establishment.establishment_gallery, image_id=self.kwargs.get('image_id')) @@ -231,12 +235,13 @@ class EstablishmentGalleryCreateDestroyView(EstablishmentMixinViews, class EstablishmentGalleryListView(EstablishmentMixinViews, generics.ListAPIView): """Resource for returning gallery for establishment for back-office users.""" + lookup_field = 'slug' serializer_class = serializers.ImageBaseSerializer def get_object(self): """Override get_object method.""" qs = super(EstablishmentGalleryListView, self).get_queryset() - establishment = get_object_or_404(qs, pk=self.kwargs.get('pk')) + establishment = get_object_or_404(qs, slug=self.kwargs.get('slug')) # May raise a permission denied self.check_object_permissions(self.request, establishment) @@ -252,6 +257,7 @@ class EstablishmentCompanyListCreateView(EstablishmentMixinViews, generics.ListCreateAPIView): """List|Create establishment company view.""" + lookup_field = 'slug' serializer_class = serializers.EstablishmentCompanyListCreateSerializer def get_object(self): @@ -259,7 +265,7 @@ class EstablishmentCompanyListCreateView(EstablishmentMixinViews, establishment_qs = models.Establishment.objects.all() filtered_ad_qs = self.filter_queryset(establishment_qs) - establishment = get_object_or_404(filtered_ad_qs, pk=self.kwargs.get('pk')) + establishment = get_object_or_404(filtered_ad_qs, slug=self.kwargs.get('slug')) # May raise a permission denied self.check_object_permissions(self.request, establishment) @@ -275,6 +281,7 @@ class EstablishmentCompanyRUDView(EstablishmentMixinViews, generics.RetrieveUpdateDestroyAPIView): """Create|Retrieve|Update|Destroy establishment company view.""" + lookup_field = 'slug' serializer_class = serializers.CompanyBaseSerializer def get_object(self): @@ -282,7 +289,7 @@ class EstablishmentCompanyRUDView(EstablishmentMixinViews, establishment_qs = models.Establishment.objects.all() filtered_ad_qs = self.filter_queryset(establishment_qs) - establishment = get_object_or_404(filtered_ad_qs, pk=self.kwargs.get('pk')) + establishment = get_object_or_404(filtered_ad_qs, slug=self.kwargs.get('slug')) company = get_object_or_404(establishment.companies.all(), pk=self.kwargs.get('company_pk')) # May raise a permission denied @@ -295,6 +302,7 @@ class EstablishmentNoteListCreateView(EstablishmentMixinViews, generics.ListCreateAPIView): """Retrieve|Update|Destroy establishment note view.""" + lookup_field = 'slug' serializer_class = serializers.EstablishmentNoteListCreateSerializer def get_object(self): @@ -302,7 +310,7 @@ class EstablishmentNoteListCreateView(EstablishmentMixinViews, establishment_qs = models.Establishment.objects.all() filtered_establishment_qs = self.filter_queryset(establishment_qs) - establishment = get_object_or_404(filtered_establishment_qs, pk=self.kwargs.get('pk')) + establishment = get_object_or_404(filtered_establishment_qs, slug=self.kwargs.get('slug')) # May raise a permission denied self.check_object_permissions(self.request, establishment) @@ -318,6 +326,7 @@ class EstablishmentNoteRUDView(EstablishmentMixinViews, generics.RetrieveUpdateDestroyAPIView): """Create|Retrieve|Update|Destroy establishment note view.""" + lookup_field = 'slug' serializer_class = serializers.EstablishmentNoteBaseSerializer def get_object(self): @@ -325,7 +334,7 @@ class EstablishmentNoteRUDView(EstablishmentMixinViews, establishment_qs = models.Establishment.objects.all() filtered_establishment_qs = self.filter_queryset(establishment_qs) - establishment = get_object_or_404(filtered_establishment_qs, pk=self.kwargs.get('pk')) + establishment = get_object_or_404(filtered_establishment_qs, slug=self.kwargs.get('slug')) note = get_object_or_404(establishment.notes.all(), pk=self.kwargs['note_pk']) # May raise a permission denied diff --git a/apps/timetable/serialziers.py b/apps/timetable/serialziers.py index 48c8374d..305be0ec 100644 --- a/apps/timetable/serialziers.py +++ b/apps/timetable/serialziers.py @@ -45,8 +45,14 @@ class ScheduleRUDSerializer(serializers.ModelSerializer): .parser_context.get('view')\ .kwargs.get('pk') + establishment_slug = self.context.get('request')\ + .parser_context.get('view')\ + .kwargs.get('slug') + + search_kwargs = {'pk': establishment_pk} if establishment_pk else {'slug': establishment_slug} + # Check if establishment exists. - establishment_qs = Establishment.objects.filter(pk=establishment_pk) + establishment_qs = Establishment.objects.filter(**search_kwargs) if not establishment_qs.exists(): raise serializers.ValidationError({'detail': _('Establishment not found.')}) attrs['establishment'] = establishment_qs.first() diff --git a/apps/utils/serializers.py b/apps/utils/serializers.py index f55b69bc..15a58f7b 100644 --- a/apps/utils/serializers.py +++ b/apps/utils/serializers.py @@ -118,6 +118,10 @@ class CarouselCreateSerializer(serializers.ModelSerializer): def pk(self): return self.request.parser_context.get('kwargs').get('pk') + @property + def slug(self): + return self.request.parser_context.get('kwargs').get('slug') + class RecursiveFieldSerializer(serializers.Serializer): def to_representation(self, value): diff --git a/apps/utils/views.py b/apps/utils/views.py index 478a3cb2..db10dbfd 100644 --- a/apps/utils/views.py +++ b/apps/utils/views.py @@ -158,7 +158,11 @@ class CarouselCreateDestroyMixinView(BaseCreateDestroyMixinView): lookup_field = 'id' def get_base_object(self): - return get_object_or_404(self._model, id=self.kwargs['pk']) + search_kwargs = { + 'id': self.kwargs.get('pk'), + 'slug': self.kwargs.get('slug'), + } + return get_object_or_404(self._model, **search_kwargs) def get_object(self): """ From 4202eb679b5a938d0a454cfcffa11b429cd05578 Mon Sep 17 00:00:00 2001 From: dormantman Date: Wed, 4 Dec 2019 15:48:54 +0300 Subject: [PATCH 03/14] Corrections for tests --- apps/establishment/tests.py | 18 +++++++++--------- apps/establishment/views/web.py | 1 + apps/tag/serializers.py | 1 + apps/utils/views.py | 11 +++++++---- 4 files changed, 18 insertions(+), 13 deletions(-) diff --git a/apps/establishment/tests.py b/apps/establishment/tests.py index 6bc23ccc..f46cccc3 100644 --- a/apps/establishment/tests.py +++ b/apps/establishment/tests.py @@ -410,21 +410,21 @@ class EstablishmentWebTagTests(BaseTestCase): class EstablishmentWebSlugTests(ChildTestCase): def test_slug_Read(self): - response = self.client.get(f'/api/web/establishments/{self.establishment.id}/', format='json') + response = self.client.get(f'/api/web/establishments/slug/{self.establishment.slug}/', format='json') self.assertEqual(response.status_code, status.HTTP_200_OK) class EstablishmentWebSimilarTests(ChildTestCase): def test_similar_Read(self): - response = self.client.get(f'/api/web/establishments/{self.establishment.id}/similar/', format='json') + response = self.client.get(f'/api/web/establishments/slug/{self.establishment.slug}/similar/', format='json') self.assertEqual(response.status_code, status.HTTP_200_OK) class EstablishmentWebCommentsTests(ChildTestCase): def test_comments_CRUD(self): - response = self.client.get(f'/api/web/establishments/{self.establishment.id}/comments/', format='json') + response = self.client.get(f'/api/web/establishments/slug/{self.establishment.slug}/comments/', format='json') self.assertEqual(response.status_code, status.HTTP_200_OK) data = { @@ -433,13 +433,13 @@ class EstablishmentWebCommentsTests(ChildTestCase): 'mark': 4 } - response = self.client.post(f'/api/web/establishments/{self.establishment.id}/comments/create/', + response = self.client.post(f'/api/web/establishments/slug/{self.establishment.slug}/comments/create/', data=data) comment = response.json() self.assertEqual(response.status_code, status.HTTP_201_CREATED) - response = self.client.get(f'/api/web/establishments/{self.establishment.id}/comments/{comment["id"]}/', + response = self.client.get(f'/api/web/establishments/slug/{self.establishment.slug}/comments/{comment["id"]}/', format='json') self.assertEqual(response.status_code, status.HTTP_200_OK) @@ -448,12 +448,12 @@ class EstablishmentWebCommentsTests(ChildTestCase): } response = self.client.patch( - f'/api/web/establishments/{self.establishment.id}/comments/{comment["id"]}/', + f'/api/web/establishments/slug/{self.establishment.slug}/comments/{comment["id"]}/', data=update_data) self.assertEqual(response.status_code, status.HTTP_200_OK) response = self.client.delete( - f'/api/web/establishments/{self.establishment.id}/comments/{comment["id"]}/', + f'/api/web/establishments/slug/{self.establishment.slug}/comments/{comment["id"]}/', format='json') self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) @@ -466,13 +466,13 @@ class EstablishmentWebFavoriteTests(ChildTestCase): "object_id": self.establishment.id } - response = self.client.post(f'/api/web/establishments/{self.establishment.id}/favorites/', + response = self.client.post(f'/api/web/establishments/slug/{self.establishment.slug}/favorites/', data=data) self.assertEqual(response.status_code, status.HTTP_201_CREATED) response = self.client.delete( - f'/api/web/establishments/{self.establishment.id}/favorites/', + f'/api/web/establishments/slug/{self.establishment.slug}/favorites/', format='json') self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) diff --git a/apps/establishment/views/web.py b/apps/establishment/views/web.py index bd826e4e..65a56a5b 100644 --- a/apps/establishment/views/web.py +++ b/apps/establishment/views/web.py @@ -142,6 +142,7 @@ class EstablishmentFavoritesCreateDestroyView(FavoritesCreateDestroyMixinView): class EstablishmentCarouselCreateDestroyView(CarouselCreateDestroyMixinView): """View for create/destroy establishment from carousel.""" + lookup_field = 'slug' _model = models.Establishment serializer_class = serializers.EstablishmentCarouselCreateSerializer diff --git a/apps/tag/serializers.py b/apps/tag/serializers.py index 2cc818a9..27590b18 100644 --- a/apps/tag/serializers.py +++ b/apps/tag/serializers.py @@ -37,6 +37,7 @@ class TagBackOfficeSerializer(TagBaseSerializer): 'category' ) + class TagCategoryProductSerializer(serializers.ModelSerializer): """SHORT Serializer for TagCategory""" diff --git a/apps/utils/views.py b/apps/utils/views.py index db10dbfd..e158357e 100644 --- a/apps/utils/views.py +++ b/apps/utils/views.py @@ -158,10 +158,10 @@ class CarouselCreateDestroyMixinView(BaseCreateDestroyMixinView): lookup_field = 'id' def get_base_object(self): - search_kwargs = { - 'id': self.kwargs.get('pk'), - 'slug': self.kwargs.get('slug'), - } + 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): @@ -175,6 +175,9 @@ class CarouselCreateDestroyMixinView(BaseCreateDestroyMixinView): # self.check_object_permissions(self.request, carousels) return carousels + def perform_destroy(self, instance): + instance.delete() + self.es_update_base_object() # BackOffice user`s views & viewsets class BindObjectMixin: From 72d52e5c19747094183e4c115e3f34d40c967dc9 Mon Sep 17 00:00:00 2001 From: dormantman Date: Wed, 4 Dec 2019 15:51:03 +0300 Subject: [PATCH 04/14] Removed unusable destroy function --- apps/utils/views.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/apps/utils/views.py b/apps/utils/views.py index e158357e..9ac8ca60 100644 --- a/apps/utils/views.py +++ b/apps/utils/views.py @@ -175,9 +175,6 @@ class CarouselCreateDestroyMixinView(BaseCreateDestroyMixinView): # self.check_object_permissions(self.request, carousels) return carousels - def perform_destroy(self, instance): - instance.delete() - self.es_update_base_object() # BackOffice user`s views & viewsets class BindObjectMixin: From 48ae6a437d8d63bf5fa911ba38bc87fe118fe218 Mon Sep 17 00:00:00 2001 From: alex Date: Fri, 6 Dec 2019 10:29:43 +0300 Subject: [PATCH 05/14] street_name_2 clear --- apps/location/management/commands/fix_address.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/location/management/commands/fix_address.py b/apps/location/management/commands/fix_address.py index 5fcdf1b9..5ecacba6 100644 --- a/apps/location/management/commands/fix_address.py +++ b/apps/location/management/commands/fix_address.py @@ -22,8 +22,9 @@ class Command(BaseCommand): new_address = Address.objects.filter(old_id=idx).first() if new_address: new_address.number = 0 + new_address.street_name_2 = '' new_address.street_name_1 = address update_address.append(new_address) - Address.objects.bulk_update(update_address, ['number', 'street_name_1']) + Address.objects.bulk_update(update_address, ['number', 'street_name_1', 'street_name_2']) self.stdout.write(self.style.WARNING(f'Updated addresses: {len(update_address)}')) From 7635eafb0906291896a3b426b99b2f0c56ac039d Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Fri, 6 Dec 2019 13:11:29 +0300 Subject: [PATCH 06/14] booking urls for mobile (cherry picked from commit 0df3425) --- apps/booking/urls/__init__.py | 0 apps/booking/{urls.py => urls/common.py} | 0 apps/booking/urls/mobile.py | 8 ++++++++ apps/booking/urls/web.py | 8 ++++++++ project/urls/mobile.py | 1 + project/urls/web.py | 2 +- 6 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 apps/booking/urls/__init__.py rename apps/booking/{urls.py => urls/common.py} (100%) create mode 100644 apps/booking/urls/mobile.py create mode 100644 apps/booking/urls/web.py diff --git a/apps/booking/urls/__init__.py b/apps/booking/urls/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/booking/urls.py b/apps/booking/urls/common.py similarity index 100% rename from apps/booking/urls.py rename to apps/booking/urls/common.py diff --git a/apps/booking/urls/mobile.py b/apps/booking/urls/mobile.py new file mode 100644 index 00000000..57e58c8f --- /dev/null +++ b/apps/booking/urls/mobile.py @@ -0,0 +1,8 @@ +from booking.urls import common as common_views +app = 'booking' + + +urlpatterns_api = [] + +urlpatterns = urlpatterns_api + \ + common_views.urlpatterns diff --git a/apps/booking/urls/web.py b/apps/booking/urls/web.py new file mode 100644 index 00000000..57e58c8f --- /dev/null +++ b/apps/booking/urls/web.py @@ -0,0 +1,8 @@ +from booking.urls import common as common_views +app = 'booking' + + +urlpatterns_api = [] + +urlpatterns = urlpatterns_api + \ + common_views.urlpatterns diff --git a/project/urls/mobile.py b/project/urls/mobile.py index 4fa53ad9..4368189e 100644 --- a/project/urls/mobile.py +++ b/project/urls/mobile.py @@ -3,6 +3,7 @@ from django.urls import path, include app_name = 'mobile' urlpatterns = [ + path('booking/', include('booking.urls.web')), path('establishments/', include('establishment.urls.mobile')), path('location/', include('location.urls.mobile')), path('main/', include('main.urls.mobile')), diff --git a/project/urls/web.py b/project/urls/web.py index c5a609e2..99c12937 100644 --- a/project/urls/web.py +++ b/project/urls/web.py @@ -19,7 +19,7 @@ app_name = 'web' urlpatterns = [ path('account/', include('account.urls.web')), - path('booking/', include('booking.urls')), + path('booking/', include('booking.urls.web')), path('re_blocks/', include('advertisement.urls.web')), path('collections/', include('collection.urls.web')), path('establishments/', include('establishment.urls.web')), From c272dc1255e1e51b90aa9973a0196aaa9a8a8686 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Fri, 6 Dec 2019 13:15:27 +0300 Subject: [PATCH 07/14] increase JWT token lifetime --- project/settings/base.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/project/settings/base.py b/project/settings/base.py index 6648f0af..1d2c5744 100644 --- a/project/settings/base.py +++ b/project/settings/base.py @@ -414,10 +414,10 @@ SORL_THUMBNAIL_ALIASES = { SIMPLE_JWT = { # Increase access token lifetime b.c. front-end dev's cant send multiple # requests to API in one HTTP request. - 'ACCESS_TOKEN_LIFETIME': timedelta(days=30), - 'ACCESS_TOKEN_LIFETIME_SECONDS': 21600, # 6 hours in seconds - 'REFRESH_TOKEN_LIFETIME': timedelta(days=30), - 'REFRESH_TOKEN_LIFETIME_SECONDS': 2592000, # 30 days in seconds + 'ACCESS_TOKEN_LIFETIME': timedelta(days=182), + 'ACCESS_TOKEN_LIFETIME_SECONDS': 15770000, # 6 months + 'REFRESH_TOKEN_LIFETIME': timedelta(days=182), + 'REFRESH_TOKEN_LIFETIME_SECONDS': 15770000, # 6 months 'ROTATE_REFRESH_TOKENS': True, 'BLACKLIST_AFTER_ROTATION': True, @@ -453,7 +453,7 @@ NOTIFICATION_PASSWORD_TEMPLATE = 'account/password_change_email.html' # COOKIES -COOKIES_MAX_AGE = 2628000 # 30 days +COOKIES_MAX_AGE = 15730000 # 6 months SESSION_COOKIE_SAMESITE = None From 91b528feb9cf846aa7ad13f29d0686be1fa57829 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Fri, 6 Dec 2019 14:03:06 +0300 Subject: [PATCH 08/14] tags for establishments --- apps/establishment/models.py | 8 +++++++- apps/establishment/serializers/common.py | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/apps/establishment/models.py b/apps/establishment/models.py index 3cdef691..fb401ddd 100644 --- a/apps/establishment/models.py +++ b/apps/establishment/models.py @@ -457,9 +457,15 @@ class Establishment(GalleryModelMixin, ProjectBaseMixin, URLImageMixin, def visible_tags(self): return super().visible_tags \ .exclude(category__index_name__in=['guide', 'collection', 'purchased_item', - 'business_tag', 'business_tags_de', 'tag']) + 'business_tag', 'business_tags_de']) \ + .exclude(value__in=['rss', 'rss_selection']) # todo: recalculate toque_number + @property + def visible_tags_detail(self): + """Removes some tags from detail Establishment representation""" + return self.visible_tags.exclude(category__index_name__in=['tag']) + def recalculate_toque_number(self): toque_number = 0 if self.address and self.public_mark: diff --git a/apps/establishment/serializers/common.py b/apps/establishment/serializers/common.py index 23fc24df..4025eb96 100644 --- a/apps/establishment/serializers/common.py +++ b/apps/establishment/serializers/common.py @@ -369,7 +369,7 @@ class EstablishmentDetailSerializer(EstablishmentBaseSerializer): employees = EstablishmentEmployeeSerializer(source='actual_establishment_employees', many=True) address = AddressDetailSerializer(read_only=True) - + tags = TagBaseSerializer(read_only=True, many=True, source='visible_tags_detail') menu = MenuSerializers(source='menu_set', many=True, read_only=True) best_price_menu = serializers.DecimalField(max_digits=14, decimal_places=2, read_only=True) best_price_carte = serializers.DecimalField(max_digits=14, decimal_places=2, read_only=True) From e08296c52d1eef433da9494204e5de87b1ed5c0c Mon Sep 17 00:00:00 2001 From: alex Date: Fri, 6 Dec 2019 14:12:26 +0300 Subject: [PATCH 09/14] address to product detail web api --- apps/establishment/serializers/common.py | 26 +++++++++++++++++++++++- apps/product/serializers/common.py | 12 +++++------ 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/apps/establishment/serializers/common.py b/apps/establishment/serializers/common.py index 23fc24df..0b2cd6be 100644 --- a/apps/establishment/serializers/common.py +++ b/apps/establishment/serializers/common.py @@ -17,7 +17,7 @@ from utils.serializers import ImageBaseSerializer, CarouselCreateSerializer from utils.serializers import (ProjectModelSerializer, TranslatedField, FavoritesCreateSerializer) from location.serializers import EstablishmentWineRegionBaseSerializer, \ - EstablishmentWineOriginBaseSerializer + EstablishmentWineOriginBaseSerializer class ContactPhonesSerializer(serializers.ModelSerializer): @@ -239,6 +239,30 @@ class EstablishmentShortSerializer(serializers.ModelSerializer): ] +class _EstablishmentAddressShortSerializer(serializers.ModelSerializer): + """Short serializer for establishment.""" + city = CitySerializer(source='address.city', allow_null=True) + establishment_type = EstablishmentTypeGeoSerializer() + establishment_subtypes = EstablishmentSubTypeBaseSerializer(many=True) + currency = CurrencySerializer(read_only=True) + address = AddressBaseSerializer(read_only=True) + + class Meta: + """Meta class.""" + model = models.Establishment + fields = [ + 'id', + 'name', + 'index_name', + 'slug', + 'city', + 'establishment_type', + 'establishment_subtypes', + 'currency', + 'address', + ] + + class EstablishmentProductShortSerializer(serializers.ModelSerializer): """SHORT Serializer for displaying info about an establishment on product page.""" establishment_type = EstablishmentTypeGeoSerializer() diff --git a/apps/product/serializers/common.py b/apps/product/serializers/common.py index 983cc2ab..d794ca93 100644 --- a/apps/product/serializers/common.py +++ b/apps/product/serializers/common.py @@ -4,15 +4,15 @@ from rest_framework import serializers from comment.models import Comment from comment.serializers import CommentSerializer -from establishment.serializers import EstablishmentShortSerializer, EstablishmentProductSerializer, EstablishmentProductShortSerializer -from gallery.models import Image +from establishment.serializers import EstablishmentProductShortSerializer +from establishment.serializers.common import _EstablishmentAddressShortSerializer +from location.serializers import WineOriginRegionBaseSerializer, WineOriginBaseSerializer +from main.serializers import AwardSerializer from product import models from review.serializers import ReviewShortSerializer +from tag.serializers import TagBaseSerializer, TagCategoryProductSerializer from utils import exceptions as utils_exceptions from utils.serializers import TranslatedField, FavoritesCreateSerializer, ImageBaseSerializer -from main.serializers import AwardSerializer -from location.serializers import WineOriginRegionBaseSerializer, WineOriginBaseSerializer -from tag.serializers import TagBaseSerializer, TagCategoryProductSerializer class ProductTagSerializer(TagBaseSerializer): @@ -119,7 +119,7 @@ class ProductBaseSerializer(serializers.ModelSerializer): class ProductDetailSerializer(ProductBaseSerializer): """Product detail serializer.""" description_translated = TranslatedField() - establishment_detail = EstablishmentShortSerializer(source='establishment', read_only=True) + establishment_detail = _EstablishmentAddressShortSerializer(source='establishment', read_only=True) review = ReviewShortSerializer(source='last_published_review', read_only=True) awards = AwardSerializer(many=True, read_only=True) classifications = ProductClassificationBaseSerializer(many=True, read_only=True) From 1372538acf7e722a97ca4064dea3eaf00bb51654 Mon Sep 17 00:00:00 2001 From: dormantman Date: Thu, 5 Dec 2019 18:10:24 +0300 Subject: [PATCH 10/14] Merge branch 'fix/dublicate-products' of /home/dormantman/PycharmProjects/gm-backend with conflicts. --- .gitignore | 6 ++---- apps/product/views/common.py | 2 ++ project/settings/local.py | 36 +++++++++++++----------------------- 3 files changed, 17 insertions(+), 27 deletions(-) diff --git a/.gitignore b/.gitignore index 90e4f23f..ea6151b0 100644 --- a/.gitignore +++ b/.gitignore @@ -21,10 +21,8 @@ logs/ /geoip_db/ # dev -./docker-compose.override.yml +docker-compose.override.yml + celerybeat-schedule local_files celerybeat.pid -/gm_viktor.dump -/docker-compose.dump.yml -/gm_production_20191029.sql diff --git a/apps/product/views/common.py b/apps/product/views/common.py index f984a87b..764a0c97 100644 --- a/apps/product/views/common.py +++ b/apps/product/views/common.py @@ -26,6 +26,8 @@ class ProductListView(ProductBaseView, generics.ListAPIView): filter_class = filters.ProductFilterSet def get_queryset(self): + print(super().get_queryset()) + qs = super().get_queryset().with_extended_related() \ .by_country_code(self.request.country_code) return qs diff --git a/project/settings/local.py b/project/settings/local.py index c56f9042..fe521d92 100644 --- a/project/settings/local.py +++ b/project/settings/local.py @@ -30,31 +30,18 @@ MEDIA_ROOT = os.path.join(PUBLIC_ROOT, MEDIA_LOCATION) THUMBNAIL_DEBUG = True # ADDED TRANSFER APP -INSTALLED_APPS.append('transfer.apps.TransferConfig') +# INSTALLED_APPS.append('transfer.apps.TransferConfig') # DATABASES -DATABASES = { - 'default': { - 'ENGINE': 'django.contrib.gis.db.backends.postgis', - 'NAME': os.environ.get('DB_NAME'), - 'USER': os.environ.get('DB_USERNAME'), - 'PASSWORD': os.environ.get('DB_PASSWORD'), - 'HOST': os.environ.get('DB_HOSTNAME'), - 'PORT': os.environ.get('DB_PORT'), - 'OPTIONS': { - 'options': '-c search_path=gm' - }, - }, - 'legacy': { - 'ENGINE': 'django.db.backends.mysql', - # 'HOST': '172.22.0.1', - 'HOST': 'mysql_db', - 'PORT': 3306, - 'NAME': 'dev', - 'USER': 'dev', - 'PASSWORD': 'octosecret123' - }, -} +# DATABASES.update({ +# 'legacy': { +# 'ENGINE': 'django.db.backends.mysql', +# # 'HOST': '172.22.0.1', +# 'HOST': 'mysql_db', +# 'PORT': 3306, +# 'NAME': 'dev', +# 'USER': 'dev', +# 'PASSWORD': 'octosecret123'}}) # LOGGING @@ -113,3 +100,6 @@ TESTING = sys.argv[1:2] == ['test'] if TESTING: ELASTICSEARCH_INDEX_NAMES = {} ELASTICSEARCH_DSL_AUTOSYNC = False + +# INSTALLED APPS +INSTALLED_APPS.append('transfer.apps.TransferConfig') \ No newline at end of file From 0c5140d35944328b2916b6eabaa04ec8677b23a5 Mon Sep 17 00:00:00 2001 From: dormantman Date: Fri, 6 Dec 2019 15:03:18 +0300 Subject: [PATCH 11/14] Added current product exclude --- apps/product/filters.py | 6 ++++++ apps/product/models.py | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/apps/product/filters.py b/apps/product/filters.py index c7e87dda..1d226356 100644 --- a/apps/product/filters.py +++ b/apps/product/filters.py @@ -9,6 +9,7 @@ class ProductFilterSet(filters.FilterSet): """Product filter set.""" establishment_id = filters.NumberFilter() + current_product = filters.CharFilter(method='without_current_product') product_type = filters.CharFilter(method='by_product_type') product_subtype = filters.CharFilter(method='by_product_subtype') @@ -21,6 +22,11 @@ class ProductFilterSet(filters.FilterSet): 'product_subtype', ] + def without_current_product(self, queryset, name, value): + if value not in EMPTY_VALUES: + return queryset.without_current_product(value) + return queryset + def by_product_type(self, queryset, name, value): if value not in EMPTY_VALUES: return queryset.by_product_type(value) diff --git a/apps/product/models.py b/apps/product/models.py index a6129c48..6d3e4954 100644 --- a/apps/product/models.py +++ b/apps/product/models.py @@ -102,6 +102,11 @@ class ProductQuerySet(models.QuerySet): def wines(self): return self.filter(type__index_name__icontains=ProductType.WINE) + def without_current_product(self, current_product: str): + """Exclude by current product.""" + kwargs = {'pk': int(current_product)} if current_product.isdigit() else {'slug': current_product} + return self.exclude(**kwargs) + def by_product_type(self, product_type: str): """Filter by type.""" return self.filter(product_type__index_name__icontains=product_type) From 6a9356aaa6b9efc7f5a41ff32a58c2b37196bf76 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Fri, 6 Dec 2019 15:10:48 +0300 Subject: [PATCH 12/14] fix merge conflicts --- .gitignore | 6 ++++-- apps/product/views/common.py | 2 -- project/settings/local.py | 36 +++++++++++++++++++++++------------- 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/.gitignore b/.gitignore index ea6151b0..90e4f23f 100644 --- a/.gitignore +++ b/.gitignore @@ -21,8 +21,10 @@ logs/ /geoip_db/ # dev -docker-compose.override.yml - +./docker-compose.override.yml celerybeat-schedule local_files celerybeat.pid +/gm_viktor.dump +/docker-compose.dump.yml +/gm_production_20191029.sql diff --git a/apps/product/views/common.py b/apps/product/views/common.py index 764a0c97..f984a87b 100644 --- a/apps/product/views/common.py +++ b/apps/product/views/common.py @@ -26,8 +26,6 @@ class ProductListView(ProductBaseView, generics.ListAPIView): filter_class = filters.ProductFilterSet def get_queryset(self): - print(super().get_queryset()) - qs = super().get_queryset().with_extended_related() \ .by_country_code(self.request.country_code) return qs diff --git a/project/settings/local.py b/project/settings/local.py index fe521d92..c56f9042 100644 --- a/project/settings/local.py +++ b/project/settings/local.py @@ -30,18 +30,31 @@ MEDIA_ROOT = os.path.join(PUBLIC_ROOT, MEDIA_LOCATION) THUMBNAIL_DEBUG = True # ADDED TRANSFER APP -# INSTALLED_APPS.append('transfer.apps.TransferConfig') +INSTALLED_APPS.append('transfer.apps.TransferConfig') # DATABASES -# DATABASES.update({ -# 'legacy': { -# 'ENGINE': 'django.db.backends.mysql', -# # 'HOST': '172.22.0.1', -# 'HOST': 'mysql_db', -# 'PORT': 3306, -# 'NAME': 'dev', -# 'USER': 'dev', -# 'PASSWORD': 'octosecret123'}}) +DATABASES = { + 'default': { + 'ENGINE': 'django.contrib.gis.db.backends.postgis', + 'NAME': os.environ.get('DB_NAME'), + 'USER': os.environ.get('DB_USERNAME'), + 'PASSWORD': os.environ.get('DB_PASSWORD'), + 'HOST': os.environ.get('DB_HOSTNAME'), + 'PORT': os.environ.get('DB_PORT'), + 'OPTIONS': { + 'options': '-c search_path=gm' + }, + }, + 'legacy': { + 'ENGINE': 'django.db.backends.mysql', + # 'HOST': '172.22.0.1', + 'HOST': 'mysql_db', + 'PORT': 3306, + 'NAME': 'dev', + 'USER': 'dev', + 'PASSWORD': 'octosecret123' + }, +} # LOGGING @@ -100,6 +113,3 @@ TESTING = sys.argv[1:2] == ['test'] if TESTING: ELASTICSEARCH_INDEX_NAMES = {} ELASTICSEARCH_DSL_AUTOSYNC = False - -# INSTALLED APPS -INSTALLED_APPS.append('transfer.apps.TransferConfig') \ No newline at end of file From 351d4920121049e0b00c3563abd6d48091969da0 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Fri, 6 Dec 2019 15:45:46 +0300 Subject: [PATCH 13/14] command to remove relative news description images --- .../news/management/commands/rm_empty_images.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 apps/news/management/commands/rm_empty_images.py diff --git a/apps/news/management/commands/rm_empty_images.py b/apps/news/management/commands/rm_empty_images.py new file mode 100644 index 00000000..3a48e2e4 --- /dev/null +++ b/apps/news/management/commands/rm_empty_images.py @@ -0,0 +1,17 @@ +from django.core.management.base import BaseCommand + +from news.models import News +import re + +class Command(BaseCommand): + help = 'Removes empty img html tags from news description' + + relative_img_regex = re.compile(r'\', re.I) + + def handle(self, *args, **kwargs): + for news in News.objects.all(): + if isinstance(news.description, dict): + news.description = {locale: self.relative_img_regex.sub('', rich_text) + for locale, rich_text in news.description.items()} + self.stdout.write(self.style.WARNING(f'Replaced {news} empty img html tags...\n')) + news.save() \ No newline at end of file From bdb82d0fb010632fcfb64b9a056e3e163f4407cc Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Fri, 6 Dec 2019 16:40:39 +0300 Subject: [PATCH 14/14] add migration command --- make_data_migration.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/make_data_migration.sh b/make_data_migration.sh index 70e27ccb..e6cec3e1 100755 --- a/make_data_migration.sh +++ b/make_data_migration.sh @@ -12,4 +12,5 @@ ./manage.py transfer --wine_characteristics ./manage.py transfer --inquiries ./manage.py transfer --assemblage -./manage.py transfer --purchased_plaques \ No newline at end of file +./manage.py transfer --purchased_plaques +./manage.py rm_empty_images \ No newline at end of file