Merge branch 'develop' into phone_list

This commit is contained in:
Dmitriy Kuzmenko 2020-01-14 15:59:46 +03:00
commit 9aeedff81e
13 changed files with 193 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

@ -8,6 +8,8 @@ from comment.serializers import common as comment_serializers
from establishment import models
from location.serializers import AddressBaseSerializer, CitySerializer, AddressDetailSerializer, \
CityShortSerializer
from location.serializers import EstablishmentWineRegionBaseSerializer, \
EstablishmentWineOriginBaseSerializer
from main.serializers import AwardSerializer, CurrencySerializer
from review.serializers import ReviewShortSerializer
from tag.serializers import TagBaseSerializer
@ -16,8 +18,6 @@ from utils import exceptions as utils_exceptions
from utils.serializers import ImageBaseSerializer, CarouselCreateSerializer
from utils.serializers import (ProjectModelSerializer, TranslatedField,
FavoritesCreateSerializer)
from location.serializers import EstablishmentWineRegionBaseSerializer, \
EstablishmentWineOriginBaseSerializer
class ContactPhonesSerializer(serializers.ModelSerializer):

View File

@ -6,7 +6,7 @@ from django.contrib.contenttypes import fields as generic
from django.contrib.contenttypes.models import ContentType
from django.contrib.postgres.fields import JSONField
from django.core.validators import EMPTY_VALUES
from django.db import connections, connection
from django.db import connections
from django.db import models
from django.db.models import Q
from django.utils.translation import gettext_lazy as _
@ -16,6 +16,7 @@ from configuration.models import TranslationSettings
from location.models import Country
from main import methods
from review.models import Review
from tag.models import Tag
from utils.exceptions import UnprocessableEntityError
from utils.methods import dictfetchall
from utils.models import (ProjectBaseMixin, TJSONField, URLImageMixin,
@ -117,6 +118,8 @@ class Feature(ProjectBaseMixin, PlatformMixin):
site_settings = models.ManyToManyField(SiteSettings, through='SiteFeature')
old_id = models.IntegerField(null=True, blank=True)
chosen_tags = generic.GenericRelation(to='tag.ChosenTag')
class Meta:
"""Meta class."""
verbose_name = _('Feature')
@ -125,6 +128,10 @@ class Feature(ProjectBaseMixin, PlatformMixin):
def __str__(self):
return f'{self.slug}'
@property
def get_chosen_tags(self):
return Tag.objects.filter(chosen_tags__in=self.chosen_tags.all()).distinct()
class SiteFeatureQuerySet(models.QuerySet):
"""Extended queryset for SiteFeature model."""

View File

@ -2,11 +2,12 @@
from django.contrib.contenttypes.models import ContentType
from rest_framework import serializers
from account.models import User
from account.serializers.back import BackUserSerializer
from location.serializers import CountrySerializer
from main import models
from tag.serializers import TagBackOfficeSerializer
from utils.serializers import ProjectModelSerializer, TranslatedField, RecursiveFieldSerializer
from account.serializers.back import BackUserSerializer
from account.models import User
class FeatureSerializer(serializers.ModelSerializer):
@ -90,6 +91,8 @@ class SiteFeatureSerializer(serializers.ModelSerializer):
route = serializers.CharField(source='feature.route.name')
source = serializers.IntegerField(source='feature.source')
nested = RecursiveFieldSerializer(many=True, allow_null=True)
chosen_tags = TagBackOfficeSerializer(
source='feature.get_chosen_tags', many=True, read_only=True)
class Meta:
"""Meta class."""
@ -101,6 +104,7 @@ class SiteFeatureSerializer(serializers.ModelSerializer):
'route',
'source',
'nested',
'chosen_tags',
)

View File

@ -85,6 +85,14 @@ class EstablishmentDocument(Document):
'value': fields.KeywordField(),
},
multi=True)
distillery_types = fields.ObjectField(
properties={
'id': fields.IntegerField(attr='id'),
'label': fields.ObjectField(attr='label_indexing',
properties=OBJECT_FIELD_PROPERTIES),
'value': fields.KeywordField(),
},
multi=True)
products = fields.ObjectField(
properties={
'wine_origins': fields.ListField(

View File

@ -277,6 +277,7 @@ class EstablishmentDocumentSerializer(InFavoritesMixin, DocumentSerializer):
tags = TagsDocumentSerializer(many=True, source='visible_tags')
restaurant_category = TagsDocumentSerializer(many=True, allow_null=True)
restaurant_cuisine = TagsDocumentSerializer(many=True, allow_null=True)
distillery_types = TagsDocumentSerializer(many=True, allow_null=True)
artisan_category = TagsDocumentSerializer(many=True, allow_null=True)
schedule = ScheduleDocumentSerializer(many=True, allow_null=True)
wine_origins = WineOriginSerializer(many=True)
@ -310,6 +311,7 @@ class EstablishmentDocumentSerializer(InFavoritesMixin, DocumentSerializer):
# 'collections',
'type',
'subtypes',
'distillery_types',
)

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,19 @@
# Generated by Django 2.2.7 on 2020-01-13 13:57
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('tag', '0017_auto_20191220_1623'),
]
operations = [
migrations.AlterField(
model_name='tagcategory',
name='country',
field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, to='location.Country'),
),
]

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

@ -0,0 +1,14 @@
# Generated by Django 2.2.7 on 2020-01-14 07:56
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('tag', '0018_auto_20200113_1357'),
('tag', '0018_chosentag'),
]
operations = [
]

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 _
@ -145,8 +146,8 @@ class TagCategory(models.Model):
(BOOLEAN, _('boolean')),
)
country = models.ForeignKey('location.Country',
on_delete=models.SET_NULL, null=True,
default=None)
on_delete=models.SET_NULL,
blank=True, null=True, default=None)
public = models.BooleanField(default=False)
index_name = models.CharField(max_length=255, blank=True, null=True,
verbose_name=_('indexing name'), unique=True)
@ -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

@ -9,6 +9,7 @@ from tag import models
from utils.exceptions import BindingObjectNotFound, ObjectAlreadyAdded, RemovedBindingObjectNotFound
from utils.serializers import TranslatedField
from utils.models import get_default_locale, get_language, to_locale
from main.models import Feature
def translate_obj(obj):
@ -56,7 +57,8 @@ class TagBackOfficeSerializer(TagBaseSerializer):
fields = TagBaseSerializer.Meta.fields + (
'label',
'category'
'category',
'value',
)
@ -191,7 +193,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 +218,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 +294,41 @@ 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"""
feature_id = serializers.IntegerField()
def validate(self, attrs):
view = self.context.get('view')
request = self.context.get('request')
obj_id = attrs.get('feature_id')
tag = view.get_object()
attrs['tag'] = tag
feature = Feature.objects.filter(pk=obj_id). \
first()
if not feature:
raise BindingObjectNotFound()
if request.method == 'DELETE' and not feature. \
chosen_tags.filter(tag=tag). \
exists():
raise RemovedBindingObjectNotFound()
attrs['related_object'] = feature
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: