refactored serializers that retrieving phone numbers

This commit is contained in:
Anatoly 2020-02-03 13:10:02 +03:00
parent 3c56b0c061
commit 15b8241213
11 changed files with 127 additions and 57 deletions

3
.gitignore vendored
View File

@ -26,7 +26,8 @@ celerybeat-schedule
local_files local_files
celerybeat.pid celerybeat.pid
/gm_viktor.dump /gm_viktor.dump
/gm_victor.dump
/docker-compose.dump.yml /docker-compose.dump.yml
/gm_production_20191029.sql /gm_production_20191029.sql
/apps/transfer/log /apps/transfer/log
/gm_production_*.sql /gm_production_*.sql

View File

@ -22,7 +22,7 @@ from establishment.models import Establishment, EstablishmentSubType
from location.models import Country from location.models import Country
from main.models import SiteSettings from main.models import SiteSettings
from utils.models import GMTokenGenerator from utils.models import GMTokenGenerator
from utils.models import ImageMixin, ProjectBaseMixin, PlatformMixin from utils.models import ImageMixin, ProjectBaseMixin, PlatformMixin, PhoneModelMixin
from utils.tokens import GMRefreshToken from utils.tokens import GMRefreshToken
@ -207,7 +207,7 @@ class UserQuerySet(models.QuerySet):
return self.filter(userrole__role__country__code=country_code).distinct() return self.filter(userrole__role__country__code=country_code).distinct()
class User(AbstractUser): class User(PhoneModelMixin, AbstractUser):
"""Base user model.""" """Base user model."""
username = models.CharField( username = models.CharField(
_('username'), _('username'),

View File

@ -45,7 +45,9 @@ class BackUserSerializer(UserSerializer):
'roles', 'roles',
'subscriptions', 'subscriptions',
'phone', 'phone',
'phone_as_international', 'country_calling_code',
'national_calling_number',
) )
extra_kwargs = { extra_kwargs = {
'password': {'write_only': True}, 'password': {'write_only': True},

View File

@ -3,7 +3,6 @@ from django.conf import settings
from django.contrib.auth import password_validation as password_validators from django.contrib.auth import password_validation as password_validators
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from fcm_django.models import FCMDevice from fcm_django.models import FCMDevice
from phonenumber_field.serializerfields import PhoneNumberField
from rest_framework import exceptions from rest_framework import exceptions
from rest_framework import serializers from rest_framework import serializers
from rest_framework import validators as rest_validators from rest_framework import validators as rest_validators
@ -14,6 +13,7 @@ from notification.models import Subscribe, Subscriber
from utils import exceptions as utils_exceptions from utils import exceptions as utils_exceptions
from utils import methods as utils_methods from utils import methods as utils_methods
from utils.methods import generate_string_code from utils.methods import generate_string_code
from utils.serializers import PhoneMixinSerializer
def subscriptions_handler(subscriptions_list, user): def subscriptions_handler(subscriptions_list, user):
@ -60,7 +60,7 @@ class RoleBaseSerializer(serializers.ModelSerializer):
] ]
class UserSerializer(serializers.ModelSerializer): class UserSerializer(PhoneMixinSerializer, serializers.ModelSerializer):
"""User serializer.""" """User serializer."""
# RESPONSE # RESPONSE
fullname = serializers.CharField(source='get_full_name', read_only=True) fullname = serializers.CharField(source='get_full_name', read_only=True)
@ -81,9 +81,6 @@ class UserSerializer(serializers.ModelSerializer):
required=False, required=False,
help_text='list of subscription_types id', help_text='list of subscription_types id',
) )
phone_as_international = PhoneNumberField(source='phone.as_international',
allow_null=True,
read_only=True)
is_superuser = serializers.BooleanField(read_only=True) is_superuser = serializers.BooleanField(read_only=True)
class Meta: class Meta:
@ -102,7 +99,8 @@ class UserSerializer(serializers.ModelSerializer):
'roles', 'roles',
'subscriptions', 'subscriptions',
'phone', 'phone',
'phone_as_international', 'country_calling_code',
'national_calling_number',
'is_superuser', 'is_superuser',
] ]
extra_kwargs = { extra_kwargs = {

View File

@ -4,11 +4,11 @@ from django.contrib.contenttypes.admin import GenericTabularInline
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from comment.models import Comment from comment.models import Comment
from utils.admin import BaseModelAdminMixin
from establishment import models from establishment import models
from main.models import Award from main.models import Award
from product.models import Product, PurchasedProduct from product.models import Product, PurchasedProduct
from review import models as review_models from review import models as review_models
from utils.admin import BaseModelAdminMixin
@admin.register(models.EstablishmentType) @admin.register(models.EstablishmentType)
@ -87,11 +87,11 @@ class EstablishmentAdmin(BaseModelAdminMixin, admin.ModelAdmin):
search_fields = ['id', 'name', 'index_name', 'slug'] search_fields = ['id', 'name', 'index_name', 'slug']
list_filter = ['public_mark', 'toque_number'] list_filter = ['public_mark', 'toque_number']
inlines = [CompanyInline, EstablishmentNote, GalleryImageInline, inlines = [CompanyInline, EstablishmentNote, GalleryImageInline,
PurchasedProductInline, ] PurchasedProductInline, ContactPhoneInline, ]
# inlines = [ # inlines = [
# AwardInline, ContactPhoneInline, ContactEmailInline, # AwardInline, ContactPhoneInline, ContactEmailInline,
# ReviewInline, CommentInline, ProductInline] # ReviewInline, CommentInline, ProductInline]
raw_id_fields = ('address', 'collections', 'tags', 'schedule') raw_id_fields = ('address', 'collections', 'tags', 'schedule', )
@admin.register(models.Position) @admin.register(models.Position)
@ -142,3 +142,7 @@ class CompanyAdmin(BaseModelAdminMixin, admin.ModelAdmin):
"""Admin conf for Company model.""" """Admin conf for Company model."""
raw_id_fields = ['establishment', 'address', ] raw_id_fields = ['establishment', 'address', ]
@admin.register(models.Employee)
class EmployeeAdmin(BaseModelAdminMixin, admin.ModelAdmin):
raw_id_fields = ['establishments', 'tags', 'user', 'photo', ]

View File

@ -14,13 +14,14 @@ from django.contrib.postgres.fields import ArrayField
from django.contrib.postgres.indexes import GinIndex from django.contrib.postgres.indexes import GinIndex
from django.contrib.postgres.search import TrigramSimilarity from django.contrib.postgres.search import TrigramSimilarity
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.core.validators import FileExtensionValidator, MaxValueValidator, MinValueValidator from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models from django.db import models
from django.db.models import Case, ExpressionWrapper, F, Prefetch, Q, Subquery, When from django.db.models import Case, ExpressionWrapper, F, Prefetch, Q, Subquery, When
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from django.utils import timezone from django.utils import timezone
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from phonenumber_field.modelfields import PhoneNumberField from phonenumber_field.modelfields import PhoneNumberField
from phonenumber_field.phonenumber import PhoneNumber
from timezone_field import TimeZoneField from timezone_field import TimeZoneField
from location.models import Address from location.models import Address
@ -30,8 +31,9 @@ from tag.models import Tag
from timetable.models import Timetable from timetable.models import Timetable
from utils.methods import transform_into_readable_str from utils.methods import transform_into_readable_str
from utils.models import ( from utils.models import (
BaseAttributes, FavoritesMixin, FileMixin, GalleryMixin, HasTagsMixin, IntermediateGalleryModelMixin, BaseAttributes, FavoritesMixin, FileMixin, GalleryMixin, HasTagsMixin,
ProjectBaseMixin, TJSONField, TranslatedFieldsMixin, TypeDefaultImageMixin, URLImageMixin, default_menu_bool_array IntermediateGalleryModelMixin, ProjectBaseMixin, TJSONField, TranslatedFieldsMixin,
TypeDefaultImageMixin, URLImageMixin, default_menu_bool_array, PhoneModelMixin,
) )
@ -1124,7 +1126,7 @@ class EmployeeQuerySet(models.QuerySet):
) )
class Employee(BaseAttributes): class Employee(PhoneModelMixin, BaseAttributes):
"""Employee model.""" """Employee model."""
user = models.OneToOneField('account.User', on_delete=models.PROTECT, user = models.OneToOneField('account.User', on_delete=models.PROTECT,
@ -1210,7 +1212,7 @@ class EstablishmentScheduleQuerySet(models.QuerySet):
"""QuerySet for model EstablishmentSchedule""" """QuerySet for model EstablishmentSchedule"""
class ContactPhone(models.Model): class ContactPhone(PhoneModelMixin, models.Model):
"""Contact phone model.""" """Contact phone model."""
establishment = models.ForeignKey( establishment = models.ForeignKey(
Establishment, related_name='phones', on_delete=models.CASCADE) Establishment, related_name='phones', on_delete=models.CASCADE)
@ -1223,16 +1225,6 @@ class ContactPhone(models.Model):
def __str__(self): def __str__(self):
return f'{self.phone.as_e164}' return f'{self.phone.as_e164}'
@property
def country_calling_code(self):
"""Return phone code from PhonеNumberField."""
return f'+{self.phone.country_code}' if self.phone and hasattr(self, 'phone') else None
@property
def national_calling_number(self):
"""Return phone national number from from PhonеNumberField."""
return self.phone.national_number if self.phone and hasattr(self, 'phone') else None
class ContactEmail(models.Model): class ContactEmail(models.Model):
"""Contact email model.""" """Contact email model."""
@ -1571,3 +1563,46 @@ class Company(ProjectBaseMixin):
"""Meta class.""" """Meta class."""
verbose_name = _('company') verbose_name = _('company')
verbose_name_plural = _('companies') verbose_name_plural = _('companies')
def _phonenumber_constructor(self, field_name: str) -> list:
array = []
if field_name in [field.name for field in self._meta.fields]:
values = getattr(self, field_name, None)
if values:
for number in values:
try:
obj = PhoneNumber.from_string(
phone_number=number,
region=None if number.startswith('+')
else settings.PHONENUMBER_DEFAULT_REGION)
except Exception as e:
pass
else:
array.append(obj) if obj.is_valid() else None
return array
def phones_(self) -> list:
"""Return PhoneNumber objects."""
return self._phonenumber_constructor('phones')
def faxes_(self) -> list:
"""Return PhoneNumber objects."""
return self._phonenumber_constructor('faxes')
def _array_constructor(self, field_name: str) -> list:
array = []
for obj in self._phonenumber_constructor(field_name):
if hasattr(obj, 'country_code') and hasattr(obj, 'national_number'):
array.append({
'country_calling_code': f'+{obj.country_code}',
'national_calling_number': str(obj.national_number),
})
return array
@property
def phones_array(self) -> list:
return self._array_constructor('phones')
@property
def faxes_array(self) -> list:
return self._array_constructor('faxes')

View File

@ -14,7 +14,8 @@ from location.serializers import AddressDetailSerializer, TranslatedField
from main.models import Currency from main.models import Currency
from main.serializers import AwardSerializer from main.serializers import AwardSerializer
from utils.decorators import with_base_attributes from utils.decorators import with_base_attributes
from utils.serializers import ImageBaseSerializer, ProjectModelSerializer, TimeZoneChoiceField from utils.serializers import ImageBaseSerializer, ProjectModelSerializer, TimeZoneChoiceField, \
PhoneMixinSerializer
def phones_handler(phones_list, establishment): def phones_handler(phones_list, establishment):
@ -136,6 +137,17 @@ class EstablishmentPositionListSerializer(model_serializers.EstablishmentBaseSer
] ]
class EstablishmentContactPhoneSerializer(PhoneMixinSerializer, serializers.ModelSerializer):
"""Establishment contact phone serializer."""
class Meta:
"""Meta class."""
model = models.ContactPhone
fields = [
'country_calling_code',
'national_calling_number',
]
class EstablishmentRUDSerializer(model_serializers.EstablishmentBaseSerializer): class EstablishmentRUDSerializer(model_serializers.EstablishmentBaseSerializer):
"""Establishment create serializer""" """Establishment create serializer"""
@ -156,16 +168,9 @@ class EstablishmentRUDSerializer(model_serializers.EstablishmentBaseSerializer):
subtypes = model_serializers.EstablishmentSubTypeBaseSerializer(source='establishment_subtypes', subtypes = model_serializers.EstablishmentSubTypeBaseSerializer(source='establishment_subtypes',
read_only=True, many=True) read_only=True, many=True)
type = model_serializers.EstablishmentTypeBaseSerializer(source='establishment_type', read_only=True) type = model_serializers.EstablishmentTypeBaseSerializer(source='establishment_type', read_only=True)
phones = serializers.ListField( phones = EstablishmentContactPhoneSerializer(read_only=True, many=True)
source='contact_phones',
allow_null=True,
allow_empty=True,
child=serializers.CharField(max_length=128),
required=False,
)
class Meta: class Meta(model_serializers.EstablishmentBaseSerializer.Meta):
model = models.Establishment
fields = [ fields = [
'id', 'id',
'slug', 'slug',
@ -248,10 +253,8 @@ class PlatesSerializers(model_serializers.PlateSerializer):
] ]
class ContactPhoneBackSerializers(model_serializers.PlateSerializer): class ContactPhoneBackSerializers(PhoneMixinSerializer, serializers.ModelSerializer):
"""ContactPhone serializers.""" """ContactPhone serializers."""
country_calling_code = serializers.CharField(read_only=True, allow_null=True)
national_calling_number = serializers.CharField(read_only=True, allow_null=True)
class Meta: class Meta:
model = models.ContactPhone model = models.ContactPhone
@ -262,6 +265,9 @@ class ContactPhoneBackSerializers(model_serializers.PlateSerializer):
'country_calling_code', 'country_calling_code',
'national_calling_number', 'national_calling_number',
] ]
extra_kwarg = {
'phone': {'write_only': True}
}
class ContactEmailBackSerializers(model_serializers.PlateSerializer): class ContactEmailBackSerializers(model_serializers.PlateSerializer):
@ -293,7 +299,7 @@ class PositionBackSerializer(serializers.ModelSerializer):
# TODO: test decorator # TODO: test decorator
@with_base_attributes @with_base_attributes
class EmployeeBackSerializers(serializers.ModelSerializer): class EmployeeBackSerializers(PhoneMixinSerializer, serializers.ModelSerializer):
"""Employee serializers.""" """Employee serializers."""
public_mark = serializers.SerializerMethodField() public_mark = serializers.SerializerMethodField()
positions = serializers.SerializerMethodField() positions = serializers.SerializerMethodField()
@ -385,11 +391,16 @@ class EmployeeBackSerializers(serializers.ModelSerializer):
'birth_date', 'birth_date',
'email', 'email',
'phone', 'phone',
'country_calling_code',
'national_calling_number',
'toque_number', 'toque_number',
'available_for_events', 'available_for_events',
'photo', 'photo',
'photo_id', 'photo_id',
] ]
extra_kwargs = {
'phone': {'write_only': True}
}
class EstablishmentEmployeeBackSerializer(serializers.ModelSerializer): class EstablishmentEmployeeBackSerializer(serializers.ModelSerializer):
@ -411,6 +422,7 @@ class EstablishmentEmployeeBackSerializer(serializers.ModelSerializer):
class EstEmployeeBackSerializer(EmployeeBackSerializers): class EstEmployeeBackSerializer(EmployeeBackSerializers):
@property @property
def request_kwargs(self): def request_kwargs(self):
"""Get url kwargs from request.""" """Get url kwargs from request."""
@ -451,10 +463,15 @@ class EstEmployeeBackSerializer(EmployeeBackSerializers):
'birth_date', 'birth_date',
'email', 'email',
'phone', 'phone',
'country_calling_code',
'national_calling_number',
'toque_number', 'toque_number',
'available_for_events', 'available_for_events',
'photo', 'photo',
] ]
extra_kwargs = {
'phone': {'write_only': True}
}
class EstablishmentBackOfficeGallerySerializer(serializers.ModelSerializer): class EstablishmentBackOfficeGallerySerializer(serializers.ModelSerializer):

View File

@ -633,8 +633,8 @@ class EstablishmentCarouselCreateSerializer(CarouselCreateSerializer):
class CompanyBaseSerializer(serializers.ModelSerializer): class CompanyBaseSerializer(serializers.ModelSerializer):
"""Company base serializer""" """Company base serializer"""
phone_list = serializers.SerializerMethodField(source='phones', read_only=True) phone_list = serializers.ListField(source='phones_array', read_only=True)
fax_list = serializers.SerializerMethodField(source='faxes', read_only=True) fax_list = serializers.ListField(source='faxes_array', read_only=True)
address_detail = AddressDetailSerializer(source='address', read_only=True) address_detail = AddressDetailSerializer(source='address', read_only=True)
class Meta: class Meta:
@ -662,14 +662,6 @@ class CompanyBaseSerializer(serializers.ModelSerializer):
'address': {'write_only': True} 'address': {'write_only': True}
} }
def get_phone_list(self, instance):
"""Return list of phone numbers."""
return instance.phones
def get_fax_list(self, instance):
"""Return list of fax numbers."""
return instance.faxes
def validate(self, attrs): def validate(self, attrs):
"""Overridden validate method""" """Overridden validate method"""
phones = [str_to_phonenumber(phone).as_national for phone in attrs.get('phones')] phones = [str_to_phonenumber(phone).as_national for phone in attrs.get('phones')]

