add chosen tag

This commit is contained in:
Dmitriy Kuzmenko 2020-01-14 10:53:43 +03:00
parent dabc63647d
commit fb00c7c3a3
8 changed files with 156 additions and 16 deletions

View File

@ -147,8 +147,8 @@ class User(AbstractUser):
) )
EMAIL_FIELD = 'email' EMAIL_FIELD = 'email'
USERNAME_FIELD = 'username' USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['email'] REQUIRED_FIELDS = ['username']
roles = models.ManyToManyField( roles = models.ManyToManyField(
Role, verbose_name=_('Roles'), symmetrical=False, Role, verbose_name=_('Roles'), symmetrical=False,

View File

@ -58,6 +58,8 @@ class EstablishmentType(TypeDefaultImageMixin, TranslatedFieldsMixin, ProjectBas
blank=True, null=True, default=None, blank=True, null=True, default=None,
verbose_name='default image') verbose_name='default image')
chosen_tags = generic.GenericRelation(to='tag.ChosenTag')
class Meta: class Meta:
"""Meta class.""" """Meta class."""

View File

@ -54,6 +54,7 @@ class NewsType(models.Model):
name = models.CharField(_('name'), max_length=250) name = models.CharField(_('name'), max_length=250)
tag_categories = models.ManyToManyField('tag.TagCategory', tag_categories = models.ManyToManyField('tag.TagCategory',
related_name='news_types') related_name='news_types')
chosen_tags = generic.GenericRelation(to='tag.ChosenTag')
class Meta: class Meta:
"""Meta class.""" """Meta class."""

View File

@ -21,7 +21,6 @@ class TagsBaseFilterSet(filters.FilterSet):
type = filters.MultipleChoiceFilter(choices=TYPE_CHOICES, type = filters.MultipleChoiceFilter(choices=TYPE_CHOICES,
method='filter_by_type') method='filter_by_type')
def filter_by_type(self, queryset, name, value): def filter_by_type(self, queryset, name, value):
if self.NEWS in value: if self.NEWS in value:
queryset = queryset.for_news() queryset = queryset.for_news()

View File

@ -0,0 +1,26 @@
# Generated by Django 2.2.7 on 2020-01-13 08:36
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('main', '0045_carousel_is_international'),
('contenttypes', '0002_remove_content_type_name'),
('tag', '0017_auto_20191220_1623'),
]
operations = [
migrations.CreateModel(
name='ChosenTag',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('object_id', models.PositiveIntegerField()),
('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType')),
('site', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='main.SiteSettings', verbose_name='site')),
('tag', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='chosen_tags', to='tag.Tag', verbose_name='tag')),
],
),
]

View File

