Merge branch 'develop' into feature/in_favorites
This commit is contained in:
commit
04d6412f39
|
|
@ -63,7 +63,7 @@ class SendConfirmationEmailView(generics.GenericAPIView):
|
|||
return Response(status=status.HTTP_200_OK)
|
||||
|
||||
|
||||
class ConfirmEmailView(JWTGenericViewMixin):
|
||||
class ConfirmEmailView(JWTGenericViewMixin, generics.GenericAPIView):
|
||||
"""View for confirm changing email"""
|
||||
|
||||
permission_classes = (permissions.AllowAny,)
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ class PasswordResetView(generics.GenericAPIView):
|
|||
return Response(status=status.HTTP_200_OK)
|
||||
|
||||
|
||||
class PasswordResetConfirmView(JWTGenericViewMixin):
|
||||
class PasswordResetConfirmView(JWTGenericViewMixin, generics.GenericAPIView):
|
||||
"""View for confirmation new password"""
|
||||
serializer_class = serializers.PasswordResetConfirmSerializer
|
||||
permission_classes = (permissions.AllowAny,)
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ class AdvertisementPageListCreateView(AdvertisementBackOfficeViewMixin,
|
|||
ad_qs = Advertisement.objects.all()
|
||||
filtered_ad_qs = self.filter_queryset(ad_qs)
|
||||
|
||||
ad = get_object_or_404(filtered_ad_qs, pk=self.kwargs['pk'])
|
||||
ad = get_object_or_404(filtered_ad_qs, pk=self.kwargs.get('pk'))
|
||||
|
||||
# May raise a permission denied
|
||||
self.check_object_permissions(self.request, ad)
|
||||
|
|
@ -68,8 +68,8 @@ class AdvertisementPageRUDView(AdvertisementBackOfficeViewMixin,
|
|||
ad_qs = Advertisement.objects.all()
|
||||
filtered_ad_qs = self.filter_queryset(ad_qs)
|
||||
|
||||
ad = get_object_or_404(filtered_ad_qs, pk=self.kwargs['ad_pk'])
|
||||
page = get_object_or_404(ad.pages.all(), pk=self.kwargs['page_pk'])
|
||||
ad = get_object_or_404(filtered_ad_qs, pk=self.kwargs.get('ad_pk'))
|
||||
page = get_object_or_404(ad.pages.all(), pk=self.kwargs.get('page_pk'))
|
||||
|
||||
# May raise a permission denied
|
||||
self.check_object_permissions(self.request, page)
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ class OAuth2ViewMixin(CsrfExemptMixin, OAuthLibMixin, BaseOAuth2ViewMixin):
|
|||
|
||||
|
||||
# Sign in via Facebook
|
||||
class OAuth2SignUpView(OAuth2ViewMixin, JWTGenericViewMixin):
|
||||
class OAuth2SignUpView(OAuth2ViewMixin, JWTGenericViewMixin, generics.GenericAPIView):
|
||||
"""
|
||||
Implements an endpoint to convert a provider token to an access token
|
||||
|
||||
|
|
@ -142,7 +142,7 @@ class SignUpView(generics.GenericAPIView):
|
|||
return Response(status=status.HTTP_201_CREATED)
|
||||
|
||||
|
||||
class ConfirmationEmailView(JWTGenericViewMixin):
|
||||
class ConfirmationEmailView(JWTGenericViewMixin, generics.GenericAPIView):
|
||||
"""View for confirmation email"""
|
||||
|
||||
permission_classes = (permissions.AllowAny, )
|
||||
|
|
@ -174,7 +174,7 @@ class ConfirmationEmailView(JWTGenericViewMixin):
|
|||
|
||||
|
||||
# Login by username|email + password
|
||||
class LoginByUsernameOrEmailView(JWTGenericViewMixin):
|
||||
class LoginByUsernameOrEmailView(JWTGenericViewMixin, generics.GenericAPIView):
|
||||
"""Login by email and password"""
|
||||
permission_classes = (permissions.AllowAny,)
|
||||
serializer_class = serializers.LoginByUsernameOrEmailSerializer
|
||||
|
|
@ -197,7 +197,7 @@ class LoginByUsernameOrEmailView(JWTGenericViewMixin):
|
|||
|
||||
|
||||
# Logout
|
||||
class LogoutView(JWTGenericViewMixin):
|
||||
class LogoutView(JWTGenericViewMixin, generics.GenericAPIView):
|
||||
"""Logout user"""
|
||||
permission_classes = (IsAuthenticatedAndTokenIsValid, )
|
||||
|
||||
|
|
@ -215,7 +215,7 @@ class LogoutView(JWTGenericViewMixin):
|
|||
|
||||
|
||||
# Refresh token
|
||||
class RefreshTokenView(JWTGenericViewMixin):
|
||||
class RefreshTokenView(JWTGenericViewMixin, generics.GenericAPIView):
|
||||
"""Refresh access_token"""
|
||||
permission_classes = (permissions.AllowAny, )
|
||||
serializer_class = serializers.RefreshTokenSerializer
|
||||
|
|
|
|||
|
|
@ -1,8 +1,14 @@
|
|||
from rest_framework import serializers
|
||||
|
||||
from collection import models
|
||||
from collection.serializers.common import CollectionBaseSerializer
|
||||
from establishment.models import Establishment
|
||||
from location.models import Country
|
||||
from location.serializers import CountrySimpleSerializer
|
||||
from collection.serializers.common import CollectionBaseSerializer
|
||||
from collection import models
|
||||
from product.models import Product
|
||||
from utils.exceptions import (
|
||||
BindingObjectNotFound, RemovedBindingObjectNotFound, ObjectAlreadyAdded
|
||||
)
|
||||
|
||||
|
||||
class CollectionBackOfficeSerializer(CollectionBaseSerializer):
|
||||
|
|
@ -31,3 +37,54 @@ class CollectionBackOfficeSerializer(CollectionBaseSerializer):
|
|||
'start',
|
||||
'end',
|
||||
]
|
||||
|
||||
|
||||
class CollectionBindObjectSerializer(serializers.Serializer):
|
||||
"""Serializer for binding collection and objects"""
|
||||
|
||||
ESTABLISHMENT = 'establishment'
|
||||
PRODUCT = 'product'
|
||||
|
||||
TYPE_CHOICES = (
|
||||
(ESTABLISHMENT, 'Establishment'),
|
||||
(PRODUCT, 'Product'),
|
||||
)
|
||||
|
||||
type = serializers.ChoiceField(TYPE_CHOICES)
|
||||
object_id = serializers.IntegerField()
|
||||
|
||||
def validate(self, attrs):
|
||||
view = self.context.get('view')
|
||||
request = self.context.get('request')
|
||||
|
||||
obj_type = attrs.get('type')
|
||||
obj_id = attrs.get('object_id')
|
||||
|
||||
collection = view.get_object()
|
||||
attrs['collection'] = collection
|
||||
|
||||
if obj_type == self.ESTABLISHMENT:
|
||||
establishment = Establishment.objects.filter(pk=obj_id).\
|
||||
first()
|
||||
if not establishment:
|
||||
raise BindingObjectNotFound()
|
||||
if request.method == 'POST' and collection.establishments.\
|
||||
filter(pk=establishment.pk).exists():
|
||||
raise ObjectAlreadyAdded()
|
||||
if request.method == 'DELETE' and not collection.\
|
||||
establishments.filter(pk=establishment.pk).\
|
||||
exists():
|
||||
raise RemovedBindingObjectNotFound()
|
||||
attrs['related_object'] = establishment
|
||||
elif obj_type == self.PRODUCT:
|
||||
product = Product.objects.filter(pk=obj_id).first()
|
||||
if not product:
|
||||
raise BindingObjectNotFound()
|
||||
if request.method == 'POST' and collection.products.\
|
||||
filter(pk=product.pk).exists():
|
||||
raise ObjectAlreadyAdded()
|
||||
if request.method == 'DELETE' and not collection.products.\
|
||||
filter(pk=product.pk).exists():
|
||||
raise RemovedBindingObjectNotFound()
|
||||
attrs['related_object'] = product
|
||||
return attrs
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
"""Collection common urlpaths."""
|
||||
from django.urls import path
|
||||
from rest_framework.routers import SimpleRouter
|
||||
|
||||
from collection.views import back as views
|
||||
|
||||
app_name = 'collection'
|
||||
router = SimpleRouter()
|
||||
router.register(r'', views.CollectionBackOfficeViewSet)
|
||||
|
||||
urlpatterns = [
|
||||
path('', views.CollectionListCreateView.as_view(), name='list-create'),
|
||||
path('<int:pk>/', views.CollectionRUDView.as_view(), name='rud-collection'),
|
||||
]
|
||||
urlpatterns = router.urls
|
||||
|
|
|
|||
|
|
@ -1,19 +1,49 @@
|
|||
from rest_framework import generics, permissions
|
||||
from rest_framework import permissions
|
||||
from rest_framework import viewsets, mixins
|
||||
|
||||
from collection import models
|
||||
from collection.serializers import back
|
||||
from collection.serializers import back as serializers
|
||||
from utils.views import BindObjectMixin
|
||||
|
||||
|
||||
class CollectionListCreateView(generics.ListCreateAPIView):
|
||||
"""Collection list-create view."""
|
||||
class CollectionViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
|
||||
"""ViewSet for Collection model."""
|
||||
|
||||
pagination_class = None
|
||||
permission_classes = (permissions.AllowAny,)
|
||||
queryset = models.Collection.objects.all()
|
||||
serializer_class = back.CollectionBackOfficeSerializer
|
||||
# todo: conf. permissions by TT
|
||||
permission_classes = (permissions.IsAuthenticated, )
|
||||
serializer_class = serializers.CollectionBackOfficeSerializer
|
||||
|
||||
|
||||
class CollectionRUDView(generics.RetrieveUpdateDestroyAPIView):
|
||||
"""Collection list-create view."""
|
||||
class CollectionBackOfficeViewSet(mixins.CreateModelMixin,
|
||||
mixins.UpdateModelMixin,
|
||||
mixins.DestroyModelMixin,
|
||||
mixins.RetrieveModelMixin,
|
||||
BindObjectMixin,
|
||||
CollectionViewSet):
|
||||
"""ViewSet for Collection model for BackOffice users."""
|
||||
|
||||
permission_classes = (permissions.IsAuthenticated,)
|
||||
queryset = models.Collection.objects.all()
|
||||
serializer_class = back.CollectionBackOfficeSerializer
|
||||
# todo: conf. permissions by TT
|
||||
permission_classes = (permissions.IsAuthenticated, )
|
||||
serializer_class = serializers.CollectionBackOfficeSerializer
|
||||
bind_object_serializer_class = serializers.CollectionBindObjectSerializer
|
||||
|
||||
def perform_binding(self, serializer):
|
||||
data = serializer.validated_data
|
||||
collection = data.pop('collection')
|
||||
obj_type = data.get('type')
|
||||
related_object = data.get('related_object')
|
||||
if obj_type == self.bind_object_serializer_class.ESTABLISHMENT:
|
||||
collection.establishments.add(related_object)
|
||||
elif obj_type == self.bind_object_serializer_class.PRODUCT:
|
||||
collection.products.add(related_object)
|
||||
|
||||
def perform_unbinding(self, serializer):
|
||||
data = serializer.validated_data
|
||||
collection = data.pop('collection')
|
||||
obj_type = data.get('type')
|
||||
related_object = data.get('related_object')
|
||||
if obj_type == self.bind_object_serializer_class.ESTABLISHMENT:
|
||||
collection.establishments.remove(related_object)
|
||||
elif obj_type == self.bind_object_serializer_class.PRODUCT:
|
||||
collection.products.remove(related_object)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ from comment.models import Comment
|
|||
from utils.admin import BaseModelAdminMixin
|
||||
from establishment import models
|
||||
from main.models import Award
|
||||
from product.models import Product
|
||||
from product.models import Product, PurchasedProduct
|
||||
from review import models as review_models
|
||||
|
||||
|
||||
|
|
@ -69,13 +69,19 @@ class EstablishmentNote(admin.TabularInline):
|
|||
extra = 0
|
||||
|
||||
|
||||
class PurchasedProduct(admin.TabularInline):
|
||||
model = PurchasedProduct
|
||||
extra = 0
|
||||
|
||||
|
||||
@admin.register(models.Establishment)
|
||||
class EstablishmentAdmin(BaseModelAdminMixin, admin.ModelAdmin):
|
||||
"""Establishment admin."""
|
||||
list_display = ['id', '__str__', 'image_tag', ]
|
||||
search_fields = ['id', 'name', 'index_name', 'slug']
|
||||
list_filter = ['public_mark', 'toque_number']
|
||||
inlines = [GalleryImageInline, CompanyInline, EstablishmentNote]
|
||||
inlines = [GalleryImageInline, CompanyInline, EstablishmentNote,
|
||||
PurchasedProduct]
|
||||
|
||||
# inlines = [
|
||||
# AwardInline, ContactPhoneInline, ContactEmailInline,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
# Generated by Django 2.2.7 on 2019-11-20 12:49
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('product', '0018_purchasedproduct'),
|
||||
('establishment', '0064_auto_20191119_1546'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='establishment',
|
||||
name='purchased_products',
|
||||
field=models.ManyToManyField(blank=True, help_text='Attribute from legacy db.\nMust be deleted after the implementation of the market.', related_name='establishments', through='product.PurchasedProduct', to='product.Product', verbose_name='purchased plaques'),
|
||||
),
|
||||
]
|
||||
|
|
@ -399,6 +399,13 @@ class Establishment(GalleryModelMixin, ProjectBaseMixin, URLImageMixin,
|
|||
currency = models.ForeignKey(Currency, blank=True, null=True, default=None,
|
||||
on_delete=models.PROTECT,
|
||||
verbose_name=_('currency'))
|
||||
purchased_products = models.ManyToManyField('product.Product', blank=True,
|
||||
through='product.PurchasedProduct',
|
||||
related_name='establishments',
|
||||
verbose_name=_('purchased plaques'),
|
||||
help_text=_('Attribute from legacy db.\n'
|
||||
'Must be deleted after the '
|
||||
'implementation of the market.'))
|
||||
|
||||
objects = EstablishmentQuerySet.as_manager()
|
||||
|
||||
|
|
@ -411,6 +418,12 @@ class Establishment(GalleryModelMixin, ProjectBaseMixin, URLImageMixin,
|
|||
def __str__(self):
|
||||
return f'id:{self.id}-{self.name}'
|
||||
|
||||
def clean_fields(self, exclude=None):
|
||||
super().clean_fields(exclude)
|
||||
if self.purchased_products.filter(product_type__index_name='souvenir').exists():
|
||||
raise ValidationError(
|
||||
_('Only souvenirs.'))
|
||||
|
||||
def delete(self, using=None, keep_parents=False):
|
||||
"""Overridden delete method"""
|
||||
# Delete all related companies
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@ from django.db.models import Q, F
|
|||
|
||||
from establishment.models import Establishment
|
||||
from location.models import Address
|
||||
from transfer.models import Establishments, Dishes, EstablishmentNotes
|
||||
from product.models import PurchasedProduct, Product
|
||||
from transfer.models import Establishments, Dishes, EstablishmentNotes, \
|
||||
EstablishmentMerchandises
|
||||
from transfer.serializers.establishment import EstablishmentSerializer, \
|
||||
EstablishmentNoteSerializer
|
||||
from transfer.serializers.plate import PlateSerializer
|
||||
|
|
@ -140,6 +142,43 @@ def transfer_establishment_note():
|
|||
pprint(f"transfer_establishment_note errors: {errors}")
|
||||
|
||||
|
||||
def transfer_purchased_plaques():
|
||||
update_products_counter = 0
|
||||
already_updated_counter = 0
|
||||
not_existed_establishment_counter = 0
|
||||
|
||||
purchased = EstablishmentMerchandises.objects.values_list(
|
||||
'establishment_id',
|
||||
'merchandise__vintage',
|
||||
'gifted',
|
||||
'quantity'
|
||||
)
|
||||
for old_est_id, vintage, gifted, quantity in purchased:
|
||||
establishment_qs = Establishment.objects.filter(old_id=old_est_id)
|
||||
product_qs = Product.objects.filter(name='Plaque restaurants',
|
||||
vintage=vintage)
|
||||
if establishment_qs.exists() and product_qs.exists():
|
||||
product = product_qs.first()
|
||||
establishment = establishment_qs.first()
|
||||
|
||||
purchases, created = PurchasedProduct.objects.get_or_create(
|
||||
establishment=establishment,
|
||||
product=product,
|
||||
is_gifted=gifted,
|
||||
quantity=quantity
|
||||
)
|
||||
if created:
|
||||
update_products_counter += 1
|
||||
else:
|
||||
already_updated_counter += 1
|
||||
else:
|
||||
not_existed_establishment_counter += 1
|
||||
|
||||
print(f'Updated products: {update_products_counter}\n'
|
||||
f'Already updated: {already_updated_counter}\n'
|
||||
f'Not existed establishment: {not_existed_establishment_counter}')
|
||||
|
||||
|
||||
data_types = {
|
||||
"establishment": [
|
||||
transfer_establishment,
|
||||
|
|
@ -149,4 +188,7 @@ data_types = {
|
|||
transfer_establishment_addresses
|
||||
],
|
||||
"menu": [transfer_menu],
|
||||
"purchased_plaques": [
|
||||
transfer_purchased_plaques
|
||||
],
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ from establishment import filters, models, serializers
|
|||
from timetable.serialziers import ScheduleRUDSerializer, ScheduleCreateSerializer
|
||||
from utils.permissions import IsCountryAdmin, IsEstablishmentManager
|
||||
from utils.views import CreateDestroyGalleryViewMixin
|
||||
from timetable.models import Timetable
|
||||
from rest_framework import status
|
||||
from rest_framework.response import Response
|
||||
|
||||
|
|
@ -36,13 +37,14 @@ class EstablishmentRUDView(generics.RetrieveUpdateDestroyAPIView):
|
|||
class EstablishmentScheduleRUDView(generics.RetrieveUpdateDestroyAPIView):
|
||||
"""Establishment schedule RUD view"""
|
||||
serializer_class = ScheduleRUDSerializer
|
||||
permission_classes = [IsEstablishmentManager]
|
||||
|
||||
def get_object(self):
|
||||
"""
|
||||
Returns the object the view is displaying.
|
||||
"""
|
||||
establishment_pk = self.kwargs['pk']
|
||||
schedule_id = self.kwargs['schedule_id']
|
||||
establishment_pk = self.kwargs.get('pk')
|
||||
schedule_id = self.kwargs.get('schedule_id')
|
||||
|
||||
establishment = get_object_or_404(klass=models.Establishment.objects.all(),
|
||||
pk=establishment_pk)
|
||||
|
|
@ -59,6 +61,8 @@ class EstablishmentScheduleRUDView(generics.RetrieveUpdateDestroyAPIView):
|
|||
class EstablishmentScheduleCreateView(generics.CreateAPIView):
|
||||
"""Establishment schedule Create view"""
|
||||
serializer_class = ScheduleCreateSerializer
|
||||
queryset = Timetable.objects.all()
|
||||
permission_classes = [IsEstablishmentManager]
|
||||
|
||||
|
||||
class MenuListCreateView(generics.ListCreateAPIView):
|
||||
|
|
@ -200,8 +204,9 @@ class EstablishmentGalleryCreateDestroyView(EstablishmentMixinViews,
|
|||
"""
|
||||
establishment_qs = self.filter_queryset(self.get_queryset())
|
||||
|
||||
establishment = get_object_or_404(establishment_qs, pk=self.kwargs['pk'])
|
||||
gallery = get_object_or_404(establishment.establishment_gallery, image_id=self.kwargs['image_id'])
|
||||
establishment = get_object_or_404(establishment_qs, pk=self.kwargs.get('pk'))
|
||||
gallery = get_object_or_404(establishment.establishment_gallery,
|
||||
image_id=self.kwargs.get('image_id'))
|
||||
|
||||
# May raise a permission denied
|
||||
self.check_object_permissions(self.request, gallery)
|
||||
|
|
@ -217,7 +222,7 @@ class EstablishmentGalleryListView(EstablishmentMixinViews,
|
|||
def get_object(self):
|
||||
"""Override get_object method."""
|
||||
qs = super(EstablishmentGalleryListView, self).get_queryset()
|
||||
establishment = get_object_or_404(qs, pk=self.kwargs['pk'])
|
||||
establishment = get_object_or_404(qs, pk=self.kwargs.get('pk'))
|
||||
|
||||
# May raise a permission denied
|
||||
self.check_object_permissions(self.request, establishment)
|
||||
|
|
@ -240,7 +245,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['pk'])
|
||||
establishment = get_object_or_404(filtered_ad_qs, pk=self.kwargs.get('pk'))
|
||||
|
||||
# May raise a permission denied
|
||||
self.check_object_permissions(self.request, establishment)
|
||||
|
|
@ -263,8 +268,8 @@ 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['pk'])
|
||||
company = get_object_or_404(establishment.companies.all(), pk=self.kwargs['company_pk'])
|
||||
establishment = get_object_or_404(filtered_ad_qs, pk=self.kwargs.get('pk'))
|
||||
company = get_object_or_404(establishment.companies.all(), pk=self.kwargs.get('company_pk'))
|
||||
|
||||
# May raise a permission denied
|
||||
self.check_object_permissions(self.request, company)
|
||||
|
|
@ -273,7 +278,7 @@ class EstablishmentCompanyRUDView(EstablishmentMixinViews,
|
|||
|
||||
|
||||
class EstablishmentNoteListCreateView(EstablishmentMixinViews,
|
||||
generics.ListCreateAPIView):
|
||||
generics.ListCreateAPIView):
|
||||
"""Retrieve|Update|Destroy establishment note view."""
|
||||
|
||||
serializer_class = serializers.EstablishmentNoteListCreateSerializer
|
||||
|
|
@ -283,7 +288,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['pk'])
|
||||
establishment = get_object_or_404(filtered_establishment_qs, pk=self.kwargs.get('pk'))
|
||||
|
||||
# May raise a permission denied
|
||||
self.check_object_permissions(self.request, establishment)
|
||||
|
|
@ -306,7 +311,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['pk'])
|
||||
establishment = get_object_or_404(filtered_establishment_qs, pk=self.kwargs.get('pk'))
|
||||
note = get_object_or_404(establishment.notes.all(), pk=self.kwargs['note_pk'])
|
||||
|
||||
# May raise a permission denied
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
from transfer.serializers import location as location_serializers
|
||||
from transfer import models as transfer_models
|
||||
from location.models import Country
|
||||
from location.models import Country, CityGallery, City
|
||||
from gallery.models import Image
|
||||
from pprint import pprint
|
||||
|
||||
from requests import get
|
||||
|
||||
|
||||
|
|
@ -179,6 +179,42 @@ def update_flags():
|
|||
query.save()
|
||||
|
||||
|
||||
def transfer_city_gallery():
|
||||
created_counter = 0
|
||||
cities_not_exists = {}
|
||||
gallery_obj_exists_counter = 0
|
||||
|
||||
city_gallery = transfer_models.CityPhotos.objects.exclude(city__isnull=True) \
|
||||
.exclude(city__country_code_2__isnull=True) \
|
||||
.exclude(city__country_code_2__iexact='') \
|
||||
.exclude(city__region_code__isnull=True) \
|
||||
.exclude(city__region_code__iexact='') \
|
||||
.values_list('city_id', 'attachment_suffix_url')
|
||||
for old_city_id, image_suffix_url in city_gallery:
|
||||
city = City.objects.filter(old_id=old_city_id)
|
||||
if city.exists():
|
||||
city = city.first()
|
||||
image, _ = Image.objects.get_or_create(image=image_suffix_url,
|
||||
defaults={
|
||||
'image': image_suffix_url,
|
||||
'orientation': Image.HORIZONTAL,
|
||||
'title': f'{city.name} - {image_suffix_url}',
|
||||
})
|
||||
city_gallery, created = CityGallery.objects.get_or_create(image=image,
|
||||
city=city,
|
||||
is_main=True)
|
||||
if created:
|
||||
created_counter += 1
|
||||
else:
|
||||
gallery_obj_exists_counter += 1
|
||||
else:
|
||||
cities_not_exists.update({'city_old_id': old_city_id})
|
||||
|
||||
print(f'Created: {created_counter}\n'
|
||||
f'City not exists: {cities_not_exists}\n'
|
||||
f'Already added: {gallery_obj_exists_counter}')
|
||||
|
||||
|
||||
data_types = {
|
||||
"dictionaries": [
|
||||
transfer_countries,
|
||||
|
|
@ -192,4 +228,5 @@ data_types = {
|
|||
"update_country_flag": [
|
||||
update_flags
|
||||
],
|
||||
"fill_city_gallery": [transfer_city_gallery]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,8 +51,8 @@ class CityGalleryCreateDestroyView(common.CityViewMixin,
|
|||
"""
|
||||
city_qs = self.filter_queryset(self.get_queryset())
|
||||
|
||||
city = get_object_or_404(city_qs, pk=self.kwargs['pk'])
|
||||
gallery = get_object_or_404(city.city_gallery, image_id=self.kwargs['image_id'])
|
||||
city = get_object_or_404(city_qs, pk=self.kwargs.get('pk'))
|
||||
gallery = get_object_or_404(city.city_gallery, image_id=self.kwargs.get('image_id'))
|
||||
|
||||
# May raise a permission denied
|
||||
self.check_object_permissions(self.request, gallery)
|
||||
|
|
|
|||
40
apps/main/filters.py
Normal file
40
apps/main/filters.py
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
from django.core.validators import EMPTY_VALUES
|
||||
from django_filters import rest_framework as filters
|
||||
|
||||
from main import models
|
||||
|
||||
|
||||
class AwardFilter(filters.FilterSet):
|
||||
"""Award filter set."""
|
||||
|
||||
establishment_id = filters.NumberFilter(field_name='object_id', )
|
||||
product_id = filters.NumberFilter(field_name='object_id', )
|
||||
employee_id = filters.NumberFilter(field_name='object_id', )
|
||||
|
||||
class Meta:
|
||||
"""Meta class."""
|
||||
|
||||
model = models.Award
|
||||
fields = (
|
||||
'establishment_id',
|
||||
'product_id',
|
||||
'employee_id',
|
||||
'state',
|
||||
'award_type',
|
||||
'vintage_year',
|
||||
)
|
||||
|
||||
def by_establishment_id(self, queryset, name, value):
|
||||
if value not in EMPTY_VALUES:
|
||||
return queryset.by_establishment_id(value, content_type='establishment')
|
||||
return queryset
|
||||
|
||||
def by_product_id(self, queryset, name, value):
|
||||
if value not in EMPTY_VALUES:
|
||||
return queryset.by_product_id(value, content_type='product')
|
||||
return queryset
|
||||
|
||||
def by_employee_id(self, queryset, name, value):
|
||||
if value not in EMPTY_VALUES:
|
||||
return queryset.by_employee_id(value, content_type='establishmentemployee')
|
||||
return queryset
|
||||
|
|
@ -153,7 +153,7 @@ class Award(TranslatedFieldsMixin, URLImageMixin, models.Model):
|
|||
PUBLISHED = 1
|
||||
|
||||
STATE_CHOICES = (
|
||||
(WAITING,'waiting'),
|
||||
(WAITING, 'waiting'),
|
||||
(PUBLISHED, 'published')
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ class SiteFeatureSerializer(serializers.ModelSerializer):
|
|||
'route',
|
||||
'source',
|
||||
'nested',
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class CurrencySerializer(ProjectModelSerializer):
|
||||
|
|
@ -145,6 +145,19 @@ class AwardSerializer(AwardBaseSerializer):
|
|||
fields = AwardBaseSerializer.Meta.fields + ['award_type', ]
|
||||
|
||||
|
||||
class BackAwardSerializer(AwardBaseSerializer):
|
||||
"""Award serializer."""
|
||||
|
||||
class Meta:
|
||||
model = models.Award
|
||||
fields = AwardBaseSerializer.Meta.fields + [
|
||||
'award_type',
|
||||
'state',
|
||||
'content_type',
|
||||
'object_id',
|
||||
]
|
||||
|
||||
|
||||
class CarouselListSerializer(serializers.ModelSerializer):
|
||||
"""Serializer for retrieving list of carousel items."""
|
||||
|
||||
|
|
@ -202,4 +215,4 @@ class PageTypeBaseSerializer(serializers.ModelSerializer):
|
|||
fields = [
|
||||
'id',
|
||||
'name',
|
||||
]
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
0
apps/main/tests/__init__.py
Normal file
0
apps/main/tests/__init__.py
Normal file
73
apps/main/tests/tests_back.py
Normal file
73
apps/main/tests/tests_back.py
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
from http.cookies import SimpleCookie
|
||||
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from rest_framework import status
|
||||
from rest_framework.test import APITestCase
|
||||
|
||||
from account.models import User
|
||||
from location.models import Country
|
||||
from main.models import Award, AwardType
|
||||
|
||||
|
||||
class AwardTestCase(APITestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.user = User.objects.create_user(
|
||||
username='alex',
|
||||
email='alex@mail.com',
|
||||
password='alex_password',
|
||||
is_staff=True,
|
||||
)
|
||||
|
||||
# get tokens
|
||||
tokens = User.create_jwt_tokens(self.user)
|
||||
self.client.cookies = SimpleCookie(
|
||||
{'access_token': tokens.get('access_token'),
|
||||
'refresh_token': tokens.get('refresh_token')})
|
||||
|
||||
self.country_ru = Country.objects.create(
|
||||
name={'en-GB': 'Russian'},
|
||||
code='RU',
|
||||
)
|
||||
|
||||
self.content_type = ContentType.objects.get(app_label="establishment", model="establishment")
|
||||
|
||||
self.award_type = AwardType.objects.create(
|
||||
country=self.country_ru,
|
||||
name="Test award type",
|
||||
)
|
||||
|
||||
self.award = Award.objects.create(
|
||||
award_type=self.award_type,
|
||||
vintage_year='2017',
|
||||
state=Award.PUBLISHED,
|
||||
object_id=1,
|
||||
content_type_id=1,
|
||||
)
|
||||
|
||||
def test_award_CRUD(self):
|
||||
response = self.client.get('/api/back/main/awards/', format='json')
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
data = {
|
||||
'award_type': self.award_type.pk,
|
||||
'state': 1,
|
||||
'object_id': 1,
|
||||
'content_type': 1,
|
||||
}
|
||||
|
||||
response = self.client.post('/api/back/main/awards/', data=data)
|
||||
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||
|
||||
response = self.client.get(f'/api/back/main/awards/{self.award.id}/', format='json')
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
update_data = {
|
||||
'vintage_year': '2019'
|
||||
}
|
||||
|
||||
response = self.client.patch(f'/api/back/main/awards/{self.award.id}/', data=update_data)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
response = self.client.delete(f'/api/back/main/awards/{self.award.id}/')
|
||||
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
|
||||
11
apps/main/urls/back.py
Normal file
11
apps/main/urls/back.py
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
"""Back main URLs"""
|
||||
from django.urls import path
|
||||
|
||||
from main.views import back as views
|
||||
|
||||
app_name = 'main'
|
||||
|
||||
urlpatterns = [
|
||||
path('awards/', views.AwardLstView.as_view(), name='awards-list-create'),
|
||||
path('awards/<int:id>/', views.AwardRUDView.as_view(), name='awards-rud'),
|
||||
]
|
||||
21
apps/main/views/back.py
Normal file
21
apps/main/views/back.py
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
from rest_framework import generics, permissions
|
||||
|
||||
from main import serializers
|
||||
from main.filters import AwardFilter
|
||||
from main.models import Award
|
||||
|
||||
|
||||
class AwardLstView(generics.ListCreateAPIView):
|
||||
"""Award list create view."""
|
||||
queryset = Award.objects.all()
|
||||
serializer_class = serializers.BackAwardSerializer
|
||||
permission_classes = (permissions.IsAdminUser,)
|
||||
filterset_class = AwardFilter
|
||||
|
||||
|
||||
class AwardRUDView(generics.RetrieveUpdateDestroyAPIView):
|
||||
"""Award RUD view."""
|
||||
queryset = Award.objects.all()
|
||||
serializer_class = serializers.BackAwardSerializer
|
||||
permission_classes = (permissions.IsAdminUser,)
|
||||
lookup_field = 'id'
|
||||
|
|
@ -70,7 +70,7 @@ class CarouselListView(generics.ListAPIView):
|
|||
|
||||
def get_queryset(self):
|
||||
country_code = self.request.country_code
|
||||
if hasattr(settings, 'CAROUSEL_ITEMS') and country_code in ['www', 'main']:
|
||||
if hasattr(settings, 'CAROUSEL_ITEMS') and country_code in settings.INTERNATIONAL_COUNTRY_CODES:
|
||||
qs = models.Carousel.objects.filter(id__in=settings.CAROUSEL_ITEMS)
|
||||
return qs
|
||||
qs = models.Carousel.objects.is_parsed().active()
|
||||
|
|
|
|||
|
|
@ -20,6 +20,16 @@ class NewsListFilterSet(filters.FilterSet):
|
|||
tag_value__in = filters.CharFilter(method='in_tags')
|
||||
type = filters.CharFilter(method='by_type')
|
||||
|
||||
state = filters.NumberFilter()
|
||||
|
||||
SORT_BY_CREATED_CHOICE = "created"
|
||||
SORT_BY_START_CHOICE = "start"
|
||||
SORT_BY_CHOICES = (
|
||||
(SORT_BY_CREATED_CHOICE, "created"),
|
||||
(SORT_BY_START_CHOICE, "start"),
|
||||
)
|
||||
sort_by = filters.ChoiceFilter(method='sort_by_field', choices=SORT_BY_CHOICES)
|
||||
|
||||
class Meta:
|
||||
"""Meta class"""
|
||||
model = models.News
|
||||
|
|
@ -29,6 +39,8 @@ class NewsListFilterSet(filters.FilterSet):
|
|||
'tag_group',
|
||||
'tag_value__exclude',
|
||||
'tag_value__in',
|
||||
'state',
|
||||
'sort_by',
|
||||
)
|
||||
|
||||
def in_tags(self, queryset, name, value):
|
||||
|
|
@ -58,3 +70,6 @@ class NewsListFilterSet(filters.FilterSet):
|
|||
return queryset.filter(news_type__name=value)
|
||||
else:
|
||||
return queryset
|
||||
|
||||
def sort_by_field(self, queryset, name, value):
|
||||
return queryset.order_by(f'-{value}')
|
||||
|
|
|
|||
|
|
@ -162,6 +162,7 @@ class NewsDetailWebSerializer(NewsDetailSerializer):
|
|||
|
||||
class NewsBackOfficeBaseSerializer(NewsBaseSerializer):
|
||||
"""News back office base serializer."""
|
||||
is_published = serializers.BooleanField(source='is_publish', read_only=True)
|
||||
|
||||
class Meta(NewsBaseSerializer.Meta):
|
||||
"""Meta class."""
|
||||
|
|
@ -169,6 +170,7 @@ class NewsBackOfficeBaseSerializer(NewsBaseSerializer):
|
|||
fields = NewsBaseSerializer.Meta.fields + (
|
||||
'title',
|
||||
'subtitle',
|
||||
'is_published',
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -108,8 +108,8 @@ class NewsBackOfficeGalleryCreateDestroyView(NewsBackOfficeMixinView,
|
|||
"""
|
||||
news_qs = self.filter_queryset(self.get_queryset())
|
||||
|
||||
news = get_object_or_404(news_qs, pk=self.kwargs['pk'])
|
||||
gallery = get_object_or_404(news.news_gallery, image_id=self.kwargs['image_id'])
|
||||
news = get_object_or_404(news_qs, pk=self.kwargs.get('pk'))
|
||||
gallery = get_object_or_404(news.news_gallery, image_id=self.kwargs.get('image_id'))
|
||||
|
||||
# May raise a permission denied
|
||||
self.check_object_permissions(self.request, gallery)
|
||||
|
|
@ -125,7 +125,7 @@ class NewsBackOfficeGalleryListView(NewsBackOfficeMixinView,
|
|||
def get_object(self):
|
||||
"""Override get_object method."""
|
||||
qs = super(NewsBackOfficeGalleryListView, self).get_queryset()
|
||||
news = get_object_or_404(qs, pk=self.kwargs['pk'])
|
||||
news = get_object_or_404(qs, pk=self.kwargs.get('pk'))
|
||||
|
||||
# May raise a permission denied
|
||||
self.check_object_permissions(self.request, news)
|
||||
|
|
|
|||
|
|
@ -6,3 +6,4 @@ from partner import models
|
|||
@admin.register(models.Partner)
|
||||
class PartnerModelAdmin(admin.ModelAdmin):
|
||||
"""Model admin for Partner model."""
|
||||
raw_id_fields = ('establishment',)
|
||||
|
|
|
|||
50
apps/partner/migrations/0003_auto_20191121_1059.py
Normal file
50
apps/partner/migrations/0003_auto_20191121_1059.py
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
# Generated by Django 2.2.7 on 2019-11-21 10:59
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('establishment', '0065_establishment_purchased_products'),
|
||||
('partner', '0002_auto_20191101_0939'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='partner',
|
||||
name='establishment',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='partners', to='establishment.Establishment', verbose_name='Establishment'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='partner',
|
||||
name='expiry_date',
|
||||
field=models.DateField(blank=True, null=True, verbose_name='expiry date'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='partner',
|
||||
name='name',
|
||||
field=models.CharField(blank=True, max_length=255, null=True, verbose_name='name'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='partner',
|
||||
name='old_id',
|
||||
field=models.PositiveIntegerField(blank=True, default=None, null=True, verbose_name='old id'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='partner',
|
||||
name='price_per_month',
|
||||
field=models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True, verbose_name='price per month'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='partner',
|
||||
name='starting_date',
|
||||
field=models.DateField(blank=True, null=True, verbose_name='starting date'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='partner',
|
||||
name='type',
|
||||
field=models.PositiveSmallIntegerField(choices=[(0, 'Partner'), (1, 'Sponsor')], default=0),
|
||||
),
|
||||
]
|
||||
|
|
@ -1,13 +1,36 @@
|
|||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from establishment.models import Establishment
|
||||
from utils.models import ImageMixin, ProjectBaseMixin
|
||||
|
||||
|
||||
class Partner(ProjectBaseMixin):
|
||||
"""Partner model."""
|
||||
|
||||
PARTNER = 0
|
||||
SPONSOR = 1
|
||||
MODEL_TYPES = (
|
||||
(PARTNER, _('Partner')),
|
||||
(SPONSOR, _('Sponsor')),
|
||||
)
|
||||
|
||||
old_id = models.PositiveIntegerField(_('old id'), blank=True, null=True, default=None)
|
||||
name = models.CharField(_('name'), max_length=255, blank=True, null=True)
|
||||
url = models.URLField(verbose_name=_('Partner URL'))
|
||||
image = models.URLField(verbose_name=_('Partner image URL'), null=True)
|
||||
establishment = models.ForeignKey(
|
||||
Establishment,
|
||||
verbose_name=_('Establishment'),
|
||||
related_name='partners',
|
||||
on_delete=models.CASCADE,
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
type = models.PositiveSmallIntegerField(choices=MODEL_TYPES, default=PARTNER)
|
||||
starting_date = models.DateField(_('starting date'), blank=True, null=True)
|
||||
expiry_date = models.DateField(_('expiry date'), blank=True, null=True)
|
||||
price_per_month = models.DecimalField(_('price per month'), max_digits=10, decimal_places=2, blank=True, null=True)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('partner')
|
||||
|
|
|
|||
|
|
@ -1,19 +1,37 @@
|
|||
from django.db.models import Value, IntegerField, F
|
||||
from pprint import pprint
|
||||
|
||||
from establishment.models import Establishment
|
||||
from partner.models import Partner
|
||||
from transfer.models import EstablishmentBacklinks
|
||||
from transfer.serializers.partner import PartnerSerializer
|
||||
|
||||
|
||||
def transfer_partner():
|
||||
queryset = EstablishmentBacklinks.objects.filter(type="Partner")
|
||||
"""
|
||||
Transfer data to Partner model only after transfer Establishment
|
||||
"""
|
||||
establishments = Establishment.objects.filter(old_id__isnull=False).values_list('old_id', flat=True)
|
||||
queryset = EstablishmentBacklinks.objects.filter(
|
||||
establishment_id__in=list(establishments),
|
||||
).values(
|
||||
'id',
|
||||
'establishment_id',
|
||||
'partnership_name',
|
||||
'partnership_icon',
|
||||
'backlink_url',
|
||||
'created_at',
|
||||
'type',
|
||||
'starting_date',
|
||||
'expiry_date',
|
||||
'price_per_month',
|
||||
)
|
||||
|
||||
# queryset = EstablishmentBacklinks.objects.all() # Partner and Sponsor
|
||||
|
||||
serialized_data = PartnerSerializer(data=list(queryset.values()), many=True)
|
||||
serialized_data = PartnerSerializer(data=list(queryset), many=True)
|
||||
if serialized_data.is_valid():
|
||||
Partner.objects.all().delete() # TODO: закоментить, если требуется сохранить старые записи
|
||||
serialized_data.save()
|
||||
else:
|
||||
pprint(f"News serializer errors: {serialized_data.errors}")
|
||||
pprint(f"Partner serializer errors: {serialized_data.errors}")
|
||||
|
||||
|
||||
data_types = {
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@ class Command(BaseCommand):
|
|||
TagCategory.objects.bulk_create(objects)
|
||||
self.stdout.write(self.style.WARNING(f'Add or get tag category objects.'))
|
||||
|
||||
|
||||
def product_type_category_sql(self):
|
||||
with connections['legacy'].cursor() as cursor:
|
||||
cursor.execute('''
|
||||
|
|
@ -56,9 +55,9 @@ class Command(BaseCommand):
|
|||
|
||||
def add_type_product_category(self):
|
||||
for c in tqdm(self.product_type_category_sql(), desc='Add type product category'):
|
||||
type = ProductType.objects.get(index_name='wine')
|
||||
type = ProductType.objects.get(index_name=ProductType.WINE)
|
||||
category = TagCategory.objects.get(index_name=c.tag_category)
|
||||
if type and category not in type.tag_categories.all():
|
||||
if category not in type.tag_categories.all():
|
||||
type.tag_categories.add(category)
|
||||
|
||||
self.stdout.write(self.style.WARNING(f'Add type product category objects.'))
|
||||
|
|
@ -116,7 +115,7 @@ class Command(BaseCommand):
|
|||
select
|
||||
DISTINCT
|
||||
m.product_id,
|
||||
lower(trim(CONVERT(m.value USING utf8))) as tag_value,
|
||||
trim(CONVERT(m.value USING utf8)) as tag_value,
|
||||
trim(CONVERT(v.key_name USING utf8)) as tag_category
|
||||
FROM product_metadata m
|
||||
JOIN product_key_value_metadata v on v.id = m.product_key_value_metadatum_id
|
||||
|
|
@ -133,7 +132,7 @@ class Command(BaseCommand):
|
|||
)
|
||||
product = Product.objects.get(old_id=t.product_id)
|
||||
for tag in tags:
|
||||
if product not in tag.products.all():
|
||||
if tag not in product.tags.all():
|
||||
product.tags.add(tag)
|
||||
|
||||
self.stdout.write(self.style.WARNING(f'Add or get tag objects.'))
|
||||
|
|
|
|||
30
apps/product/migrations/0018_purchasedproduct.py
Normal file
30
apps/product/migrations/0018_purchasedproduct.py
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
# Generated by Django 2.2.7 on 2019-11-20 12:49
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('establishment', '0064_auto_20191119_1546'),
|
||||
('product', '0017_auto_20191119_1546'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='PurchasedProduct',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('is_gifted', models.NullBooleanField(default=None, verbose_name='is gifted')),
|
||||
('quantity', models.PositiveSmallIntegerField(verbose_name='quantity')),
|
||||
('establishment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='purchased_plaques', to='establishment.Establishment', verbose_name='establishment')),
|
||||
('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='purchased_by_establishments', to='product.Product', verbose_name='plaque')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'purchased plaque',
|
||||
'verbose_name_plural': 'purchased plaques',
|
||||
'unique_together': {('establishment', 'product')},
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
@ -264,12 +264,23 @@ class Product(GalleryModelMixin, TranslatedFieldsMixin, BaseAttributes,
|
|||
def grape_variety(self):
|
||||
return self.tags.filter(category__index_name='grape-variety')
|
||||
|
||||
@property
|
||||
def bottle_sizes(self):
|
||||
return self.tags.filter(category__index_name='bottle_size')
|
||||
|
||||
@property
|
||||
def alcohol_percentage(self):
|
||||
qs = self.tags.filter(category__index_name='alcohol_percentage')
|
||||
if qs.exists():
|
||||
return qs.first()
|
||||
|
||||
@property
|
||||
def related_tags(self):
|
||||
return super().visible_tags.exclude(category__index_name__in=[
|
||||
'sugar-content', 'wine-color', 'bottles-produced',
|
||||
'serial-number', 'grape-variety']
|
||||
)
|
||||
'serial-number', 'grape-variety', 'serial_number',
|
||||
'alcohol_percentage', 'bottle_size',
|
||||
])
|
||||
|
||||
@property
|
||||
def display_name(self):
|
||||
|
|
@ -316,6 +327,26 @@ class OnlineProduct(Product):
|
|||
verbose_name_plural = _('Online products')
|
||||
|
||||
|
||||
class PurchasedProduct(models.Model):
|
||||
"""Model for storing establishment purchased plaques."""
|
||||
|
||||
establishment = models.ForeignKey('establishment.Establishment', on_delete=models.CASCADE,
|
||||
related_name='purchased_plaques',
|
||||
verbose_name=_('establishment'))
|
||||
product = models.ForeignKey('product.Product', on_delete=models.CASCADE,
|
||||
related_name='purchased_by_establishments',
|
||||
verbose_name=_('plaque'))
|
||||
is_gifted = models.NullBooleanField(default=None,
|
||||
verbose_name=_('is gifted'))
|
||||
quantity = models.PositiveSmallIntegerField(verbose_name=_('quantity'))
|
||||
|
||||
class Meta:
|
||||
"""Meta class."""
|
||||
verbose_name = _('purchased plaque')
|
||||
verbose_name_plural = _('purchased plaques')
|
||||
unique_together = ('establishment', 'product')
|
||||
|
||||
|
||||
class Unit(models.Model):
|
||||
"""Product unit model."""
|
||||
name = models.CharField(max_length=255,
|
||||
|
|
|
|||
|
|
@ -128,6 +128,8 @@ class ProductDetailSerializer(ProductBaseSerializer):
|
|||
bottles_produced = TagBaseSerializer(many=True, read_only=True)
|
||||
sugar_contents = TagBaseSerializer(many=True, read_only=True)
|
||||
grape_variety = TagBaseSerializer(many=True, read_only=True)
|
||||
bottle_sizes = TagBaseSerializer(many=True, read_only=True)
|
||||
alcohol_percentage = TagBaseSerializer(read_only=True)
|
||||
image_url = serializers.URLField(allow_null=True,
|
||||
read_only=True)
|
||||
new_image = ImageBaseSerializer(source='crop_main_image', allow_null=True, read_only=True)
|
||||
|
|
@ -146,6 +148,8 @@ class ProductDetailSerializer(ProductBaseSerializer):
|
|||
'new_image',
|
||||
'grape_variety',
|
||||
'average_price',
|
||||
'bottle_sizes',
|
||||
'alcohol_percentage',
|
||||
]
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -2,17 +2,6 @@ from pprint import pprint
|
|||
|
||||
from transfer import models as transfer_models
|
||||
from transfer.serializers import product as product_serializers
|
||||
from transfer.serializers.partner import PartnerSerializer
|
||||
|
||||
|
||||
def transfer_partner():
|
||||
queryset = transfer_models.EstablishmentBacklinks.objects.filter(type="Partner")
|
||||
|
||||
serialized_data = PartnerSerializer(data=list(queryset.values()), many=True)
|
||||
if serialized_data.is_valid():
|
||||
serialized_data.save()
|
||||
else:
|
||||
pprint(f"News serializer errors: {serialized_data.errors}")
|
||||
|
||||
|
||||
def transfer_wine_color():
|
||||
|
|
@ -50,8 +39,8 @@ def transfer_wine_bottles_produced():
|
|||
)
|
||||
queryset = [vars(query) for query in raw_queryset]
|
||||
serialized_data = product_serializers.WineBottlesProducedSerializer(
|
||||
data=queryset,
|
||||
many=True)
|
||||
data=queryset,
|
||||
many=True)
|
||||
if serialized_data.is_valid():
|
||||
serialized_data.save()
|
||||
else:
|
||||
|
|
@ -69,8 +58,8 @@ def transfer_wine_classification_type():
|
|||
)
|
||||
queryset = [vars(query) for query in raw_queryset]
|
||||
serialized_data = product_serializers.WineClassificationTypeSerializer(
|
||||
data=queryset,
|
||||
many=True)
|
||||
data=queryset,
|
||||
many=True)
|
||||
if serialized_data.is_valid():
|
||||
serialized_data.save()
|
||||
else:
|
||||
|
|
@ -79,10 +68,10 @@ def transfer_wine_classification_type():
|
|||
|
||||
def transfer_wine_standard():
|
||||
queryset = transfer_models.ProductClassification.objects.filter(parent_id__isnull=True) \
|
||||
.exclude(type='Classification')
|
||||
.exclude(type='Classification')
|
||||
serialized_data = product_serializers.ProductStandardSerializer(
|
||||
data=list(queryset.values()),
|
||||
many=True)
|
||||
data=list(queryset.values()),
|
||||
many=True)
|
||||
if serialized_data.is_valid():
|
||||
serialized_data.save()
|
||||
else:
|
||||
|
|
@ -92,8 +81,8 @@ def transfer_wine_standard():
|
|||
def transfer_wine_classifications():
|
||||
queryset = transfer_models.ProductClassification.objects.filter(type='Classification')
|
||||
serialized_data = product_serializers.ProductClassificationSerializer(
|
||||
data=list(queryset.values()),
|
||||
many=True)
|
||||
data=list(queryset.values()),
|
||||
many=True)
|
||||
if serialized_data.is_valid():
|
||||
serialized_data.save()
|
||||
else:
|
||||
|
|
@ -104,8 +93,8 @@ def transfer_product():
|
|||
errors = []
|
||||
queryset = transfer_models.Products.objects.all()
|
||||
serialized_data = product_serializers.ProductSerializer(
|
||||
data=list(queryset.values()),
|
||||
many=True)
|
||||
data=list(queryset.values()),
|
||||
many=True)
|
||||
if serialized_data.is_valid():
|
||||
serialized_data.save()
|
||||
else:
|
||||
|
|
@ -117,8 +106,8 @@ def transfer_product_note():
|
|||
errors = []
|
||||
queryset = transfer_models.ProductNotes.objects.exclude(text='')
|
||||
serialized_data = product_serializers.ProductNoteSerializer(
|
||||
data=list(queryset.values()),
|
||||
many=True)
|
||||
data=list(queryset.values()),
|
||||
many=True)
|
||||
if serialized_data.is_valid():
|
||||
serialized_data.save()
|
||||
else:
|
||||
|
|
@ -130,8 +119,8 @@ def transfer_plate():
|
|||
errors = []
|
||||
queryset = transfer_models.Merchandise.objects.all()
|
||||
serialized_data = product_serializers.PlateSerializer(
|
||||
data=list(queryset.values()),
|
||||
many=True)
|
||||
data=list(queryset.values()),
|
||||
many=True)
|
||||
if serialized_data.is_valid():
|
||||
serialized_data.save()
|
||||
else:
|
||||
|
|
@ -143,8 +132,8 @@ def transfer_plate_image():
|
|||
errors = []
|
||||
queryset = transfer_models.Merchandise.objects.all()
|
||||
serialized_data = product_serializers.PlateImageSerializer(
|
||||
data=list(queryset.values()),
|
||||
many=True)
|
||||
data=list(queryset.values()),
|
||||
many=True)
|
||||
if serialized_data.is_valid():
|
||||
serialized_data.save()
|
||||
else:
|
||||
|
|
@ -153,7 +142,6 @@ def transfer_plate_image():
|
|||
|
||||
|
||||
data_types = {
|
||||
"partner": [transfer_partner],
|
||||
"wine_characteristics": [
|
||||
transfer_wine_sugar_content,
|
||||
transfer_wine_color,
|
||||
|
|
@ -161,12 +149,12 @@ data_types = {
|
|||
transfer_wine_classification_type,
|
||||
transfer_wine_standard,
|
||||
transfer_wine_classifications,
|
||||
],
|
||||
],
|
||||
"product": [
|
||||
transfer_product,
|
||||
],
|
||||
"product_note": [
|
||||
transfer_product_note,
|
||||
transfer_product_note,
|
||||
],
|
||||
"souvenir": [
|
||||
transfer_plate,
|
||||
|
|
|
|||
|
|
@ -57,8 +57,8 @@ class ProductBackOfficeGalleryCreateDestroyView(ProductBackOfficeMixinView,
|
|||
"""
|
||||
product_qs = self.filter_queryset(self.get_queryset())
|
||||
|
||||
product = get_object_or_404(product_qs, pk=self.kwargs['pk'])
|
||||
gallery = get_object_or_404(product.product_gallery, image_id=self.kwargs['image_id'])
|
||||
product = get_object_or_404(product_qs, pk=self.kwargs.get('pk'))
|
||||
gallery = get_object_or_404(product.product_gallery, image_id=self.kwargs.get('image_id'))
|
||||
|
||||
# May raise a permission denied
|
||||
self.check_object_permissions(self.request, gallery)
|
||||
|
|
@ -75,7 +75,7 @@ class ProductBackOfficeGalleryListView(ProductBackOfficeMixinView,
|
|||
def get_object(self):
|
||||
"""Override get_object method."""
|
||||
qs = super(ProductBackOfficeGalleryListView, self).get_queryset()
|
||||
product = get_object_or_404(qs, pk=self.kwargs['pk'])
|
||||
product = get_object_or_404(qs, pk=self.kwargs.get('pk'))
|
||||
|
||||
# May raise a permission denied
|
||||
self.check_object_permissions(self.request, product)
|
||||
|
|
@ -149,7 +149,7 @@ class ProductNoteListCreateView(ProductBackOfficeMixinView,
|
|||
product_qs = models.Product.objects.all()
|
||||
filtered_product_qs = self.filter_queryset(product_qs)
|
||||
|
||||
product = get_object_or_404(filtered_product_qs, pk=self.kwargs['pk'])
|
||||
product = get_object_or_404(filtered_product_qs, pk=self.kwargs.get('pk'))
|
||||
|
||||
# May raise a permission denied
|
||||
self.check_object_permissions(self.request, product)
|
||||
|
|
@ -173,8 +173,8 @@ class ProductNoteRUDView(ProductBackOfficeMixinView,
|
|||
product_qs = models.Product.objects.all()
|
||||
filtered_product_qs = self.filter_queryset(product_qs)
|
||||
|
||||
product = get_object_or_404(filtered_product_qs, pk=self.kwargs['pk'])
|
||||
note = get_object_or_404(product.notes.all(), pk=self.kwargs['note_pk'])
|
||||
product = get_object_or_404(filtered_product_qs, pk=self.kwargs.get('pk'))
|
||||
note = get_object_or_404(product.notes.all(), pk=self.kwargs.get('note_pk'))
|
||||
|
||||
# May raise a permission denied
|
||||
self.check_object_permissions(self.request, note)
|
||||
|
|
|
|||
|
|
@ -14,7 +14,9 @@ class ReviewBaseSerializer(serializers.ModelSerializer):
|
|||
'child',
|
||||
'published_at',
|
||||
'vintage',
|
||||
'country'
|
||||
'country',
|
||||
'content_type',
|
||||
'object_id',
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ class BaseTestCase(APITestCase):
|
|||
username=self.username,
|
||||
email=self.email,
|
||||
password=self.password,
|
||||
is_staff=True,
|
||||
)
|
||||
|
||||
tokens = User.create_jwt_tokens(self.user)
|
||||
|
|
@ -61,6 +62,49 @@ class BaseTestCase(APITestCase):
|
|||
)
|
||||
|
||||
|
||||
class ReviewTestCase(BaseTestCase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
||||
def test_review_list(self):
|
||||
response = self.client.get('/api/back/review/')
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
def test_review_post(self):
|
||||
test_review = {
|
||||
'reviewer': self.user.id,
|
||||
'status': Review.READY,
|
||||
'vintage': 2019,
|
||||
'country': self.country_ru.id,
|
||||
'object_id': 1,
|
||||
'content_type': 1,
|
||||
}
|
||||
response = self.client.post('/api/back/review/', data=test_review)
|
||||
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||
|
||||
def test_review_detail(self):
|
||||
response = self.client.get(f'/api/back/review/{self.test_review.id}/', format='json')
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
def test_review_detail_put(self):
|
||||
data = {
|
||||
'id': self.test_review.id,
|
||||
'vintage': 2018,
|
||||
'reviewer': self.user.id,
|
||||
'status': Review.READY,
|
||||
'country': self.country_ru.id,
|
||||
'object_id': 1,
|
||||
'content_type': 1,
|
||||
}
|
||||
|
||||
response = self.client.put(f'/api/back/review/{self.test_review.id}/', data=data, format='json')
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
def test_review_delete(self):
|
||||
response = self.client.delete(f'/api/back/review/{self.test_review.id}/')
|
||||
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
|
||||
|
||||
|
||||
class InquiriesTestCase(BaseTestCase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
from django_filters.rest_framework import DjangoFilterBackend
|
||||
from rest_framework import generics, permissions
|
||||
|
||||
from review import filters
|
||||
from review import models
|
||||
from review import serializers
|
||||
from review import filters
|
||||
from utils.permissions import IsReviewerManager, IsRestaurantReviewer
|
||||
|
||||
|
||||
|
|
@ -12,7 +11,6 @@ class ReviewLstView(generics.ListCreateAPIView):
|
|||
serializer_class = serializers.ReviewBaseSerializer
|
||||
queryset = models.Review.objects.all()
|
||||
permission_classes = [permissions.IsAuthenticatedOrReadOnly, ]
|
||||
filter_backends = (DjangoFilterBackend,)
|
||||
filterset_class = filters.ReviewFilter
|
||||
|
||||
|
||||
|
|
@ -20,7 +18,7 @@ class ReviewRUDView(generics.RetrieveUpdateDestroyAPIView):
|
|||
"""Comment RUD view."""
|
||||
serializer_class = serializers.ReviewBaseSerializer
|
||||
queryset = models.Review.objects.all()
|
||||
permission_classes = [IsReviewerManager | IsRestaurantReviewer]
|
||||
permission_classes = [permissions.IsAdminUser | IsReviewerManager | IsRestaurantReviewer]
|
||||
lookup_field = 'id'
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -71,14 +71,21 @@ class CustomSearchFilterBackend(SearchFilterBackend):
|
|||
Q("match", **{k: v})
|
||||
)
|
||||
__queries.append(
|
||||
Q('wildcard', **{k: f'*{search_term.lower()}*'})
|
||||
Q('wildcard',
|
||||
**{k: {
|
||||
'value': f'*{search_term.lower()}*',
|
||||
'boost': v.get('boost', 1) + 30
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
else:
|
||||
__queries.append(
|
||||
Q("match", **field_kwargs)
|
||||
)
|
||||
__queries.append(
|
||||
Q('wildcard', **{field: f'*{search_term.lower()}*'})
|
||||
Q('wildcard', **{field: {'value': f'*{search_term.lower()}*',
|
||||
'boost': field_kwargs[field].get('boost', 1) + 30}})
|
||||
)
|
||||
else:
|
||||
for field in view.search_fields:
|
||||
|
|
@ -99,13 +106,20 @@ class CustomSearchFilterBackend(SearchFilterBackend):
|
|||
Q("match", **{k: v})
|
||||
)
|
||||
__queries.append(
|
||||
Q('wildcard', **{k: f'*{search_term.lower()}*'})
|
||||
Q('wildcard',
|
||||
**{k: {
|
||||
'value': f'*{search_term.lower()}*',
|
||||
'boost': v.get('boost', 1) + 30
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
else:
|
||||
__queries.append(
|
||||
Q("match", **field_kwargs)
|
||||
)
|
||||
__queries.append(
|
||||
Q('wildcard', **{field: f'*{search_term.lower()}*'})
|
||||
Q('wildcard', **{field: {'value': f'*{search_term.lower()}*',
|
||||
'boost': field_kwargs[field].get('boost', 1) + 30}})
|
||||
)
|
||||
return __queries
|
||||
|
|
@ -43,8 +43,8 @@ class TagCategoryFilterSet(TagsBaseFilterSet):
|
|||
'product_type', )
|
||||
|
||||
def by_product_type(self, queryset, name, value):
|
||||
if value == product_models.ProductType.WINE:
|
||||
queryset = queryset.filter(index_name='wine-color').filter(tags__products__isnull=False)
|
||||
# if value == product_models.ProductType.WINE:
|
||||
# queryset = queryset.filter(index_name='wine-color').filter(tags__products__isnull=False)
|
||||
queryset = queryset.by_product_type(value)
|
||||
return queryset
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,9 @@ from timetable.models import Timetable
|
|||
|
||||
class ScheduleRUDSerializer(serializers.ModelSerializer):
|
||||
"""Serializer for Establishment model."""
|
||||
NULLABLE_FIELDS = ['lunch_start', 'lunch_end', 'dinner_start',
|
||||
'dinner_end', 'opening_at', 'closed_at']
|
||||
|
||||
weekday_display = serializers.CharField(source='get_weekday_display',
|
||||
read_only=True)
|
||||
|
||||
|
|
@ -18,9 +21,6 @@ class ScheduleRUDSerializer(serializers.ModelSerializer):
|
|||
opening_at = serializers.TimeField(required=False)
|
||||
closed_at = serializers.TimeField(required=False)
|
||||
|
||||
NULLABLE_FIELDS = ['lunch_start', 'lunch_end', 'dinner_start',
|
||||
'dinner_end', 'opening_at', 'closed_at']
|
||||
|
||||
class Meta:
|
||||
"""Meta class."""
|
||||
model = Timetable
|
||||
|
|
|
|||
|
|
@ -39,6 +39,8 @@ class Command(BaseCommand):
|
|||
'rating_count',
|
||||
'product_review',
|
||||
'newsletter_subscriber', # подписчики на рассылку - переносить после переноса пользователей №1
|
||||
'purchased_plaques', # №6 - перенос купленных тарелок
|
||||
'fill_city_gallery', # №3 - перенос галереи городов
|
||||
]
|
||||
|
||||
def handle(self, *args, **options):
|
||||
|
|
|
|||
|
|
@ -217,10 +217,10 @@ class CityNames(MigrateMixin):
|
|||
class CityPhotos(MigrateMixin):
|
||||
using = 'legacy'
|
||||
|
||||
# city_id = models.IntegerField(blank=True, null=True)
|
||||
city = models.ForeignKey(Cities, models.DO_NOTHING, blank=True, null=True)
|
||||
attachment_file_name = models.CharField(max_length=255, blank=True, null=True)
|
||||
attachment_content_type = models.CharField(max_length=255, blank=True, null=True)
|
||||
attachment_suffix_url = models.CharField(max_length=255)
|
||||
geometries = models.CharField(max_length=1024, blank=True, null=True)
|
||||
attachment_file_size = models.IntegerField(blank=True, null=True)
|
||||
attachment_updated_at = models.DateTimeField(blank=True, null=True)
|
||||
|
|
@ -581,22 +581,19 @@ class EstablishmentInfos(MigrateMixin):
|
|||
db_table = 'establishment_infos'
|
||||
|
||||
|
||||
# class EstablishmentMerchandises(MigrateMixin):
|
||||
# using = 'legacy'
|
||||
#
|
||||
# establishment = models.ForeignKey('Establishments', models.DO_NOTHING, blank=True, null=True)
|
||||
class EstablishmentMerchandises(MigrateMixin):
|
||||
using = 'legacy'
|
||||
|
||||
# TODO: модели Merchandises нету в гугл таблице Check Migrations
|
||||
establishment = models.ForeignKey('Establishments', models.DO_NOTHING, blank=True, null=True)
|
||||
merchandise = models.ForeignKey('Merchandise', models.DO_NOTHING, blank=True, null=True)
|
||||
gifted = models.NullBooleanField(blank=True, null=True)
|
||||
quantity = models.IntegerField(blank=True, null=True)
|
||||
created_at = models.DateTimeField()
|
||||
updated_at = models.DateTimeField()
|
||||
|
||||
# merchandise = models.ForeignKey('Merchandises', models.DO_NOTHING, blank=True, null=True)
|
||||
# gifted = models.IntegerField(blank=True, null=True)
|
||||
# quantity = models.IntegerField(blank=True, null=True)
|
||||
# created_at = models.DateTimeField()
|
||||
# updated_at = models.DateTimeField()
|
||||
#
|
||||
# class Meta:
|
||||
# managed = False
|
||||
# db_table = 'establishment_merchandises'
|
||||
class Meta:
|
||||
managed = False
|
||||
db_table = 'establishment_merchandises'
|
||||
|
||||
|
||||
class Menus(MigrateMixin):
|
||||
|
|
|
|||
|
|
@ -1,28 +1,53 @@
|
|||
from rest_framework import serializers
|
||||
|
||||
from establishment.models import Establishment
|
||||
from partner.models import Partner
|
||||
|
||||
|
||||
class PartnerSerializer(serializers.ModelSerializer):
|
||||
backlink_url = serializers.CharField(source="url")
|
||||
partnership_icon = serializers.CharField()
|
||||
partnership_name = serializers.CharField()
|
||||
|
||||
class Meta:
|
||||
model = Partner
|
||||
fields = (
|
||||
"backlink_url",
|
||||
"partnership_icon",
|
||||
"partnership_name"
|
||||
)
|
||||
class PartnerSerializer(serializers.Serializer):
|
||||
id = serializers.IntegerField()
|
||||
establishment_id = serializers.IntegerField()
|
||||
partnership_name = serializers.CharField(allow_null=True)
|
||||
partnership_icon = serializers.CharField(allow_null=True)
|
||||
backlink_url = serializers.CharField(allow_null=True)
|
||||
created_at = serializers.DateTimeField(format='%m-%d-%Y %H:%M:%S')
|
||||
type = serializers.CharField(allow_null=True)
|
||||
starting_date = serializers.DateField(allow_null=True)
|
||||
expiry_date = serializers.DateField(allow_null=True)
|
||||
price_per_month = serializers.DecimalField(max_digits=10, decimal_places=2, allow_null=True)
|
||||
|
||||
def validate(self, data):
|
||||
data["image"] = partnership_to_image_url.get(data["partnership_name"]).get(data["partnership_icon"])
|
||||
data.pop("partnership_name")
|
||||
data.pop("partnership_icon")
|
||||
data.update({
|
||||
'old_id': data.pop('id'),
|
||||
'name': data['partnership_name'],
|
||||
'url': data.pop('backlink_url'),
|
||||
'image': self.get_image(data),
|
||||
'establishment': self.get_establishment(data),
|
||||
'type': Partner.PARTNER if data['type'] == 'Partner' else Partner.SPONSOR,
|
||||
'created': data.pop('created_at'),
|
||||
})
|
||||
data.pop('partnership_icon')
|
||||
data.pop('partnership_name')
|
||||
data.pop('establishment_id')
|
||||
return data
|
||||
|
||||
@staticmethod
|
||||
def get_image(data):
|
||||
return partnership_to_image_url.get(data['partnership_name']).get(data['partnership_icon'])
|
||||
|
||||
@staticmethod
|
||||
def get_establishment(data):
|
||||
establishment = Establishment.objects.filter(old_id=data['establishment_id']).first()
|
||||
if not establishment:
|
||||
raise ValueError(f"Establishment not found with old_id {data['establishment_id']}: ")
|
||||
return establishment
|
||||
|
||||
def create(self, validated_data):
|
||||
return Partner.objects.create(**validated_data)
|
||||
obj, _ = Partner.objects.update_or_create(
|
||||
old_id=validated_data['old_id'],
|
||||
defaults=validated_data,
|
||||
)
|
||||
return obj
|
||||
|
||||
|
||||
partnership_to_image_url = {
|
||||
|
|
|
|||
|
|
@ -265,7 +265,7 @@ class ProductSerializer(TransferSerializerMixin):
|
|||
state = serializers.CharField()
|
||||
bottles_produced = serializers.CharField(allow_null=True, allow_blank=True)
|
||||
unique_key = serializers.CharField(allow_null=True)
|
||||
price = serializers.DecimalField(max_digits=14, decimal_places=2)
|
||||
price = serializers.DecimalField(max_digits=14, decimal_places=2, allow_null=True)
|
||||
|
||||
class Meta:
|
||||
model = models.Product
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ from django.conf import settings
|
|||
from django.db.transaction import on_commit
|
||||
from django.shortcuts import get_object_or_404
|
||||
from rest_framework import generics, status
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.response import Response
|
||||
|
||||
from gallery.tasks import delete_image
|
||||
|
|
@ -12,7 +13,7 @@ from search_indexes.documents import es_update
|
|||
|
||||
# JWT
|
||||
# Login base view mixins
|
||||
class JWTGenericViewMixin(generics.GenericAPIView):
|
||||
class JWTGenericViewMixin:
|
||||
"""JWT view mixin"""
|
||||
|
||||
ACCESS_TOKEN_HTTP_ONLY = False
|
||||
|
|
@ -39,30 +40,31 @@ class JWTGenericViewMixin(generics.GenericAPIView):
|
|||
"""
|
||||
COOKIES = []
|
||||
|
||||
if hasattr(self.request, 'locale'):
|
||||
COOKIES.append(self.COOKIE(key='locale',
|
||||
value=self.request.locale,
|
||||
http_only=self.ACCESS_TOKEN_HTTP_ONLY,
|
||||
secure=self.LOCALE_SECURE,
|
||||
max_age=settings.COOKIES_MAX_AGE if permanent else None))
|
||||
if hasattr(self.request, 'country_code'):
|
||||
COOKIES.append(self.COOKIE(key='country_code',
|
||||
value=self.request.country_code,
|
||||
http_only=self.COUNTRY_CODE_HTTP_ONLY,
|
||||
secure=self.COUNTRY_CODE_SECURE,
|
||||
max_age=settings.COOKIES_MAX_AGE if permanent else None))
|
||||
if access_token:
|
||||
COOKIES.append(self.COOKIE(key='access_token',
|
||||
value=access_token,
|
||||
http_only=self.ACCESS_TOKEN_HTTP_ONLY,
|
||||
secure=self.ACCESS_TOKEN_SECURE,
|
||||
max_age=settings.COOKIES_MAX_AGE if permanent else None))
|
||||
if refresh_token:
|
||||
COOKIES.append(self.COOKIE(key='refresh_token',
|
||||
value=refresh_token,
|
||||
http_only=self.REFRESH_TOKEN_HTTP_ONLY,
|
||||
secure=self.REFRESH_TOKEN_SECURE,
|
||||
max_age=settings.COOKIES_MAX_AGE if permanent else None))
|
||||
if hasattr(self, 'request'):
|
||||
if hasattr(self.request, 'locale'):
|
||||
COOKIES.append(self.COOKIE(key='locale',
|
||||
value=self.request.locale,
|
||||
http_only=self.ACCESS_TOKEN_HTTP_ONLY,
|
||||
secure=self.LOCALE_SECURE,
|
||||
max_age=settings.COOKIES_MAX_AGE if permanent else None))
|
||||
if hasattr(self.request, 'country_code'):
|
||||
COOKIES.append(self.COOKIE(key='country_code',
|
||||
value=self.request.country_code,
|
||||
http_only=self.COUNTRY_CODE_HTTP_ONLY,
|
||||
secure=self.COUNTRY_CODE_SECURE,
|
||||
max_age=settings.COOKIES_MAX_AGE if permanent else None))
|
||||
if access_token:
|
||||
COOKIES.append(self.COOKIE(key='access_token',
|
||||
value=access_token,
|
||||
http_only=self.ACCESS_TOKEN_HTTP_ONLY,
|
||||
secure=self.ACCESS_TOKEN_SECURE,
|
||||
max_age=settings.COOKIES_MAX_AGE if permanent else None))
|
||||
if refresh_token:
|
||||
COOKIES.append(self.COOKIE(key='refresh_token',
|
||||
value=refresh_token,
|
||||
http_only=self.REFRESH_TOKEN_HTTP_ONLY,
|
||||
secure=self.REFRESH_TOKEN_SECURE,
|
||||
max_age=settings.COOKIES_MAX_AGE if permanent else None))
|
||||
return COOKIES
|
||||
|
||||
def _put_cookies_in_response(self, cookies: list, response: Response):
|
||||
|
|
@ -156,3 +158,29 @@ class FavoritesCreateDestroyMixinView(generics.CreateAPIView,
|
|||
instance.delete()
|
||||
self.es_update_base_object()
|
||||
|
||||
|
||||
# BackOffice user`s views & viewsets
|
||||
class BindObjectMixin:
|
||||
"""Bind object mixin."""
|
||||
|
||||
def get_serializer_class(self):
|
||||
if self.action == 'bind_object':
|
||||
return self.bind_object_serializer_class
|
||||
return self.serializer_class
|
||||
|
||||
def perform_binding(self, serializer):
|
||||
raise NotImplemented
|
||||
|
||||
def perform_unbinding(self, serializer):
|
||||
raise NotImplemented
|
||||
|
||||
@action(methods=['post', 'delete'], detail=True, url_path='bind-object')
|
||||
def bind_object(self, request, pk=None):
|
||||
serializer = self.get_serializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
if request.method == 'POST':
|
||||
self.perform_binding(serializer)
|
||||
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
||||
elif request.method == 'DELETE':
|
||||
self.perform_unbinding(serializer)
|
||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||
|
|
|
|||
|
|
@ -509,9 +509,8 @@ FALLBACK_LOCALE = 'en-GB'
|
|||
|
||||
# TMP TODO remove it later
|
||||
# Временный хардкод для демонстрации > 15 ноября, потом удалить!
|
||||
CAROUSEL_ITEMS = [230, 231, 232]
|
||||
CAROUSEL_ITEMS = [465]
|
||||
ESTABLISHMENT_CHOSEN_TAGS = ['gastronomic', 'en_vogue', 'terrace', 'streetfood', 'business', 'bar_cocktail', 'brunch', 'pop']
|
||||
NEWS_CHOSEN_TAGS = ['eat', 'drink', 'cook', 'style', 'international', 'event', 'partnership']
|
||||
INTERNATIONAL_COUNTRY_CODES = ['www', 'main', 'next']
|
||||
|
||||
ELASTICSEARCH_DSL_AUTOSYNC = False
|
||||
|
|
@ -33,6 +33,7 @@ ELASTICSEARCH_INDEX_NAMES = {
|
|||
'search_indexes.documents.product': 'development_product',
|
||||
}
|
||||
|
||||
# ELASTICSEARCH_DSL_AUTOSYNC = False
|
||||
|
||||
sentry_sdk.init(
|
||||
dsn="https://35d9bb789677410ab84a822831c6314f@sentry.io/1729093",
|
||||
|
|
|
|||
|
|
@ -15,5 +15,5 @@ urlpatterns = [
|
|||
path('products/', include(('product.urls.back', 'product'), namespace='product')),
|
||||
path('re_blocks/', include(('advertisement.urls.back', 'advertisement'),
|
||||
namespace='advertisement')),
|
||||
path('main/', include('main.urls.back')),
|
||||
]
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user