View File

@ -511,3 +511,18 @@ class TypeDefaultImageMixin:
def default_menu_bool_array(): def default_menu_bool_array():
return [False] * 7 return [False] * 7
class PhoneModelMixin:
"""Mixin for PhoneNumberField."""
@property
def country_calling_code(self):
"""Return phone code from PhonеNumberField."""
if hasattr(self, 'phone') and self.phone:
return f'+{self.phone.country_code}'
@property
def national_calling_number(self):
"""Return phone national number from from PhonеNumberField."""
if hasattr(self, 'phone') and (self.phone and hasattr(self.phone, 'national_number')):
return self.phone.national_number

View File

@ -137,3 +137,9 @@ class ImageBaseSerializer(serializers.Serializer):
original_url = serializers.URLField() original_url = serializers.URLField()
orientation_display = serializers.CharField() orientation_display = serializers.CharField()
auto_crop_images = serializers.DictField(allow_null=True) auto_crop_images = serializers.DictField(allow_null=True)
class PhoneMixinSerializer(serializers.Serializer):
"""Phone mixin serializer."""
country_calling_code = serializers.CharField(read_only=True, allow_null=True)
national_calling_number = serializers.CharField(read_only=True, allow_null=True)

View File

@ -86,11 +86,11 @@ LOGGING = {
'py.warnings': { 'py.warnings': {
'handlers': ['console'], 'handlers': ['console'],
}, },
'django.db.backends': { # 'django.db.backends': {
'handlers': ['console', ], # 'handlers': ['console', ],
'level': 'DEBUG', # 'level': 'DEBUG',
'propagate': False, # 'propagate': False,
}, # },
} }
} }