@ -1,4 +1,5 @@
"""Tag app models.""" """Tag app models."""
from django.contrib.contenttypes import fields as generic
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.db import models from django.db import models
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
@ -154,7 +155,8 @@ class TagCategory(models.Model):
value_type = models.CharField(_('value type'), max_length=255, value_type = models.CharField(_('value type'), max_length=255,
choices=VALUE_TYPE_CHOICES, default=LIST, ) choices=VALUE_TYPE_CHOICES, default=LIST, )
old_id = models.IntegerField(blank=True, null=True) old_id = models.IntegerField(blank=True, null=True)
translation = models.OneToOneField('translation.SiteInterfaceDictionary', on_delete=models.SET_NULL, translation = models.OneToOneField(
'translation.SiteInterfaceDictionary', on_delete=models.SET_NULL,
null=True, related_name='tag_category', verbose_name=_('Translation')) null=True, related_name='tag_category', verbose_name=_('Translation'))
@property @property
@ -175,3 +177,20 @@ class TagCategory(models.Model):
def __str__(self): def __str__(self):
return self.index_name return self.index_name
class ChosenTag(models.Model):
"""Chosen tag for type."""
tag = models.ForeignKey(
'Tag', verbose_name=_('tag'), related_name='chosen_tags',
on_delete=models.CASCADE)
site = models.ForeignKey(
'main.SiteSettings', verbose_name=_('site'), on_delete=models.CASCADE)
content_type = models.ForeignKey(
generic.ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey('content_type', 'object_id')
def __str__(self):
return f'chosen_tag:{self.tag}'

View File

@ -30,10 +30,6 @@ class TagBaseSerializer(serializers.ModelSerializer):
return super().get_extra_kwargs() return super().get_extra_kwargs()
index_name = serializers.CharField(source='value', read_only=True, allow_null=True) index_name = serializers.CharField(source='value', read_only=True, allow_null=True)
label_translated = serializers.SerializerMethodField(read_only=True, allow_null=True)
def get_label_translated(self, obj):
return translate_obj(obj)
class Meta: class Meta:
"""Meta class.""" """Meta class."""
@ -41,7 +37,6 @@ class TagBaseSerializer(serializers.ModelSerializer):
model = models.Tag model = models.Tag
fields = ( fields = (
'id', 'id',
'label_translated',
'index_name', 'index_name',
) )
@ -49,13 +44,11 @@ class TagBaseSerializer(serializers.ModelSerializer):
class TagBackOfficeSerializer(TagBaseSerializer): class TagBackOfficeSerializer(TagBaseSerializer):
"""Serializer for Tag model for Back office users.""" """Serializer for Tag model for Back office users."""
label = serializers.DictField(source='translation.text')
class Meta(TagBaseSerializer.Meta): class Meta(TagBaseSerializer.Meta):
"""Meta class.""" """Meta class."""
fields = TagBaseSerializer.Meta.fields + ( fields = TagBaseSerializer.Meta.fields + (
'label', 'value',
'category' 'category'
) )
@ -191,7 +184,7 @@ class TagCategoryBackOfficeDetailSerializer(TagCategoryBaseSerializer):
class TagBindObjectSerializer(serializers.Serializer): class TagBindObjectSerializer(serializers.Serializer):
"""Serializer for binding tag category and objects""" """Serializer for binding tag category and objects."""
ESTABLISHMENT = 'establishment' ESTABLISHMENT = 'establishment'
NEWS = 'news' NEWS = 'news'
@ -216,15 +209,20 @@ class TagBindObjectSerializer(serializers.Serializer):
if obj_type == self.ESTABLISHMENT: if obj_type == self.ESTABLISHMENT:
establishment = Establishment.objects.filter(pk=obj_id).first() establishment = Establishment.objects.filter(pk=obj_id).first()
if not establishment: if not establishment:
raise BindingObjectNotFound() raise BindingObjectNotFound()
if request.method == 'POST' and tag.establishments.filter( if request.method == 'POST' and tag.establishments.filter(
pk=establishment.pk).exists(): pk=establishment.pk).exists():
raise ObjectAlreadyAdded() raise ObjectAlreadyAdded()
if request.method == 'DELETE' and not tag.establishments.filter( if request.method == 'DELETE' and not tag.establishments.filter(
pk=establishment.pk).exists(): pk=establishment.pk).exists():
raise RemovedBindingObjectNotFound() raise RemovedBindingObjectNotFound()
attrs['related_object'] = establishment attrs['related_object'] = establishment
elif obj_type == self.NEWS: elif obj_type == self.NEWS:
news = News.objects.filter(pk=obj_id).first() news = News.objects.filter(pk=obj_id).first()
if not news: if not news:
@ -287,3 +285,64 @@ class TagCategoryBindObjectSerializer(serializers.Serializer):
raise RemovedBindingObjectNotFound() raise RemovedBindingObjectNotFound()
attrs['related_object'] = news_type attrs['related_object'] = news_type
return attrs return attrs
class ChosenTagSerializer(serializers.ModelSerializer):
tag = TagBackOfficeSerializer(read_only=True)
class Meta:
model = models.ChosenTag
fields = [
'id',
'tag',
]
class ChosenTagBindObjectSerializer(serializers.Serializer):
"""Serializer for binding chosen tag 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 = view.get_object()
attrs['tag'] = tag
if obj_type == self.ESTABLISHMENT_TYPE:
establishment_type = EstablishmentType.objects.filter(pk=obj_id). \
first()
if not establishment_type:
raise BindingObjectNotFound()
if request.method == 'DELETE' and not establishment_type. \
chosen_tags.filter(tag=tag). \
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 news_type.chosen_tags. \
filter(tag=tag).exists():
raise ObjectAlreadyAdded()
if request.method == 'DELETE' and not news_type.chosen_tags. \
filter(tag=tag).exists():
raise RemovedBindingObjectNotFound()
attrs['related_object'] = news_type
return attrs

View File

@ -1,14 +1,15 @@
"""Tag views.""" """Tag views."""
from django.conf import settings from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.utils.translation import gettext_lazy as _
from rest_framework import generics, mixins, permissions, status, viewsets from rest_framework import generics, mixins, permissions, status, viewsets
from rest_framework.decorators import action from rest_framework.decorators import action
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.serializers import ValidationError from rest_framework.serializers import ValidationError
from django.utils.translation import gettext_lazy as _
from search_indexes import views as search_views
from location.models import WineRegion from location.models import WineRegion
from product.models import ProductType from product.models import ProductType
from search_indexes import views as search_views
from tag import filters, models, serializers from tag import filters, models, serializers
@ -292,6 +293,8 @@ class BindObjectMixin:
def get_serializer_class(self): def get_serializer_class(self):
if self.action == 'bind_object': if self.action == 'bind_object':
return self.bind_object_serializer_class return self.bind_object_serializer_class
elif self.action == 'chosen':
return self.chosen_serializer_class
return self.serializer_class return self.serializer_class
def perform_binding(self, serializer): def perform_binding(self, serializer):
@ -311,6 +314,17 @@ class BindObjectMixin:
self.perform_unbinding(serializer) self.perform_unbinding(serializer)
return Response(status=status.HTTP_204_NO_CONTENT) return Response(status=status.HTTP_204_NO_CONTENT)
@action(methods=['post', 'delete'], detail=True, url_path='chosen')
def chosen(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, class TagBackOfficeViewSet(mixins.ListModelMixin, mixins.CreateModelMixin,
mixins.UpdateModelMixin, mixins.DestroyModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin,
@ -322,12 +336,27 @@ class TagBackOfficeViewSet(mixins.ListModelMixin, mixins.CreateModelMixin,
queryset = models.Tag.objects.all() queryset = models.Tag.objects.all()
serializer_class = serializers.TagBackOfficeSerializer serializer_class = serializers.TagBackOfficeSerializer
bind_object_serializer_class = serializers.TagBindObjectSerializer bind_object_serializer_class = serializers.TagBindObjectSerializer
chosen_serializer_class = serializers.ChosenTagBindObjectSerializer
def perform_binding(self, serializer): def perform_binding(self, serializer):
data = serializer.validated_data data = serializer.validated_data
tag = data.pop('tag') tag = data.pop('tag')
obj_type = data.get('type') obj_type = data.get('type')
related_object = data.get('related_object') related_object = data.get('related_object')
# for compatible exist code
if self.action == 'chosen':
obj_type = ContentType.objects.get_for_model(models.ChosenTag)
models.ChosenTag.objects.update_or_create(
tag=tag,
content_type=obj_type,
object_id=related_object.id,
defaults={
"content_object": related_object,
"site": self.request.user.last_country
},
)
if obj_type == self.bind_object_serializer_class.ESTABLISHMENT: if obj_type == self.bind_object_serializer_class.ESTABLISHMENT:
tag.establishments.add(related_object) tag.establishments.add(related_object)
elif obj_type == self.bind_object_serializer_class.NEWS: elif obj_type == self.bind_object_serializer_class.NEWS:
@ -338,6 +367,11 @@ class TagBackOfficeViewSet(mixins.ListModelMixin, mixins.CreateModelMixin,
tag = data.pop('tag') tag = data.pop('tag')
obj_type = data.get('type') obj_type = data.get('type')
related_object = data.get('related_object') related_object = data.get('related_object')
# for compatible exist code
if self.action == 'chosen':
related_object.chosen_tags.filter(tag=tag).delete()
if obj_type == self.bind_object_serializer_class.ESTABLISHMENT: if obj_type == self.bind_object_serializer_class.ESTABLISHMENT:
tag.establishments.remove(related_object) tag.establishments.remove(related_object)
elif obj_type == self.bind_object_serializer_class.NEWS: elif obj_type == self.bind_object_serializer_class.NEWS: