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'
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['email']
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
roles = models.ManyToManyField(
Role, verbose_name=_('Roles'), symmetrical=False,

View File

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

View File

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

View File

@ -21,7 +21,6 @@ class TagsBaseFilterSet(filters.FilterSet):
type = filters.MultipleChoiceFilter(choices=TYPE_CHOICES,
method='filter_by_type')
def filter_by_type(self, queryset, name, value):
if self.NEWS in value:
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."""
from django.contrib.contenttypes import fields as generic
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.utils.translation import gettext_lazy as _
@ -154,8 +155,9 @@ class TagCategory(models.Model):
value_type = models.CharField(_('value type'), max_length=255,
choices=VALUE_TYPE_CHOICES, default=LIST, )
old_id = models.IntegerField(blank=True, null=True)
translation = models.OneToOneField('translation.SiteInterfaceDictionary', on_delete=models.SET_NULL,
null=True, related_name='tag_category', verbose_name=_('Translation'))
translation = models.OneToOneField(
'translation.SiteInterfaceDictionary', on_delete=models.SET_NULL,
null=True, related_name='tag_category', verbose_name=_('Translation'))
@property
def label_indexing(self):
@ -175,3 +177,20 @@ class TagCategory(models.Model):
def __str__(self):
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()
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:
"""Meta class."""
@ -41,7 +37,6 @@ class TagBaseSerializer(serializers.ModelSerializer):
model = models.Tag
fields = (
'id',
'label_translated',
'index_name',
)
@ -49,13 +44,11 @@ class TagBaseSerializer(serializers.ModelSerializer):
class TagBackOfficeSerializer(TagBaseSerializer):
"""Serializer for Tag model for Back office users."""
label = serializers.DictField(source='translation.text')
class Meta(TagBaseSerializer.Meta):
"""Meta class."""
fields = TagBaseSerializer.Meta.fields + (
'label',
'value',
'category'
)
@ -191,7 +184,7 @@ class TagCategoryBackOfficeDetailSerializer(TagCategoryBaseSerializer):
class TagBindObjectSerializer(serializers.Serializer):
"""Serializer for binding tag category and objects"""
"""Serializer for binding tag category and objects."""
ESTABLISHMENT = 'establishment'
NEWS = 'news'
@ -216,15 +209,20 @@ class TagBindObjectSerializer(serializers.Serializer):
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:
@ -287,3 +285,64 @@ class TagCategoryBindObjectSerializer(serializers.Serializer):
raise RemovedBindingObjectNotFound()
attrs['related_object'] = news_type
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."""
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.decorators import action
from rest_framework.response import Response
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 product.models import ProductType
from search_indexes import views as search_views
from tag import filters, models, serializers
@ -292,6 +293,8 @@ class BindObjectMixin:
def get_serializer_class(self):
if self.action == 'bind_object':
return self.bind_object_serializer_class
elif self.action == 'chosen':
return self.chosen_serializer_class
return self.serializer_class
def perform_binding(self, serializer):
@ -311,6 +314,17 @@ class BindObjectMixin:
self.perform_unbinding(serializer)
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,
mixins.UpdateModelMixin, mixins.DestroyModelMixin,
@ -322,12 +336,27 @@ class TagBackOfficeViewSet(mixins.ListModelMixin, mixins.CreateModelMixin,
queryset = models.Tag.objects.all()
serializer_class = serializers.TagBackOfficeSerializer
bind_object_serializer_class = serializers.TagBindObjectSerializer
chosen_serializer_class = serializers.ChosenTagBindObjectSerializer
def perform_binding(self, serializer):
data = serializer.validated_data
tag = data.pop('tag')
obj_type = data.get('type')
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:
tag.establishments.add(related_object)
elif obj_type == self.bind_object_serializer_class.NEWS:
@ -338,6 +367,11 @@ class TagBackOfficeViewSet(mixins.ListModelMixin, mixins.CreateModelMixin,
tag = data.pop('tag')
obj_type = data.get('type')
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:
tag.establishments.remove(related_object)
elif obj_type == self.bind_object_serializer_class.NEWS: