Merge branch 'develop' into feature/guides

This commit is contained in:
Dmitriy Kuzmenko 2019-12-27 18:00:20 +03:00
commit e2d71fca8f
18 changed files with 194 additions and 93 deletions

1
.gitignore vendored
View File

@ -28,3 +28,4 @@ celerybeat.pid
/gm_viktor.dump /gm_viktor.dump
/docker-compose.dump.yml /docker-compose.dump.yml
/gm_production_20191029.sql /gm_production_20191029.sql
/apps/transfer/log

View File

@ -65,7 +65,7 @@ class CollectionEstablishmentListView(CollectionListView):
# May raise a permission denied # May raise a permission denied
self.check_object_permissions(self.request, collection) self.check_object_permissions(self.request, collection)
return collection.establishments.all().annotate_in_favorites(self.request.user) return collection.establishments.published().annotate_in_favorites(self.request.user)
# Guide # Guide

View File

@ -13,6 +13,8 @@ class EstablishmentFilter(filters.FilterSet):
search = filters.CharFilter(method='search_text') search = filters.CharFilter(method='search_text')
type = filters.CharFilter(method='by_type') type = filters.CharFilter(method='by_type')
subtype = filters.CharFilter(method='by_subtype') subtype = filters.CharFilter(method='by_subtype')
city_id = filters.CharFilter(field_name='address__city__id')
city_name = filters.CharFilter(field_name='address__city__name')
class Meta: class Meta:
"""Meta class.""" """Meta class."""
@ -24,6 +26,8 @@ class EstablishmentFilter(filters.FilterSet):
'search', 'search',
'type', 'type',
'subtype', 'subtype',
'city_id',
'city_name',
) )
def search_text(self, queryset, name, value): def search_text(self, queryset, name, value):

View File

@ -132,7 +132,8 @@ class EstablishmentQuerySet(models.QuerySet):
def with_extended_address_related(self): def with_extended_address_related(self):
"""Return qs with deeply related address models.""" """Return qs with deeply related address models."""
return self.select_related('address__city', 'address__city__region', 'address__city__region__country', return self.select_related('address__city', 'address__city__region',
'address__city__region__country',
'address__city__country') 'address__city__country')
def with_extended_related(self): def with_extended_related(self):
@ -147,7 +148,8 @@ class EstablishmentQuerySet(models.QuerySet):
def with_es_related(self): def with_es_related(self):
"""Return qs with related for ES indexing objects.""" """Return qs with related for ES indexing objects."""
return self.select_related('address', 'establishment_type', 'address__city', 'address__city__country'). \ return self.select_related('address', 'establishment_type', 'address__city',
'address__city__country'). \
prefetch_related('tags', 'schedule') prefetch_related('tags', 'schedule')
def search(self, value, locale=None): def search(self, value, locale=None):
@ -246,7 +248,8 @@ class EstablishmentQuerySet(models.QuerySet):
'address__city__country': establishment.address.city.country 'address__city__country': establishment.address.city.country
} }
if establishment.establishment_subtypes.exists(): if establishment.establishment_subtypes.exists():
filters.update({'establishment_subtypes__in': establishment.establishment_subtypes.all()}) filters.update(
{'establishment_subtypes__in': establishment.establishment_subtypes.all()})
return self.exclude(id=establishment.id) \ return self.exclude(id=establishment.id) \
.filter(**filters) \ .filter(**filters) \
@ -495,10 +498,6 @@ class Establishment(GalleryMixin, ProjectBaseMixin, URLImageMixin,
schedule = models.ManyToManyField(to='timetable.Timetable', blank=True, schedule = models.ManyToManyField(to='timetable.Timetable', blank=True,
verbose_name=_('Establishment schedule'), verbose_name=_('Establishment schedule'),
related_name='schedule') related_name='schedule')
# holidays_from = models.DateTimeField(verbose_name=_('Holidays from'),
# help_text=_('Holidays closing date from'))
# holidays_to = models.DateTimeField(verbose_name=_('Holidays to'),
# help_text=_('Holidays closing date to'))
transportation = models.TextField(blank=True, null=True, default=None, transportation = models.TextField(blank=True, null=True, default=None,
verbose_name=_('Transportation')) verbose_name=_('Transportation'))
collections = models.ManyToManyField(to='collection.Collection', collections = models.ManyToManyField(to='collection.Collection',
@ -592,13 +591,6 @@ class Establishment(GalleryMixin, ProjectBaseMixin, URLImageMixin,
country = self.address.city.country country = self.address.city.country
return country.low_price, country.high_price return country.low_price, country.high_price
# todo: make via prefetch
# @property
# def subtypes(self):
# return EstablishmentSubType.objects.filter(
# subtype_establishment=self,
# establishment_type=self.establishment_type,
# establishment_type__use_subtypes=True)
def set_establishment_type(self, establishment_type): def set_establishment_type(self, establishment_type):
self.establishment_type = establishment_type self.establishment_type = establishment_type
@ -925,6 +917,12 @@ class EmployeeQuerySet(models.QuerySet):
"""Search by name or last_name.""" """Search by name or last_name."""
return self._generic_search(value, ['name', 'last_name']) return self._generic_search(value, ['name', 'last_name'])
def actual_establishment(self):
e = EstablishmentEmployee.objects.actual().filter(employee=self)
return self.prefetch_related(models.Prefetch('establishmentemployee_set',
queryset=EstablishmentEmployee.objects.actual()
)).all().distinct()
class Employee(BaseAttributes): class Employee(BaseAttributes):
"""Employee model.""" """Employee model."""
@ -933,7 +931,8 @@ class Employee(BaseAttributes):
null=True, blank=True, default=None, null=True, blank=True, default=None,
verbose_name=_('User')) verbose_name=_('User'))
name = models.CharField(max_length=255, verbose_name=_('Name')) name = models.CharField(max_length=255, verbose_name=_('Name'))
last_name = models.CharField(max_length=255, verbose_name=_('Last Name'), null=True, default=None) last_name = models.CharField(max_length=255, verbose_name=_('Last Name'), null=True,
default=None)
# SEX CHOICES # SEX CHOICES
MALE = 0 MALE = 0
@ -943,11 +942,14 @@ class Employee(BaseAttributes):
(MALE, _('Male')), (MALE, _('Male')),
(FEMALE, _('Female')) (FEMALE, _('Female'))
) )
sex = models.PositiveSmallIntegerField(choices=SEX_CHOICES, verbose_name=_('Sex'), null=True, default=None) sex = models.PositiveSmallIntegerField(choices=SEX_CHOICES, verbose_name=_('Sex'), null=True,
birth_date = models.DateTimeField(editable=True, verbose_name=_('Birth date'), null=True, default=None) default=None)
birth_date = models.DateTimeField(editable=True, verbose_name=_('Birth date'), null=True,
default=None)
email = models.EmailField(blank=True, null=True, default=None, verbose_name=_('Email')) email = models.EmailField(blank=True, null=True, default=None, verbose_name=_('Email'))
phone = PhoneNumberField(null=True, default=None) phone = PhoneNumberField(null=True, default=None)
toque_number = models.PositiveSmallIntegerField(verbose_name=_('Toque number'), null=True, default=None) toque_number = models.PositiveSmallIntegerField(verbose_name=_('Toque number'), null=True,
default=None)
establishments = models.ManyToManyField(Establishment, related_name='employees', establishments = models.ManyToManyField(Establishment, related_name='employees',
through=EstablishmentEmployee, ) through=EstablishmentEmployee, )
@ -998,25 +1000,6 @@ class ContactEmail(models.Model):
return f'{self.email}' return f'{self.email}'
#
# class Wine(TranslatedFieldsMixin, models.Model):
# """Wine model."""
# establishment = models.ForeignKey(
# 'establishment.Establishment', verbose_name=_('establishment'),
# on_delete=models.CASCADE)
# bottles = models.IntegerField(_('bottles'))
# price_min = models.DecimalField(
# _('price min'), max_digits=14, decimal_places=2)
# price_max = models.DecimalField(
# _('price max'), max_digits=14, decimal_places=2)
# by_glass = models.BooleanField(_('by glass'))
# price_glass_min = models.DecimalField(
# _('price min'), max_digits=14, decimal_places=2)
# price_glass_max = models.DecimalField(
# _('price max'), max_digits=14, decimal_places=2)
#
class Plate(TranslatedFieldsMixin, models.Model): class Plate(TranslatedFieldsMixin, models.Model):
"""Plate model.""" """Plate model."""
STR_FIELD_NAME = 'name' STR_FIELD_NAME = 'name'

View File

@ -157,28 +157,6 @@ class ContactEmailBackSerializers(model_serializers.PlateSerializer):
] ]
# TODO: test decorator
@with_base_attributes
class EmployeeBackSerializers(serializers.ModelSerializer):
"""Employee serializers."""
awards = AwardSerializer(many=True, read_only=True)
class Meta:
model = models.Employee
fields = [
'id',
'user',
'name',
'last_name',
'sex',
'birth_date',
'email',
'phone',
'toque_number',
'awards',
]
class PositionBackSerializer(serializers.ModelSerializer): class PositionBackSerializer(serializers.ModelSerializer):
"""Position Back serializer.""" """Position Back serializer."""
@ -194,6 +172,64 @@ class PositionBackSerializer(serializers.ModelSerializer):
'index_name', 'index_name',
] ]
# TODO: test decorator
@with_base_attributes
class EmployeeBackSerializers(serializers.ModelSerializer):
"""Employee serializers."""
public_mark = serializers.SerializerMethodField()
positions = serializers.SerializerMethodField()
establishment = serializers.SerializerMethodField()
awards = AwardSerializer(many=True, read_only=True)
def get_public_mark(self, obj):
"""Get last list actual public_mark"""
qs = obj.establishmentemployee_set.actual().order_by('-from_date')\
.values('establishment__public_mark').first()
return qs['establishment__public_mark']
def get_positions(self, obj):
"""Get last list actual positions"""
est_id = obj.establishmentemployee_set.actual().\
order_by('-from_date').first().establishment_id
qs = obj.establishmentemployee_set.actual()\
.filter(establishment_id=est_id)\
.prefetch_related('position').values('position')
positions = models.Position.objects.filter(id__in=[q['position'] for q in qs])
return [PositionBackSerializer(p).data for p in positions]
def get_establishment(self, obj):
"""Get last actual establishment"""
est = obj.establishmentemployee_set.actual().order_by('-from_date')\
.first().establishment
return {
"id": est.id,
"slug": est.slug
}
class Meta:
model = models.Employee
fields = [
'id',
'name',
'last_name',
'user',
'public_mark',
'positions',
'awards',
'establishment',
'sex',
'birth_date',
'email',
'phone',
'toque_number'
]
class EstablishmentEmployeeBackSerializer(serializers.ModelSerializer): class EstablishmentEmployeeBackSerializer(serializers.ModelSerializer):
"""Establishment Employee serializer.""" """Establishment Employee serializer."""

View File

@ -99,6 +99,9 @@ class EstablishmentTypeBaseSerializer(serializers.ModelSerializer):
name_translated = TranslatedField() name_translated = TranslatedField()
default_image_url = serializers.ImageField(source='default_image.image', default_image_url = serializers.ImageField(source='default_image.image',
allow_null=True) allow_null=True)
preview_image = serializers.URLField(source='preview_image_url',
allow_null=True,
read_only=True)
class Meta: class Meta:
"""Meta class.""" """Meta class."""
@ -110,6 +113,7 @@ class EstablishmentTypeBaseSerializer(serializers.ModelSerializer):
'use_subtypes', 'use_subtypes',
'index_name', 'index_name',
'default_image_url', 'default_image_url',
'preview_image',
] ]
extra_kwargs = { extra_kwargs = {
'name': {'write_only': True}, 'name': {'write_only': True},
@ -135,6 +139,9 @@ class EstablishmentSubTypeBaseSerializer(serializers.ModelSerializer):
name_translated = TranslatedField() name_translated = TranslatedField()
default_image_url = serializers.ImageField(source='default_image.image', default_image_url = serializers.ImageField(source='default_image.image',
allow_null=True) allow_null=True)
preview_image = serializers.URLField(source='preview_image_url',
allow_null=True,
read_only=True)
class Meta: class Meta:
"""Meta class.""" """Meta class."""
@ -146,6 +153,7 @@ class EstablishmentSubTypeBaseSerializer(serializers.ModelSerializer):
'establishment_type', 'establishment_type',
'index_name', 'index_name',
'default_image_url', 'default_image_url',
'preview_image',
] ]
extra_kwargs = { extra_kwargs = {
'name': {'write_only': True}, 'name': {'write_only': True},

View File

@ -7,6 +7,7 @@ from utils.permissions import IsCountryAdmin
from utils.views import CreateDestroyGalleryViewMixin from utils.views import CreateDestroyGalleryViewMixin
from rest_framework.permissions import IsAuthenticatedOrReadOnly from rest_framework.permissions import IsAuthenticatedOrReadOnly
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from django.db import IntegrityError
from utils.serializers import ImageBaseSerializer from utils.serializers import ImageBaseSerializer
from location.filters import RegionFilter from location.filters import RegionFilter
@ -81,6 +82,15 @@ class CityGalleryCreateDestroyView(common.CityViewMixin,
return gallery return gallery
def create(self, request, *args, **kwargs):
try:
return super(CityGalleryCreateDestroyView, self).create(request, *args, **kwargs)
except IntegrityError as e:
if not 'unique constraint' in e.args[0]:
raise e
models.CityGallery.objects.filter(city=kwargs['pk'], is_main=request.data['is_main']).delete()
return super(CityGalleryCreateDestroyView, self).create(request, *args, **kwargs)
class CityGalleryListView(common.CityViewMixin, class CityGalleryListView(common.CityViewMixin,
generics.ListAPIView): generics.ListAPIView):

View File

@ -121,6 +121,7 @@ class NewsListSerializer(NewsBaseSerializer):
fields = NewsBaseSerializer.Meta.fields + ( fields = NewsBaseSerializer.Meta.fields + (
'image', 'image',
'in_favorites',
) )
@ -210,16 +211,23 @@ class NewsBackOfficeBaseSerializer(NewsBaseSerializer):
"""Overridden validate method.""" """Overridden validate method."""
if 'descriptions' in attrs: if 'descriptions' in attrs:
descriptions = attrs.pop('descriptions') descriptions = attrs.pop('descriptions')
locales = list(map(lambda x: x['locale'], descriptions))
status_to_bool = { status_to_bool = {
'active': True, 'active': True,
'inactive': False, 'inactive': False,
} }
attrs['slugs'] = {obj['locale']: obj['slug'] for obj in descriptions} attrs['slugs'] = {obj['locale']: obj['slug'] for obj in descriptions if 'slug' in obj}
attrs['title'] = {obj['locale']: obj['title'] for obj in descriptions} attrs['title'] = {obj['locale']: obj['title'] for obj in descriptions if 'title' in obj}
attrs['locale_to_description_is_active'] = { attrs['locale_to_description_is_active'] = {
obj['locale']: status_to_bool[obj['status']] for obj in descriptions obj['locale']: status_to_bool[obj['status']] for obj in descriptions
} }
attrs['description'] = {obj['locale']: obj['text'] for obj in descriptions} attrs['description'] = {obj['locale']: obj['text'] for obj in descriptions if 'text' in obj}
if self.context['request'].method == 'PATCH':
instance = models.News.objects.get(pk=self.context['request'].data['id'])
for key in ['slugs', 'title', 'locale_to_description_is_active', 'description']:
for locale in locales:
if not attrs[key].get(locale):
attrs[key][locale] = getattr(instance, key).get(locale)
return attrs return attrs
def create(self, validated_data): def create(self, validated_data):

View File

@ -36,6 +36,9 @@ class ProductSubTypeBaseSerializer(serializers.ModelSerializer):
read_only=True) read_only=True)
default_image_url = serializers.ImageField(source='default_image.image', default_image_url = serializers.ImageField(source='default_image.image',
allow_null=True) allow_null=True)
preview_image = serializers.URLField(source='preview_image_url',
allow_null=True,
read_only=True)
class Meta: class Meta:
model = models.ProductSubType model = models.ProductSubType
@ -44,6 +47,7 @@ class ProductSubTypeBaseSerializer(serializers.ModelSerializer):
'name_translated', 'name_translated',
'index_name_display', 'index_name_display',
'default_image_url', 'default_image_url',
'preview_image',
] ]
@ -52,6 +56,9 @@ class ProductTypeBaseSerializer(serializers.ModelSerializer):
name_translated = TranslatedField() name_translated = TranslatedField()
default_image_url = serializers.ImageField(source='default_image.image', default_image_url = serializers.ImageField(source='default_image.image',
allow_null=True) allow_null=True)
preview_image = serializers.URLField(source='preview_image_url',
allow_null=True,
read_only=True)
class Meta: class Meta:
model = models.ProductType model = models.ProductType
@ -60,6 +67,7 @@ class ProductTypeBaseSerializer(serializers.ModelSerializer):
'name_translated', 'name_translated',
'index_name', 'index_name',
'default_image_url', 'default_image_url',
'preview_image',
] ]

View File

@ -55,7 +55,7 @@ def transfer_text_review():
text__isnull=True text__isnull=True
).exclude( ).exclude(
text__iexact='' text__iexact=''
).values('review_id', 'locale', 'text') ).values('review_id', 'locale', 'text', 'updated_by', 'created_at', 'updated_at')
serialized_data = ReviewTextSerializer(data=list(queryset), many=True) serialized_data = ReviewTextSerializer(data=list(queryset), many=True)
if serialized_data.is_valid(): if serialized_data.is_valid():
@ -142,5 +142,8 @@ data_types = {
], ],
"product_review": [ "product_review": [
transfer_product_reviews, transfer_product_reviews,
],
"transfer_text_review": [
transfer_text_review,
] ]
} }

View File

@ -24,6 +24,7 @@ class EstablishmentDocument(Document):
properties=OBJECT_FIELD_PROPERTIES), properties=OBJECT_FIELD_PROPERTIES),
'index_name': fields.KeywordField(attr='index_name'), 'index_name': fields.KeywordField(attr='index_name'),
'default_image': fields.KeywordField(attr='default_image_url'), 'default_image': fields.KeywordField(attr='default_image_url'),
'preview_image_url': fields.KeywordField(),
}) })
establishment_subtypes = fields.ObjectField( establishment_subtypes = fields.ObjectField(
properties={ properties={
@ -31,6 +32,7 @@ class EstablishmentDocument(Document):
'name': fields.ObjectField(attr='name_indexing'), 'name': fields.ObjectField(attr='name_indexing'),
'index_name': fields.KeywordField(attr='index_name'), 'index_name': fields.KeywordField(attr='index_name'),
'default_image': fields.KeywordField(attr='default_image_url'), 'default_image': fields.KeywordField(attr='default_image_url'),
'preview_image_url': fields.KeywordField(),
}, },
multi=True) multi=True)
works_evening = fields.ListField(fields.IntegerField( works_evening = fields.ListField(fields.IntegerField(

View File

@ -20,6 +20,7 @@ class ProductDocument(Document):
'name': fields.ObjectField(attr='name_indexing', properties=OBJECT_FIELD_PROPERTIES), 'name': fields.ObjectField(attr='name_indexing', properties=OBJECT_FIELD_PROPERTIES),
'index_name': fields.KeywordField(), 'index_name': fields.KeywordField(),
'default_image': fields.KeywordField(attr='default_image_url'), 'default_image': fields.KeywordField(attr='default_image_url'),
'preview_image_url': fields.KeywordField(),
}, },
) )
subtypes = fields.ObjectField( subtypes = fields.ObjectField(
@ -28,6 +29,7 @@ class ProductDocument(Document):
'name': fields.ObjectField(attr='name_indexing', properties=OBJECT_FIELD_PROPERTIES), 'name': fields.ObjectField(attr='name_indexing', properties=OBJECT_FIELD_PROPERTIES),
'index_name': fields.KeywordField(), 'index_name': fields.KeywordField(),
'default_image': fields.KeywordField(attr='default_image_url'), 'default_image': fields.KeywordField(attr='default_image_url'),
'preview_image_url': fields.KeywordField(),
}, },
multi=True multi=True
) )

View File

@ -27,6 +27,8 @@ class EstablishmentTypeSerializer(serializers.Serializer):
id = serializers.IntegerField() id = serializers.IntegerField()
name_translated = serializers.SerializerMethodField() name_translated = serializers.SerializerMethodField()
index_name = serializers.CharField() index_name = serializers.CharField()
default_image = serializers.CharField()
preview_image_url = serializers.CharField()
def get_name_translated(self, obj): def get_name_translated(self, obj):
if isinstance(obj, dict): if isinstance(obj, dict):
@ -39,6 +41,8 @@ class ProductSubtypeDocumentSerializer(serializers.Serializer):
id = serializers.IntegerField() id = serializers.IntegerField()
name_translated = serializers.SerializerMethodField() name_translated = serializers.SerializerMethodField()
default_image = serializers.CharField()
preview_image_url = serializers.CharField()
def get_name_translated(self, obj): def get_name_translated(self, obj):
if isinstance(obj, dict): if isinstance(obj, dict):
@ -102,6 +106,8 @@ class ProductTypeSerializer(serializers.Serializer):
id = serializers.IntegerField() id = serializers.IntegerField()
index_name = serializers.CharField() index_name = serializers.CharField()
name_translated = serializers.SerializerMethodField() name_translated = serializers.SerializerMethodField()
default_image = serializers.CharField()
preview_image_url = serializers.CharField()
@staticmethod @staticmethod
def get_name_translated(obj): def get_name_translated(obj):

View File

@ -276,9 +276,6 @@ class EstablishmentDocumentViewSet(BaseDocumentViewSet):
}, },
'subtype': { 'subtype': {
'field': 'establishment_subtypes.index_name', 'field': 'establishment_subtypes.index_name',
'lookups': [
constants.LOOKUP_QUERY_IN,
],
}, },
'works_noon': { 'works_noon': {
'field': 'works_noon', 'field': 'works_noon',

View File

@ -58,7 +58,8 @@ class Command(BaseCommand):
'setup_clean_db', 'setup_clean_db',
'languages', # №4 - перенос языков 'languages', # №4 - перенос языков
'set_unused_regions', 'set_unused_regions',
'update_fake_country_flag' 'update_fake_country_flag',
'transfer_text_review', # переводы для review с их авторами - запускать после overlook и product_review
] ]
def handle(self, *args, **options): def handle(self, *args, **options):
@ -68,7 +69,8 @@ class Command(BaseCommand):
if len(data_type) != 1: if len(data_type) != 1:
data_type = list(set(option for option in options.keys() if options[option]) & set(self.SHORT_DATA_TYPES)) data_type = list(set(option for option in options.keys() if options[option]) & set(self.SHORT_DATA_TYPES))
if len(data_type) != 1: if len(data_type) != 1:
print("You must set correct option!\r\nYou can get options list with \r\n\r\n\tmanage.py help transfer\r\n") print(
"You must set correct option!\r\nYou can get options list with \r\n\r\n\tmanage.py help transfer\r\n")
exit(1) exit(1)
else: else:
data_type = data_type[0] data_type = data_type[0]

View File

@ -1,5 +1,5 @@
from rest_framework import serializers from rest_framework import serializers
from review.models import Review from review.models import Review, ReviewTextAuthor
from account.models import User from account.models import User
from translation.models import Language from translation.models import Language
from establishment.models import Establishment from establishment.models import Establishment
@ -97,24 +97,41 @@ class ReviewTextSerializer(serializers.Serializer):
review_id = serializers.IntegerField() review_id = serializers.IntegerField()
locale = serializers.CharField(allow_null=True) locale = serializers.CharField(allow_null=True)
text = serializers.CharField() text = serializers.CharField()
updated_by = serializers.IntegerField(allow_null=True)
created_at = serializers.DateTimeField(format='%m-%d-%Y %H:%M:%S')
updated_at = serializers.DateTimeField(format='%m-%d-%Y %H:%M:%S')
def validate(self, data): def validate(self, data):
data.update({ data.update({
'new_text': self.get_text(data), 'new_text': self.get_text(data),
'review': self.get_review(data), 'review': self.get_review(data),
'author': self.get_author(data),
}) })
data.pop('review_id') data.pop('review_id')
data.pop('locale')
data.pop('text') data.pop('text')
return data return data
def create(self, validated_data): def create(self, validated_data):
review = validated_data['review'] review = validated_data['review']
author_payload = {
'review': review,
'author': validated_data['author'],
'locale': validated_data['locale'] or 'en-GB',
'created': validated_data['created_at'],
}
if review.text: if review.text:
review.text.update(validated_data['new_text']) review.text.update(validated_data['new_text'])
else: else:
review.text = validated_data['new_text'] review.text = validated_data['new_text']
review.save() review.save()
if validated_data['author'] and validated_data['locale']:
review_author, _ = ReviewTextAuthor.objects.update_or_create(
review=review,
locale=author_payload['locale'],
defaults=author_payload,
)
return review return review
@staticmethod @staticmethod
@ -130,6 +147,12 @@ class ReviewTextSerializer(serializers.Serializer):
raise ValueError(f"Review not found with old_id {data['review_id']}: ") raise ValueError(f"Review not found with old_id {data['review_id']}: ")
return review return review
@staticmethod
def get_author(data):
user = User.objects.filter(old_id=data['updated_by']).first()
if user:
return user
class LanguageSerializer(serializers.ModelSerializer): class LanguageSerializer(serializers.ModelSerializer):
LANGUAGES = { LANGUAGES = {

View File

@ -440,9 +440,11 @@ class HasTagsMixin(models.Model):
@property @property
def visible_tags(self): def visible_tags(self):
return self.tags.filter(category__public=True).prefetch_related('category', return self.tags.filter(category__public=True).prefetch_related(
'translation', 'category__translation')\ 'category',
.exclude(category__value_type='bool') 'translation',
'category__translation',
).exclude(category__value_type='bool')
class Meta: class Meta:
"""Meta class.""" """Meta class."""
@ -468,3 +470,8 @@ class TypeDefaultImageMixin:
"""Return image url.""" """Return image url."""
if hasattr(self, 'default_image') and self.default_image: if hasattr(self, 'default_image') and self.default_image:
return self.default_image.image.url return self.default_image.image.url
@property
def preview_image_url(self):
if hasattr(self, 'default_image') and self.default_image:
return self.default_image.get_image_url(thumbnail_key='type_preview')

View File

@ -409,6 +409,7 @@ SORL_THUMBNAIL_ALIASES = {
'city_xlarge': {'geometry_string': '560x560', 'crop': 'center'}, 'city_xlarge': {'geometry_string': '560x560', 'crop': 'center'},
'city_detail': {'geometry_string': '1120x1120', 'crop': 'center'}, 'city_detail': {'geometry_string': '1120x1120', 'crop': 'center'},
'city_original': {'geometry_string': '2048x1536', 'crop': 'center'}, 'city_original': {'geometry_string': '2048x1536', 'crop': 'center'},
'type_preview': {'geometry_string': '300x260', 'crop': 'center'},
} }