added field phone to User model, change establishment transfer, added filters (full-text search, role_country_code, country_code), change account serializers

This commit is contained in:
Anatoly 2020-01-21 17:36:31 +03:00
parent 6d627ac240
commit de78ee5d6e
10 changed files with 103 additions and 24 deletions

View File

@ -25,13 +25,13 @@ class UserAdmin(BaseUserAdmin):
'is_staff', 'is_superuser', 'email_confirmed')
list_filter = ('is_active', 'is_staff', 'is_superuser', 'email_confirmed',
'groups',)
search_fields = ('email', 'first_name', 'last_name')
search_fields = ('email', 'first_name', 'last_name', 'phone',)
readonly_fields = ('last_login', 'date_joined', 'image_preview', 'cropped_image_preview', 'last_ip', 'last_country')
fieldsets = (
(None, {'fields': ('email', 'password',)}),
(_('Personal info'), {
'fields': ('username', 'first_name', 'last_name',
'image_url', 'image_preview',
'image_url', 'image_preview', 'phone',
'cropped_image_url', 'cropped_image_preview',)}),
(_('Subscription'), {
'fields': (

View File

@ -8,8 +8,10 @@ from account import models
class AccountBackOfficeFilter(filters.FilterSet):
"""Account filter set."""
search = filters.CharFilter(method='search_text')
role_country_code = filters.CharFilter(method='by_role_country_code')
role = filters.MultipleChoiceFilter(choices=models.Role.ROLE_CHOICES,
method='filter_by_roles')
method='by_roles')
class Meta:
"""Meta class."""
@ -21,9 +23,33 @@ class AccountBackOfficeFilter(filters.FilterSet):
'is_staff',
'is_active',
'is_superuser',
'search',
'role_country_code',
)
def filter_by_roles(self, queryset, name, value):
def by_roles(self, queryset, name, value):
if value not in EMPTY_VALUES:
return queryset.by_roles(value)
return queryset
def search_text(self, queryset, name, value):
if value not in EMPTY_VALUES:
return queryset.search_text(value)
return queryset
def by_role_country_code(self, queryset, name, value):
if value not in EMPTY_VALUES:
return queryset.by_role_country_code(value)
return queryset
class RoleListFilter(filters.FilterSet):
"""Role filter set."""
country_code = filters.CharFilter(field_name='country__code')
class Meta:
"""Meta class."""
model = models.Role
fields = [
'country_code',
]

View File

@ -0,0 +1,19 @@
# Generated by Django 2.2.7 on 2020-01-20 09:39
from django.db import migrations
import phonenumber_field.modelfields
class Migration(migrations.Migration):
dependencies = [
('account', '0032_auto_20200114_1311'),
]
operations = [
migrations.AddField(
model_name='user',
name='phone',
field=phonenumber_field.modelfields.PhoneNumberField(blank=True, default=None, max_length=128, null=True, verbose_name='Phone'),
),
]

View File

@ -20,6 +20,8 @@ from main.models import SiteSettings
from utils.models import GMTokenGenerator
from utils.models import ImageMixin, ProjectBaseMixin, PlatformMixin
from utils.tokens import GMRefreshToken
from django.contrib.postgres.search import SearchVector
from phonenumber_field.modelfields import PhoneNumberField
class RoleQuerySet(models.QuerySet):
@ -142,6 +144,24 @@ class UserQuerySet(models.QuerySet):
role = Role.objects.filter(role=Role.ESTABLISHMENT_MANAGER).first()
return self.by_role(role).filter(userrole__establishment=establishment)
def annotate_search_text(self):
"""Full-text search"""
return self.annotate(search_text=SearchVector(
'username',
'first_name',
'last_name',
'email',
'phone',
))
def search_text(self, value: str):
"""Filter by annotated search vector."""
return self.annotate_search_text().filter(search_text=value)
def by_role_country_code(self, country_code: str):
"""Filter by role country code."""
return self.filter(userrole__role__country__code=country_code).distinct()
class User(AbstractUser):
"""Base user model."""
@ -178,6 +198,8 @@ class User(AbstractUser):
default=None,
on_delete=models.SET_NULL,
)
phone = PhoneNumberField(blank=True, null=True, default=None,
verbose_name=_('Phone'))
EMAIL_FIELD = 'email'
USERNAME_FIELD = 'email'

View File

@ -2,8 +2,7 @@
from rest_framework import serializers
from account import models
from account.models import User
from account.serializers import RoleBaseSerializer, subscriptions_handler
from account.serializers import RoleBaseSerializer, UserSerializer, subscriptions_handler
from main.models import SiteSettings
@ -17,20 +16,12 @@ class _SiteSettingsSerializer(serializers.ModelSerializer):
)
class BackUserSerializer(serializers.ModelSerializer):
class BackUserSerializer(UserSerializer):
last_country = _SiteSettingsSerializer(read_only=True)
roles = RoleBaseSerializer(many=True, read_only=True)
subscriptions = serializers.ListField(
source='subscription_types',
allow_null=True,
allow_empty=True,
child=serializers.IntegerField(min_value=1),
required=False,
help_text='list of subscription_types id',
)
class Meta:
model = User
class Meta(UserSerializer.Meta):
fields = (
'id',
'last_login',
@ -54,9 +45,12 @@ class BackUserSerializer(serializers.ModelSerializer):
'last_country',
'roles',
'subscriptions',
'phone',
'phone_as_international',
)
extra_kwargs = {
'password': {'write_only': True},
'phone': {'write_only': True},
}
read_only_fields = (
'old_password',
@ -82,8 +76,7 @@ class BackUserSerializer(serializers.ModelSerializer):
class BackDetailUserSerializer(BackUserSerializer):
class Meta:
model = User
class Meta(BackUserSerializer.Meta):
fields = (
'id',
'last_country',
@ -109,6 +102,8 @@ class BackDetailUserSerializer(BackUserSerializer):
'groups',
'user_permissions',
'subscriptions',
'phone',
'phone_as_international',
)
read_only_fields = (
'old_password',

View File

@ -13,6 +13,7 @@ from notification.models import Subscribe, Subscriber
from utils import exceptions as utils_exceptions
from utils import methods as utils_methods
from utils.methods import generate_string_code
from phonenumber_field.serializerfields import PhoneNumberField
def subscriptions_handler(subscriptions_list, user):
@ -43,6 +44,7 @@ class RoleBaseSerializer(serializers.ModelSerializer):
"""Serializer for model Role."""
role_display = serializers.CharField(source='get_role_display', read_only=True)
navigation_bar_permission = NavigationBarPermissionBaseSerializer(read_only=True)
country_code = serializers.CharField(source='country.code', read_only=True, allow_null=True)
class Meta:
"""Meta class."""
@ -51,6 +53,7 @@ class RoleBaseSerializer(serializers.ModelSerializer):
'id',
'role_display',
'navigation_bar_permission',
'country_code',
]
@ -75,6 +78,9 @@ class UserSerializer(serializers.ModelSerializer):
required=False,
help_text='list of subscription_types id',
)
phone_as_international = PhoneNumberField(source='phone.as_international',
allow_null=True,
read_only=True)
class Meta:
model = models.User
@ -90,6 +96,8 @@ class UserSerializer(serializers.ModelSerializer):
'newsletter',
'roles',
'subscriptions',
'phone',
'phone_as_international',
]
extra_kwargs = {
'first_name': {'required': False, 'write_only': True, },
@ -98,6 +106,7 @@ class UserSerializer(serializers.ModelSerializer):
'image_url': {'required': False, },
'cropped_image_url': {'required': False, },
'newsletter': {'required': False, },
'phone': {'required': False, 'write_only': True, }
}
def create(self, validated_data):
@ -113,7 +122,7 @@ class UserSerializer(serializers.ModelSerializer):
def validate_email(self, value):
"""Validate email value"""
if value == self.instance.email:
if hasattr(self.instance, 'email') and self.instance.email and value == self.instance.email:
raise serializers.ValidationError(detail='Equal email address.')
return value

