Merge branch 'feature/gm-192' into feature/gm-148
This commit is contained in:
commit
2dbe65a169
|
|
@ -10,6 +10,10 @@ class EstablishmentFilter(filters.FilterSet):
|
|||
tag_id = filters.NumberFilter(field_name='tags__metadata__id',)
|
||||
award_id = filters.NumberFilter(field_name='awards__id',)
|
||||
search = filters.CharFilter(method='search_text')
|
||||
est_type = filters.ChoiceFilter(choices=models.EstablishmentType.INDEX_NAME_TYPES,
|
||||
method='by_type')
|
||||
est_subtype = filters.ChoiceFilter(choices=models.EstablishmentSubType.INDEX_NAME_TYPES,
|
||||
method='by_subtype')
|
||||
|
||||
class Meta:
|
||||
"""Meta class."""
|
||||
|
|
@ -19,6 +23,8 @@ class EstablishmentFilter(filters.FilterSet):
|
|||
'tag_id',
|
||||
'award_id',
|
||||
'search',
|
||||
'est_type',
|
||||
'est_subtype',
|
||||
)
|
||||
|
||||
def search_text(self, queryset, name, value):
|
||||
|
|
@ -27,6 +33,12 @@ class EstablishmentFilter(filters.FilterSet):
|
|||
return queryset.search(value, locale=self.request.locale)
|
||||
return queryset
|
||||
|
||||
def by_type(self, queryset, name, value):
|
||||
return queryset.by_type(value)
|
||||
|
||||
def by_subtype(self, queryset, name, value):
|
||||
return queryset.by_subtype(value)
|
||||
|
||||
|
||||
class EstablishmentTypeTagFilter(filters.FilterSet):
|
||||
"""Establishment tag filter set."""
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
# Generated by Django 2.2.4 on 2019-10-16 11:33
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
def fill_establishment_type(apps, schema_editor):
|
||||
# We can't import the Person model directly as it may be a newer
|
||||
# version than this migration expects. We use the historical version.
|
||||
EstablishmentType = apps.get_model('establishment', 'EstablishmentType')
|
||||
for n, et in enumerate(EstablishmentType.objects.all()):
|
||||
et.index_name = f'Type {n}'
|
||||
et.save()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('establishment', '0037_auto_20191015_1404'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='establishmenttype',
|
||||
name='index_name',
|
||||
field=models.CharField(blank=True, db_index=True, max_length=50, null=True, unique=True, default=None, verbose_name='Index name'),
|
||||
),
|
||||
migrations.RunPython(fill_establishment_type, migrations.RunPython.noop),
|
||||
migrations.AlterField(
|
||||
model_name='establishmenttype',
|
||||
name='index_name',
|
||||
field=models.CharField(choices=[('restaurant', 'Restaurant'), ('artisan', 'Artisan'),
|
||||
('producer', 'Producer')], db_index=True, max_length=50,
|
||||
unique=True, verbose_name='Index name'),
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
# Generated by Django 2.2.4 on 2019-10-18 13:47
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
def fill_establishment_subtype(apps, schema_editor):
|
||||
# We can't import the Person model directly as it may be a newer
|
||||
# version than this migration expects. We use the historical version.
|
||||
EstablishmentSubType = apps.get_model('establishment', 'EstablishmentSubType')
|
||||
for n, et in enumerate(EstablishmentSubType.objects.all()):
|
||||
et.index_name = f'Type {n}'
|
||||
et.save()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('establishment', '0038_establishmenttype_index_name'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='establishmentsubtype',
|
||||
name='index_name',
|
||||
field=models.CharField(blank=True, db_index=True, max_length=50, null=True, unique=True, default=None, verbose_name='Index name'),
|
||||
),
|
||||
migrations.RunPython(fill_establishment_subtype),
|
||||
migrations.AlterField(
|
||||
model_name='establishmentsubtype',
|
||||
name='index_name',
|
||||
field=models.CharField(choices=[('winery', 'Winery'), ], db_index=True, max_length=50,
|
||||
unique=True, verbose_name='Index name'),
|
||||
),
|
||||
|
||||
]
|
||||
|
|
@ -30,8 +30,22 @@ class EstablishmentType(TranslatedFieldsMixin, ProjectBaseMixin):
|
|||
|
||||
STR_FIELD_NAME = 'name'
|
||||
|
||||
# INDEX NAME CHOICES
|
||||
RESTAURANT = 'restaurant'
|
||||
ARTISAN = 'artisan'
|
||||
PRODUCER = 'producer'
|
||||
|
||||
INDEX_NAME_TYPES = (
|
||||
(RESTAURANT, _('Restaurant')),
|
||||
(ARTISAN, _('Artisan')),
|
||||
(PRODUCER, _('Producer')),
|
||||
)
|
||||
|
||||
name = TJSONField(blank=True, null=True, default=None, verbose_name=_('Description'),
|
||||
help_text='{"en-GB":"some text"}')
|
||||
index_name = models.CharField(max_length=50, choices=INDEX_NAME_TYPES,
|
||||
unique=True, db_index=True,
|
||||
verbose_name=_('Index name'))
|
||||
use_subtypes = models.BooleanField(_('Use subtypes'), default=True)
|
||||
tag_categories = models.ManyToManyField('tag.TagCategory',
|
||||
related_name='establishment_types',
|
||||
|
|
@ -57,8 +71,18 @@ class EstablishmentSubTypeManager(models.Manager):
|
|||
class EstablishmentSubType(ProjectBaseMixin, TranslatedFieldsMixin):
|
||||
"""Establishment type model."""
|
||||
|
||||
# INDEX NAME CHOICES
|
||||
WINERY = 'winery'
|
||||
|
||||
INDEX_NAME_TYPES = (
|
||||
(WINERY, _('Winery')),
|
||||
)
|
||||
|
||||
name = TJSONField(blank=True, null=True, default=None, verbose_name=_('Description'),
|
||||
help_text='{"en-GB":"some text"}')
|
||||
index_name = models.CharField(max_length=50, choices=INDEX_NAME_TYPES,
|
||||
unique=True, db_index=True,
|
||||
verbose_name=_('Index name'))
|
||||
establishment_type = models.ForeignKey(EstablishmentType,
|
||||
on_delete=models.CASCADE,
|
||||
verbose_name=_('Type'))
|
||||
|
|
@ -93,6 +117,9 @@ class EstablishmentQuerySet(models.QuerySet):
|
|||
'phones').\
|
||||
prefetch_actual_employees()
|
||||
|
||||
def with_type_related(self):
|
||||
return self.prefetch_related('establishment_subtypes')
|
||||
|
||||
def search(self, value, locale=None):
|
||||
"""Search text in JSON fields."""
|
||||
if locale is not None:
|
||||
|
|
@ -240,6 +267,31 @@ class EstablishmentQuerySet(models.QuerySet):
|
|||
kwargs = {unit: radius}
|
||||
return self.filter(address__coordinates__distance_lte=(center, DistanceMeasure(**kwargs)))
|
||||
|
||||
def artisans(self):
|
||||
"""Return artisans."""
|
||||
return self.filter(establishment_type__index_name=EstablishmentType.ARTISAN)
|
||||
|
||||
def producers(self):
|
||||
"""Return producers."""
|
||||
return self.filter(establishment_type__index_name=EstablishmentType.PRODUCER)
|
||||
|
||||
def restaurants(self):
|
||||
"""Return restaurants."""
|
||||
return self.filter(establishment_type__index_name=EstablishmentType.RESTAURANT)
|
||||
|
||||
def wineries(self):
|
||||
"""Return wineries."""
|
||||
return self.producers().filter(
|
||||
establishment_subtypes__index_name=EstablishmentSubType.WINERY)
|
||||
|
||||
def by_type(self, value):
|
||||
"""Return QuerySet with type by value."""
|
||||
return self.filter(establishment_type__index_name=value)
|
||||
|
||||
def by_subtype(self, value):
|
||||
"""Return QuerySet with subtype by value."""
|
||||
return self.filter(establishment_subtypes__index_name=value)
|
||||
|
||||
|
||||
class Establishment(ProjectBaseMixin, URLImageMixin, TranslatedFieldsMixin):
|
||||
"""Establishment model."""
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ urlpatterns = [
|
|||
path('', views.EstablishmentListView.as_view(), name='list'),
|
||||
path('recent-reviews/', views.EstablishmentRecentReviewListView.as_view(),
|
||||
name='recent-reviews'),
|
||||
# path('wineries/', views.WineriesListView.as_view(), name='wineries-list'),
|
||||
path('slug/<slug:slug>/', views.EstablishmentRetrieveView.as_view(), name='detail'),
|
||||
path('slug/<slug:slug>/similar/', views.EstablishmentSimilarListView.as_view(), name='similar'),
|
||||
path('slug/<slug:slug>/comments/', views.EstablishmentCommentListView.as_view(), name='list-comments'),
|
||||
|
|
|
|||
|
|
@ -174,3 +174,14 @@ class EstablishmentNearestRetrieveView(EstablishmentListView, generics.ListAPIVi
|
|||
return qs.by_distance_from_point(**{k: v for k, v in filter_kwargs.items()
|
||||
if v is not None})
|
||||
return qs
|
||||
|
||||
|
||||
# Wineries
|
||||
# todo: find out about difference between subtypes data
|
||||
# class WineriesListView(EstablishmentListView):
|
||||
# """Return list establishments with type Wineries"""
|
||||
#
|
||||
# def get_queryset(self):
|
||||
# """Overridden get_queryset method."""
|
||||
# qs = super(WineriesListView, self).get_queryset()
|
||||
# return qs.with_type_related().wineries()
|
||||
|
|
|
|||
|
|
@ -40,12 +40,8 @@ def update_document(sender, **kwargs):
|
|||
for establishment in establishments:
|
||||
registry.update(establishment)
|
||||
|
||||
if app_label == 'main':
|
||||
if model_name == 'metadata':
|
||||
establishments = Establishment.objects.filter(tags__metadata=instance)
|
||||
for establishment in establishments:
|
||||
registry.update(establishment)
|
||||
if model_name == 'metadatacontent':
|
||||
if app_label == 'tag':
|
||||
if model_name == 'tag':
|
||||
establishments = Establishment.objects.filter(tags=instance)
|
||||
for establishment in establishments:
|
||||
registry.update(establishment)
|
||||
|
|
@ -70,12 +66,8 @@ def update_news(sender, **kwargs):
|
|||
for news in qs:
|
||||
registry.update(news)
|
||||
|
||||
if app_label == 'main':
|
||||
if model_name == 'metadata':
|
||||
qs = News.objects.filter(tags__metadata=instance)
|
||||
for news in qs:
|
||||
registry.update(news)
|
||||
if model_name == 'metadatacontent':
|
||||
if app_label == 'tag':
|
||||
if model_name == 'tag':
|
||||
qs = News.objects.filter(tags=instance)
|
||||
for news in qs:
|
||||
registry.update(news)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
"""Tag app filters."""
|
||||
from django_filters import rest_framework as filters
|
||||
from establishment.models import EstablishmentType
|
||||
from tag import models
|
||||
|
||||
|
||||
|
|
@ -18,16 +19,9 @@ class TagCategoryFilterSet(filters.FilterSet):
|
|||
type = filters.MultipleChoiceFilter(choices=TYPE_CHOICES,
|
||||
method='filter_by_type')
|
||||
|
||||
# Establishment type choices
|
||||
RESTAURANT = 'restaurant'
|
||||
|
||||
ESTABLISHMENT_TYPE_CHOICES = (
|
||||
(RESTAURANT, 'restaurant'),
|
||||
)
|
||||
|
||||
establishment_type = filters.MultipleChoiceFilter(
|
||||
choices=ESTABLISHMENT_TYPE_CHOICES,
|
||||
method='filter_by_establishment_type')
|
||||
establishment_type = filters.ChoiceFilter(
|
||||
choices=EstablishmentType.INDEX_NAME_TYPES,
|
||||
method='by_establishment_type')
|
||||
|
||||
class Meta:
|
||||
"""Meta class."""
|
||||
|
|
@ -44,5 +38,5 @@ class TagCategoryFilterSet(filters.FilterSet):
|
|||
return queryset
|
||||
|
||||
# todo: filter by establishment type
|
||||
def filter_by_establishment_type(self, queryset, name, value):
|
||||
return queryset.for_establishments()
|
||||
def by_establishment_type(self, queryset, name, value):
|
||||
return queryset.by_establishment_type(value)
|
||||
|
|
|
|||
19
apps/tag/migrations/0003_auto_20191018_0758.py
Normal file
19
apps/tag/migrations/0003_auto_20191018_0758.py
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
# Generated by Django 2.2.4 on 2019-10-18 07:58
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('tag', '0002_auto_20191009_1408'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='tag',
|
||||
name='category',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='tags', to='tag.TagCategory', verbose_name='Category'),
|
||||
),
|
||||
]
|
||||
|
|
@ -11,7 +11,7 @@ class Tag(TranslatedFieldsMixin, models.Model):
|
|||
label = TJSONField(blank=True, null=True, default=None,
|
||||
verbose_name=_('label'),
|
||||
help_text='{"en-GB":"some text"}')
|
||||
category = models.ForeignKey('TagCategory', on_delete=models.PROTECT,
|
||||
category = models.ForeignKey('TagCategory', on_delete=models.CASCADE,
|
||||
null=True, related_name='tags',
|
||||
verbose_name=_('Category'))
|
||||
# chosen tags should have non-null priority,other tags priority should be null
|
||||
|
|
@ -38,6 +38,10 @@ class TagCategoryQuerySet(models.QuerySet):
|
|||
"""Select related objects."""
|
||||
return self.prefetch_related('tags')
|
||||
|
||||
def with_extended_related(self):
|
||||
"""Select related objects."""
|
||||
return self.select_related('country')
|
||||
|
||||
def for_news(self):
|
||||
"""Select tag categories for news."""
|
||||
return self.filter(news_types__isnull=True)
|
||||
|
|
@ -47,6 +51,10 @@ class TagCategoryQuerySet(models.QuerySet):
|
|||
return self.filter(models.Q(establishment_types__isnull=False) |
|
||||
models.Q(establishment_subtypes__isnull=False))
|
||||
|
||||
def by_establishment_type(self, index_name):
|
||||
"""Filter by establishment type index name."""
|
||||
return self.filter(establishment_types__index_name=index_name)
|
||||
|
||||
def with_tags(self, switcher=True):
|
||||
"""Filter by existing tags."""
|
||||
return self.exclude(tags__isnull=switcher)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,11 @@
|
|||
"""Tag serializers."""
|
||||
from rest_framework import serializers
|
||||
from establishment.models import (Establishment, EstablishmentType,
|
||||
EstablishmentSubType)
|
||||
from news.models import News, NewsType
|
||||
from tag import models
|
||||
from utils.exceptions import (ObjectAlreadyAdded, BindingObjectNotFound,
|
||||
RemovedBindingObjectNotFound)
|
||||
from utils.serializers import TranslatedField
|
||||
|
||||
|
||||
|
|
@ -49,14 +54,114 @@ class TagCategoryBaseSerializer(serializers.ModelSerializer):
|
|||
|
||||
|
||||
class TagCategoryBackOfficeDetailSerializer(TagCategoryBaseSerializer):
|
||||
"""Tag Category detail serializer for back-office users."""
|
||||
|
||||
country_translated = TranslatedField(source='country.name_translated')
|
||||
|
||||
class Meta(TagBaseSerializer.Meta):
|
||||
class Meta(TagCategoryBaseSerializer.Meta):
|
||||
"""Meta class."""
|
||||
|
||||
fields = TagCategoryBaseSerializer.Meta.fields + (
|
||||
'news_types',
|
||||
'label',
|
||||
'country',
|
||||
'country_translated',
|
||||
)
|
||||
|
||||
|
||||
class TagBindObjectSerializer(serializers.Serializer):
|
||||
"""Serializer for binding tag category and objects"""
|
||||
|
||||
ESTABLISHMENT = 'establishment'
|
||||
NEWS = 'news'
|
||||
|
||||
TYPE_CHOICES = (
|
||||
(ESTABLISHMENT, 'Establishment type'),
|
||||
(NEWS, 'News type'),
|
||||
)
|
||||
|
||||
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')
|
||||
|
||||
tag = view.get_object()
|
||||
attrs['tag'] = tag
|
||||
|
||||
if obj_type == self.ESTABLISHMENT:
|
||||
establishment = Establishment.objects.filter(pk=obj_id).first()
|
||||
if not establishment:
|
||||
raise BindingObjectNotFound()
|
||||
if request.method == 'POST' and tag.establishments.filter(
|
||||
pk=establishment.pk).exists():
|
||||
raise ObjectAlreadyAdded()
|
||||
if request.method == 'DELETE' and not tag.establishments.filter(
|
||||
pk=establishment.pk).exists():
|
||||
raise RemovedBindingObjectNotFound()
|
||||
attrs['related_object'] = establishment
|
||||
elif obj_type == self.NEWS:
|
||||
news = News.objects.filter(pk=obj_id).first()
|
||||
if not news:
|
||||
raise BindingObjectNotFound()
|
||||
if request.method == 'POST' and tag.news.filter(pk=news.pk).exists():
|
||||
raise ObjectAlreadyAdded()
|
||||
if request.method == 'DELETE' and not tag.news.filter(
|
||||
pk=news.pk).exists():
|
||||
raise RemovedBindingObjectNotFound()
|
||||
attrs['related_object'] = news
|
||||
return attrs
|
||||
|
||||
|
||||
class TagCategoryBindObjectSerializer(serializers.Serializer):
|
||||
"""Serializer for binding tag category and objects"""
|
||||
|
||||
ESTABLISHMENT_TYPE = 'establishment_type'
|
||||
NEWS_TYPE = 'news_type'
|
||||
|
||||
TYPE_CHOICES = (
|
||||
(ESTABLISHMENT_TYPE, 'Establishment type'),
|
||||
(NEWS_TYPE, 'News type'),
|
||||
)
|
||||
|
||||
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')
|
||||
|
||||
tag_category = view.get_object()
|
||||
attrs['tag_category'] = tag_category
|
||||
|
||||
if obj_type == self.ESTABLISHMENT_TYPE:
|
||||
establishment_type = EstablishmentType.objects.filter(pk=obj_id).\
|
||||
first()
|
||||
if not establishment_type:
|
||||
raise BindingObjectNotFound()
|
||||
if request.method == 'POST' and tag_category.establishment_types.\
|
||||
filter(pk=establishment_type.pk).exists():
|
||||
raise ObjectAlreadyAdded()
|
||||
if request.method == 'DELETE' and not tag_category.\
|
||||
establishment_types.filter(pk=establishment_type.pk).\
|
||||
exists():
|
||||
raise RemovedBindingObjectNotFound()
|
||||
attrs['related_object'] = establishment_type
|
||||
elif obj_type == self.NEWS_TYPE:
|
||||
news_type = NewsType.objects.filter(pk=obj_id).first()
|
||||
if not news_type:
|
||||
raise BindingObjectNotFound()
|
||||
if request.method == 'POST' and tag_category.news_types.\
|
||||
filter(pk=news_type.pk).exists():
|
||||
raise ObjectAlreadyAdded()
|
||||
if request.method == 'DELETE' and not tag_category.news_types.\
|
||||
filter(pk=news_type.pk).exists():
|
||||
raise RemovedBindingObjectNotFound()
|
||||
attrs['related_object'] = news_type
|
||||
return attrs
|
||||
|
|
|
|||
|
|
@ -1,16 +1,11 @@
|
|||
"""Urlconf for app tag."""
|
||||
from django.urls import path
|
||||
from rest_framework.routers import SimpleRouter
|
||||
from tag import views
|
||||
|
||||
app_name = 'tag'
|
||||
|
||||
router = SimpleRouter()
|
||||
router.register(r'', views.TagViewSet)
|
||||
router.register(r'categories', views.TagCategoryBackOfficeViewSet)
|
||||
router.register(r'', views.TagBackOfficeViewSet)
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
path('category/', views.TagCategoryListCreateView.as_view(), name='category-list-create'),
|
||||
]
|
||||
|
||||
urlpatterns += router.urls
|
||||
urlpatterns = router.urls
|
||||
|
|
|
|||
|
|
@ -1,24 +1,111 @@
|
|||
"""Tag views."""
|
||||
from rest_framework import viewsets, mixins
|
||||
from rest_framework import viewsets, mixins, status
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.response import Response
|
||||
from tag import filters, models, serializers
|
||||
from rest_framework import permissions
|
||||
|
||||
|
||||
class TagViewSet(mixins.ListModelMixin, mixins.CreateModelMixin,
|
||||
mixins.UpdateModelMixin, mixins.DestroyModelMixin,
|
||||
viewsets.GenericViewSet):
|
||||
"""List/create tag view."""
|
||||
|
||||
pagination_class = None
|
||||
queryset = models.Tag.objects.all()
|
||||
serializer_class = serializers.TagBackOfficeSerializer
|
||||
|
||||
|
||||
# User`s views & viewsets
|
||||
class TagCategoryViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
|
||||
"""ViewSet for TagCategory model."""
|
||||
|
||||
filterset_class = filters.TagCategoryFilterSet
|
||||
pagination_class = None
|
||||
permission_classes = (permissions.AllowAny, )
|
||||
queryset = models.TagCategory.objects.with_tags().with_base_related()
|
||||
queryset = models.TagCategory.objects.with_tags().with_base_related().\
|
||||
distinct()
|
||||
serializer_class = serializers.TagCategoryBaseSerializer
|
||||
|
||||
|
||||
# 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)
|
||||
|
||||
|
||||
class TagBackOfficeViewSet(mixins.ListModelMixin, mixins.CreateModelMixin,
|
||||
mixins.UpdateModelMixin, mixins.DestroyModelMixin,
|
||||
BindObjectMixin, viewsets.GenericViewSet):
|
||||
"""List/create tag view."""
|
||||
|
||||
pagination_class = None
|
||||
permission_classes = (permissions.IsAuthenticated, )
|
||||
queryset = models.Tag.objects.all()
|
||||
serializer_class = serializers.TagBackOfficeSerializer
|
||||
bind_object_serializer_class = serializers.TagBindObjectSerializer
|
||||
|
||||
def perform_binding(self, serializer):
|
||||
data = serializer.validated_data
|
||||
tag = data.pop('tag')
|
||||
obj_type = data.get('type')
|
||||
related_object = data.get('related_object')
|
||||
if obj_type == self.bind_object_serializer_class.ESTABLISHMENT:
|
||||
tag.establishments.add(related_object)
|
||||
elif obj_type == self.bind_object_serializer_class.NEWS:
|
||||
tag.news.add(related_object)
|
||||
|
||||
def perform_unbinding(self, serializer):
|
||||
data = serializer.validated_data
|
||||
tag = data.pop('tag')
|
||||
obj_type = data.get('type')
|
||||
related_object = data.get('related_object')
|
||||
if obj_type == self.bind_object_serializer_class.ESTABLISHMENT:
|
||||
tag.establishments.remove(related_object)
|
||||
elif obj_type == self.bind_object_serializer_class.NEWS:
|
||||
tag.news.remove(related_object)
|
||||
|
||||
|
||||
class TagCategoryBackOfficeViewSet(mixins.CreateModelMixin,
|
||||
mixins.UpdateModelMixin,
|
||||
mixins.DestroyModelMixin,
|
||||
mixins.RetrieveModelMixin,
|
||||
BindObjectMixin,
|
||||
TagCategoryViewSet):
|
||||
"""ViewSet for TagCategory model for BackOffice users."""
|
||||
|
||||
permission_classes = (permissions.IsAuthenticated, )
|
||||
queryset = TagCategoryViewSet.queryset.with_extended_related()
|
||||
serializer_class = serializers.TagCategoryBackOfficeDetailSerializer
|
||||
bind_object_serializer_class = serializers.TagCategoryBindObjectSerializer
|
||||
|
||||
def perform_binding(self, serializer):
|
||||
data = serializer.validated_data
|
||||
tag_category = data.pop('tag_category')
|
||||
obj_type = data.get('type')
|
||||
related_object = data.get('related_object')
|
||||
if obj_type == self.bind_object_serializer_class.ESTABLISHMENT_TYPE:
|
||||
tag_category.establishment_types.add(related_object)
|
||||
elif obj_type == self.bind_object_serializer_class.NEWS_TYPE:
|
||||
tag_category.news_types.add(related_object)
|
||||
|
||||
def perform_unbinding(self, serializer):
|
||||
data = serializer.validated_data
|
||||
tag_category = data.pop('tag_category')
|
||||
obj_type = data.get('type')
|
||||
related_object = data.get('related_object')
|
||||
if obj_type == self.bind_object_serializer_class.ESTABLISHMENT_TYPE:
|
||||
tag_category.establishment_types.remove(related_object)
|
||||
elif obj_type == self.bind_object_serializer_class.NEWS_TYPE:
|
||||
tag_category.news_types.remove(related_object)
|
||||
|
|
|
|||
18
apps/translation/migrations/0004_auto_20191018_0832.py
Normal file
18
apps/translation/migrations/0004_auto_20191018_0832.py
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 2.2.4 on 2019-10-18 08:32
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('translation', '0003_auto_20190901_1032'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='language',
|
||||
name='locale',
|
||||
field=models.CharField(max_length=10, unique=True, verbose_name='Locale identifier'),
|
||||
),
|
||||
]
|
||||
|
|
@ -22,7 +22,7 @@ class Language(models.Model):
|
|||
|
||||
title = models.CharField(max_length=255,
|
||||
verbose_name=_('Language title'))
|
||||
locale = models.CharField(max_length=10,
|
||||
locale = models.CharField(max_length=10, unique=True,
|
||||
verbose_name=_('Locale identifier'))
|
||||
|
||||
objects = LanguageQuerySet.as_manager()
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
from django.utils.translation import gettext_lazy as _
|
||||
from rest_framework import exceptions, status
|
||||
from rest_framework import exceptions, serializers, status
|
||||
|
||||
|
||||
class ProjectBaseException(exceptions.APIException):
|
||||
|
|
@ -142,3 +142,24 @@ class PasswordResetRequestExistedError(exceptions.APIException):
|
|||
"""
|
||||
status_code = status.HTTP_400_BAD_REQUEST
|
||||
default_detail = _('Password reset request is already exists and valid.')
|
||||
|
||||
|
||||
class ObjectAlreadyAdded(serializers.ValidationError):
|
||||
"""
|
||||
The exception must be thrown if the object has already been added to the
|
||||
list.
|
||||
"""
|
||||
|
||||
default_detail = _('Object has already been added.')
|
||||
|
||||
|
||||
class BindingObjectNotFound(serializers.ValidationError):
|
||||
"""The exception must be thrown if the object not found."""
|
||||
|
||||
default_detail = _('Binding object not found.')
|
||||
|
||||
|
||||
class RemovedBindingObjectNotFound(serializers.ValidationError):
|
||||
"""The exception must be thrown if the object not found."""
|
||||
|
||||
default_detail = _('Removed binding object not found.')
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@ from django.urls import path, include
|
|||
app_name = 'back'
|
||||
|
||||
urlpatterns = [
|
||||
path('gallery/', include(('gallery.urls', 'gallery'), namespace='gallery')),
|
||||
path('establishments/', include('establishment.urls.back')),
|
||||
path('location/', include('location.urls.back')),
|
||||
path('news/', include('news.urls.back')),
|
||||
# path('tags/', include(('tag.urls.back', 'tag'), namespace='tag')),
|
||||
path('account/', include('account.urls.back')),
|
||||
path('comment/', include('comment.urls.back')),
|
||||
path('establishments/', include('establishment.urls.back')),
|
||||
path('gallery/', include(('gallery.urls', 'gallery'), namespace='gallery')),
|
||||
path('location/', include('location.urls.back')),
|
||||
path('news/', include('news.urls.back')),
|
||||
path('tags/', include(('tag.urls.back', 'tag'), namespace='tag')),
|
||||
]
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user