View File

@ -15,6 +15,7 @@ from account.serializers.common import RoleBaseSerializer
class RoleListView(generics.ListCreateAPIView):
serializer_class = RoleBaseSerializer
queryset = models.Role.objects.all()
filter_class = filters.RoleListFilter
class RoleTabRetrieveView(generics.GenericAPIView):

View File

@ -54,6 +54,3 @@ class Image(ProjectBaseMixin, SORLImageMixin, PlatformMixin):
if completely:
# Delete an instance of image
super().delete()

View File

@ -12,6 +12,7 @@ from timetable.models import Timetable
from utils.legacy_parser import parse_legacy_schedule_content
from utils.serializers import TimeZoneChoiceField
from utils.slug_generator import generate_unique_slug
from phonenumber_field.phonenumber import PhoneNumber
class EstablishmentSerializer(serializers.ModelSerializer):
@ -58,6 +59,7 @@ class EstablishmentSerializer(serializers.ModelSerializer):
def validate(self, data):
old_type = data.pop('type', None)
phone = data.pop('phone', None)
data.update({
'slug': generate_unique_slug(Establishment, data['slug'] if data['slug'] else data['name']),
@ -65,6 +67,7 @@ class EstablishmentSerializer(serializers.ModelSerializer):
'establishment_type_id': self.get_type(old_type),
'is_publish': data.get('state') == 'published',
'subtype': self.get_subtype(old_type),
'phone': self.get_phone(phone),
})
data.pop('location')
data.pop('state')
@ -179,6 +182,13 @@ class EstablishmentSerializer(serializers.ModelSerializer):
establishment_type_id=establishment_type_id)
return subtype
@staticmethod
def get_phone(phone: str):
phone_obj = PhoneNumber.from_string(phone_number=phone, region='FR')
if phone_obj.is_valid():
fmt = PhoneNumber.format_map[getattr(settings, 'PHONENUMBER_DB_FORMAT', 'INTERNATIONAL')]
return phone_obj.format_as(fmt)
class EstablishmentNoteSerializer(serializers.ModelSerializer):

View File

@ -517,7 +517,7 @@ STATICFILES_DIRS = (
# MEDIA
MEDIA_LOCATION = 'media'
PHONENUMBER_DB_FORMAT = 'NATIONAL'
PHONENUMBER_DB_FORMAT = 'INTERNATIONAL'
PHONENUMBER_DEFAULT_REGION = "FR"
FALLBACK_LOCALE = 'en-GB'