diff --git a/account/serializers.py b/account/serializers.py index 88d544d..d75039a 100644 --- a/account/serializers.py +++ b/account/serializers.py @@ -26,7 +26,7 @@ class UpdateJuniorProfileImageSerializer(serializers.ModelSerializer): class Meta(object): """Meta info""" model = Junior - fields = ['image'] + fields = ['id', 'image'] def update(self, instance, validated_data): """update image """ @@ -41,7 +41,7 @@ class UpdateGuardianImageSerializer(serializers.ModelSerializer): class Meta(object): """Meta info""" model = Guardian - fields = ['image'] + fields = ['id','image'] def update(self, instance, validated_data): """update image """ @@ -192,7 +192,7 @@ class JuniorSerializer(serializers.ModelSerializer): model = Junior fields = ['id', 'auth_token', 'email', 'first_name', 'last_name', 'country_code', 'phone', 'gender', 'dob', 'guardian_code', 'referral_code','is_active', 'is_complete_profile', 'created_at', 'image', - 'updated_at', 'user_type', 'country_name'] + 'updated_at', 'user_type', 'country_name','is_invited'] class EmailVerificationSerializer(serializers.ModelSerializer): """Email verification serializer""" @@ -208,7 +208,7 @@ class DefaultTaskImagesSerializer(serializers.ModelSerializer): class Meta(object): """Meta info""" model = DefaultTaskImages - fields = ['task_name', 'image_url'] + fields = ['id', 'task_name', 'image_url'] def create(self, validated_data): data = DefaultTaskImages.objects.create(**validated_data) return data @@ -225,7 +225,7 @@ class UserDeleteSerializer(serializers.ModelSerializer): class Meta(object): """Meta Information""" model = UserDelete - fields = ['reason'] + fields = ['id','reason'] def create(self, validated_data): user = self.context['user'] user_type = str(self.context['user_type']) diff --git a/account/templates/templated_email/junior_approval_mail.email b/account/templates/templated_email/junior_approval_mail.email new file mode 100644 index 0000000..fd3c19f --- /dev/null +++ b/account/templates/templated_email/junior_approval_mail.email @@ -0,0 +1,23 @@ +{% extends "templated_email/email_base.email" %} + +{% block subject %} + Approval Email +{% endblock %} + +{% block plain %} + + +

+ Hi {{full_name}}, +

+ + + + +

+ Please approve {{full_name}} for accessing the Zod bank platform as a junior. + +

+ + +{% endblock %} diff --git a/account/templates/templated_email/junior_notification_email.email b/account/templates/templated_email/junior_notification_email.email new file mode 100644 index 0000000..ea9c214 --- /dev/null +++ b/account/templates/templated_email/junior_notification_email.email @@ -0,0 +1,24 @@ +{% extends "templated_email/email_base.email" %} + +{% block subject %} + Invitation Email +{% endblock %} + +{% block plain %} + + +

+ Hi {{full_name}}, +

+ + + + +

+ You are receiving this email for join the ZOD bank platform. Please use {{ url }} link to join the platform. +
Your credentials are:- username = {{email}} and password {{password}}

Below are the steps to complete the account and how to use this platform. + +

+ + +{% endblock %} diff --git a/account/utils.py b/account/utils.py index 17f1bf5..3b74d24 100644 --- a/account/utils.py +++ b/account/utils.py @@ -6,6 +6,7 @@ from rest_framework.response import Response """Third party Django app""" from templated_email import send_templated_mail import jwt +import string from datetime import datetime from calendar import timegm from uuid import uuid4 @@ -179,3 +180,10 @@ def get_token(data: dict = dict): 'access': access, 'refresh': refresh } + + +def generate_alphanumeric_code(length): + alphabet = string.ascii_letters + string.digits + code = ''.join(secrets.choice(alphabet) for _ in range(length)) + return code + diff --git a/account/views.py b/account/views.py index aab69fb..6b93ac8 100644 --- a/account/views.py +++ b/account/views.py @@ -86,11 +86,21 @@ class GoogleLoginMixin: user_obj = User.objects.create(username=email, email=email, first_name=first_name, last_name=last_name) if str(user_type) == '1': junior_query = Junior.objects.create(auth=user_obj, is_verified=True, is_active=True, - image=profile_picture, signup_method='2') + image=profile_picture, signup_method='2', + junior_code=''.join( + [str(random.randrange(9)) for _ in range(6)]), + referral_code=''.join( + [str(random.randrange(9)) for _ in range(6)]) + ) serializer = JuniorSerializer(junior_query) if str(user_type) == '2': guardian_query = Guardian.objects.create(user=user_obj, is_verified=True, is_active=True, - image=profile_picture,signup_method='2') + image=profile_picture,signup_method='2', + guardian_code=''.join( + [str(random.randrange(9)) for _ in range(6)]), + referral_code=''.join( + [str(random.randrange(9)) for _ in range(6)]) + ) serializer = GuardianSerializer(guardian_query) # Return a JSON response with the user's email and name return custom_response(SUCCESS_CODE['3003'], serializer.data, @@ -131,11 +141,18 @@ class SigninWithApple(views.APIView): user = User.objects.create(**user_data) if str(user_type) == '1': junior_query = Junior.objects.create(auth=user, is_verified=True, is_active=True, - signup_method='3') + signup_method='3',junior_code=''.join( + [str(random.randrange(9)) for _ in range(6)]), + referral_code=''.join( + [str(random.randrange(9)) for _ in range(6)])) serializer = JuniorSerializer(junior_query) if str(user_type) == '2': guardian_query = Guardian.objects.create(user=user, is_verified=True, is_active=True, - signup_method='3') + signup_method='3', + guardian_code=''.join( + [str(random.randrange(9)) for _ in range(6)]), + referral_code=''.join( + [str(random.randrange(9)) for _ in range(6)])) serializer = GuardianSerializer(guardian_query) return custom_response(SUCCESS_CODE['3003'], serializer.data, response_status=status.HTTP_200_OK) @@ -442,7 +459,6 @@ class DeleteUserProfileAPIViewSet(viewsets.GenericViewSet): user_type = str(request.data['user_type']) password = request.data.get('password') signup_method = str(request.data.get('signup_method')) - print("signup_method===>",signup_method,'==>',type(signup_method)) serializer = self.get_serializer(data=request.data, context={'request': request, 'user': request.user, 'user_type': user_type, 'password': password, diff --git a/base/constants.py b/base/constants.py index c1f48c3..c2458f8 100644 --- a/base/constants.py +++ b/base/constants.py @@ -51,6 +51,11 @@ SIGNUP_METHODS = ( ('2', 'google'), ('3', 'apple') ) +"""relationship""" +RELATIONSHIP = ( + ('1', 'parent'), + ('2', 'legal_guardian') +) PENDING = 1 IN_PROGRESS = 2 REJECTED = 3 @@ -69,5 +74,3 @@ BYTE_IMAGE_SIZE = 1024 # validate file size MAX_FILE_SIZE = 1024 * 1024 * 5 - - diff --git a/base/messages.py b/base/messages.py index 7810bfc..f811439 100644 --- a/base/messages.py +++ b/base/messages.py @@ -56,7 +56,8 @@ ERROR_CODE = { "2030": "Use correct user type and token", "2031": "Invalid password", "2032": "Failed to send email", - "2033": "Missing required fields" + "2033": "Missing required fields", + "2034": "Junior is not associated" } SUCCESS_CODE = { # Success code for password @@ -88,7 +89,9 @@ SUCCESS_CODE = { "3017": "Profile image update successfully", "3018": "Task created successfully", "3019": "Support Email sent successfully", - "3020": "Logged out successfully." + "3020": "Logged out successfully.", + "3021": "Add junior successfully", + "3022": "Remove junior successfully" } STATUS_CODE_ERROR = { diff --git a/guardian/serializers.py b/guardian/serializers.py index 9ba837f..2aee559 100644 --- a/guardian/serializers.py +++ b/guardian/serializers.py @@ -21,7 +21,7 @@ class UserSerializer(serializers.ModelSerializer): class Meta(object): """Meta info""" model = User - fields = ['email', 'password', 'auth_token'] + fields = ['id', 'email', 'password', 'auth_token'] def get_auth_token(self, obj): """generate auth token""" @@ -72,7 +72,7 @@ class CreateGuardianSerializer(serializers.ModelSerializer): class Meta(object): """Meta info""" model = Guardian - fields = ['first_name', 'last_name', 'email', 'phone', 'family_name', 'gender', 'country_code', + fields = ['id', 'first_name', 'last_name', 'email', 'phone', 'family_name', 'gender', 'country_code', 'dob', 'referral_code', 'passcode', 'is_complete_profile', 'referral_code_used', 'country_name', 'image'] @@ -139,7 +139,7 @@ class TaskSerializer(serializers.ModelSerializer): class Meta(object): """Meta info""" model = JuniorTask - fields = ['task_name','task_description','points', 'due_date', 'junior', 'default_image'] + fields = ['id', 'task_name','task_description','points', 'due_date', 'junior', 'default_image'] def create(self, validated_data): """create default task image data""" validated_data['guardian'] = Guardian.objects.filter(user=self.context['user']).last() diff --git a/guardian/views.py b/guardian/views.py index f8295a3..5d28bfe 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -169,6 +169,6 @@ class TopJuniorListAPIView(viewsets.ModelViewSet): junior.save() serializer = self.get_serializer(junior_total_points, many=True) - return custom_response(serializer.data, response_status=status.HTTP_200_OK) + return custom_response(None, serializer.data, response_status=status.HTTP_200_OK) diff --git a/junior/migrations/0011_junior_relationship.py b/junior/migrations/0011_junior_relationship.py new file mode 100644 index 0000000..45defb0 --- /dev/null +++ b/junior/migrations/0011_junior_relationship.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.2 on 2023-07-12 06:52 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('junior', '0010_junior_signup_method'), + ] + + operations = [ + migrations.AddField( + model_name='junior', + name='relationship', + field=models.CharField(blank=True, choices=[('1', 'parent'), ('2', 'legal_guardian')], default='1', max_length=31, null=True), + ), + ] diff --git a/junior/migrations/0012_junior_is_invited.py b/junior/migrations/0012_junior_is_invited.py new file mode 100644 index 0000000..f36f9b4 --- /dev/null +++ b/junior/migrations/0012_junior_is_invited.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.2 on 2023-07-12 10:51 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('junior', '0011_junior_relationship'), + ] + + operations = [ + migrations.AddField( + model_name='junior', + name='is_invited', + field=models.BooleanField(default=False), + ), + ] diff --git a/junior/models.py b/junior/models.py index c150d20..58b1ae3 100644 --- a/junior/models.py +++ b/junior/models.py @@ -4,7 +4,7 @@ from django.db import models from django.contrib.auth import get_user_model from django.contrib.postgres.fields import ArrayField """Import django app""" -from base.constants import GENDERS, SIGNUP_METHODS +from base.constants import GENDERS, SIGNUP_METHODS, RELATIONSHIP User = get_user_model() # Create your models here. @@ -19,6 +19,9 @@ class Junior(models.Model): gender = models.CharField(max_length=10, choices=GENDERS, null=True, blank=True, default=None) dob = models.DateField(max_length=15, null=True, blank=True, default=None) image = models.URLField(null=True, blank=True, default=None) + """relationship""" + relationship = models.CharField(max_length=31, choices=RELATIONSHIP, null=True, blank=True, + default='1') """Sign up method""" signup_method = models.CharField(max_length=31, choices=SIGNUP_METHODS, default='1') """Codes""" @@ -26,6 +29,8 @@ class Junior(models.Model): guardian_code = ArrayField(models.CharField(max_length=10, null=True, blank=True, default=None),null=True) referral_code = models.CharField(max_length=10, null=True, blank=True, default=None) referral_code_used = models.CharField(max_length=10, null=True, blank=True, default=None) + """invited junior""" + is_invited = models.BooleanField(default=False) """Profile activity""" is_active = models.BooleanField(default=True) is_complete_profile = models.BooleanField(default=False) diff --git a/junior/serializers.py b/junior/serializers.py index cf936cb..34b3653 100644 --- a/junior/serializers.py +++ b/junior/serializers.py @@ -4,12 +4,19 @@ from rest_framework import serializers from django.contrib.auth.models import User from django.db import transaction import random +from django.utils import timezone +from rest_framework_simplejwt.tokens import RefreshToken + """Import django app""" +from account.utils import send_otp_email from junior.models import Junior, JuniorPoints -from guardian.utils import upload_image_to_alibaba +from guardian.tasks import generate_otp from base.messages import ERROR_CODE, SUCCESS_CODE -from guardian.models import Guardian, JuniorTask from base.constants import PENDING, IN_PROGRESS, REJECTED, REQUESTED, COMPLETED +from guardian.models import Guardian, JuniorTask +from account.models import UserEmailOtp +from junior.utils import junior_notification_email, junior_approval_mail + class ListCharField(serializers.ListField): """Serializer for Array field""" @@ -41,9 +48,9 @@ class CreateJuniorSerializer(serializers.ModelSerializer): class Meta(object): """Meta info""" model = Junior - fields = ['first_name', 'last_name', 'email', 'phone', 'gender', 'country_code', 'dob', 'referral_code', + fields = ['id', 'first_name', 'last_name', 'email', 'phone', 'gender', 'country_code', 'dob', 'referral_code', 'passcode', 'is_complete_profile', 'guardian_code', 'referral_code_used', - 'country_name', 'image'] + 'country_name', 'image', 'is_invited'] def get_first_name(self,obj): """first name of junior""" @@ -128,7 +135,7 @@ class JuniorDetailSerializer(serializers.ModelSerializer): """Meta info""" model = Junior fields = ['id', 'email', 'first_name', 'last_name', 'country_code', 'phone', 'gender', 'dob', - 'guardian_code', 'referral_code','is_active', 'is_complete_profile', 'created_at', 'image', + 'guardian_code', 'is_invited', 'referral_code','is_active', 'is_complete_profile', 'created_at', 'image', 'updated_at'] class JuniorDetailListSerializer(serializers.ModelSerializer): @@ -195,7 +202,7 @@ class JuniorDetailListSerializer(serializers.ModelSerializer): fields = ['id', 'email', 'first_name', 'last_name', 'country_code', 'phone', 'gender', 'dob', 'guardian_code', 'referral_code','is_active', 'is_complete_profile', 'created_at', 'image', 'updated_at', 'assigned_task','points', 'pending_task', 'in_progress_task', 'completed_task', - 'requested_task', 'rejected_task', 'position'] + 'requested_task', 'rejected_task', 'position', 'is_invited'] class JuniorProfileSerializer(serializers.ModelSerializer): """junior serializer""" @@ -238,4 +245,68 @@ class JuniorProfileSerializer(serializers.ModelSerializer): fields = ['id', 'email', 'first_name', 'last_name', 'country_name', 'country_code', 'phone', 'gender', 'dob', 'guardian_code', 'referral_code','is_active', 'is_complete_profile', 'created_at', 'image', 'updated_at', 'notification_count', 'total_count', 'complete_field_count', 'signup_method', - 'passcode'] + 'is_invited', 'passcode'] + +class AddJuniorSerializer(serializers.ModelSerializer): + """Add junior serializer""" + auth_token = serializers.SerializerMethodField('get_auth_token') + + def get_auth_token(self, obj): + """auth token""" + refresh = RefreshToken.for_user(obj) + access_token = str(refresh.access_token) + return access_token + + class Meta(object): + """Meta info""" + model = Junior + fields = ['id', 'gender','dob', 'relationship', 'auth_token', 'is_invited'] + + + def create(self, validated_data): + """ create junior""" + with transaction.atomic(): + email = self.context['email'] + guardian = self.context['user'] + full_name = self.context['first_name'] + ' ' + self.context['last_name'] + guardian_data = Guardian.objects.filter(user__username=guardian).last() + user_data = User.objects.create(username=email, email=email, + first_name=self.context['first_name'], + last_name=self.context['last_name']) + + password = User.objects.make_random_password() + user_data.set_password(password) + user_data.save() + junior_data = Junior.objects.create(auth=user_data, gender=validated_data.get('gender'), + dob=validated_data.get('dob'), is_invited=True, + relationship=validated_data.get('relationship'), + junior_code=''.join([str(random.randrange(9)) for _ in range(4)]), + referral_code=''.join([str(random.randrange(9)) for _ in range(4)]), + referral_code_used=guardian_data.referral_code) + """Generate otp""" + otp_value = generate_otp() + expiry_time = timezone.now() + timezone.timedelta(days=1) + UserEmailOtp.objects.create(email=email, otp=otp_value, + user_type='1', expired_at=expiry_time) + """Send email to the register user""" + send_otp_email(email, otp_value) + """Notification email""" + junior_notification_email(email, full_name, email, password) + junior_approval_mail(guardian, full_name) + return junior_data + + + +class RemoveJuniorSerializer(serializers.ModelSerializer): + """User Update Serializer""" + class Meta(object): + """Meta class""" + model = Junior + fields = ('id', 'is_invited') + def update(self, instance, validated_data): + if instance: + instance.is_invited = False + instance.guardian_code = '{}' + instance.save() + return instance + diff --git a/junior/urls.py b/junior/urls.py index 5eb0f4c..162f693 100644 --- a/junior/urls.py +++ b/junior/urls.py @@ -1,7 +1,8 @@ """ Urls files""" """Django import""" from django.urls import path, include -from .views import UpdateJuniorProfile, ValidateGuardianCode, JuniorListAPIView +from .views import (UpdateJuniorProfile, ValidateGuardianCode, JuniorListAPIView, AddJuniorAPIView, + InvitedJuniorAPIView, FilterJuniorAPIView, RemoveJuniorAPIView) """Third party import""" from rest_framework import routers @@ -15,7 +16,14 @@ router.register('create-junior-profile', UpdateJuniorProfile, basename='profile- router.register('validate-guardian-code', ValidateGuardianCode, basename='validate-guardian-code') """junior list API""" router.register('junior-list', JuniorListAPIView, basename='junior-list') +"""Add junior list API""" +router.register('add-junior', AddJuniorAPIView, basename='add-junior') +"""Invited junior list API""" +router.register('invited-junior', InvitedJuniorAPIView, basename='invited-junior') +"""Filter junior list API""" +router.register('filter-junior', FilterJuniorAPIView, basename='filter-junior') """Define url pattern""" urlpatterns = [ path('api/v1/', include(router.urls)), + path('api/v1/remove-junior/', RemoveJuniorAPIView.as_view()) ] diff --git a/junior/utils.py b/junior/utils.py new file mode 100644 index 0000000..7d19a2d --- /dev/null +++ b/junior/utils.py @@ -0,0 +1,51 @@ +"""Account utils""" +"""Import django""" +from django.conf import settings +from rest_framework import viewsets, status +from rest_framework.response import Response +"""Third party Django app""" +from templated_email import send_templated_mail +import jwt +import string +from datetime import datetime +from calendar import timegm +from uuid import uuid4 +import secrets +from rest_framework import serializers +from junior.models import Junior +from guardian.models import Guardian +from account.models import UserDelete +from base.messages import ERROR_CODE + + + +def junior_notification_email(recipient_email, full_name, email, password): + """Notification email""" + from_email = settings.EMAIL_FROM_ADDRESS + recipient_list = [recipient_email] + send_templated_mail( + template_name='junior_notification_email.email', + from_email=from_email, + recipient_list=recipient_list, + context={ + 'full_name': full_name, + 'url':'link', + 'email': email, + 'password': password + } + ) + return full_name + +def junior_approval_mail(guardian, full_name): + """junior approval mail""" + from_email = settings.EMAIL_FROM_ADDRESS + recipient_list = [guardian] + send_templated_mail( + template_name='junior_approval_mail.email', + from_email=from_email, + recipient_list=recipient_list, + context={ + 'full_name': full_name + } + ) + return full_name diff --git a/junior/views.py b/junior/views.py index fa3b57c..bea0525 100644 --- a/junior/views.py +++ b/junior/views.py @@ -1,9 +1,12 @@ """Junior view file""" -from rest_framework import viewsets, status +from rest_framework import viewsets, status, generics,views from rest_framework.permissions import IsAuthenticated +from rest_framework.pagination import PageNumberPagination +from rest_framework.response import Response """Django app import""" from junior.models import Junior -from .serializers import CreateJuniorSerializer, JuniorDetailListSerializer +from .serializers import (CreateJuniorSerializer, JuniorDetailListSerializer, AddJuniorSerializer,\ + RemoveJuniorSerializer) from guardian.models import Guardian from base.messages import ERROR_CODE, SUCCESS_CODE from account.utils import custom_response, custom_error_response @@ -61,3 +64,85 @@ class JuniorListAPIView(viewsets.ModelViewSet): queryset = Junior.objects.filter(guardian_code__icontains=str(guardian_data.guardian_code)) serializer = JuniorDetailListSerializer(queryset, many=True) return custom_response(None, serializer.data, response_status=status.HTTP_200_OK) + +class AddJuniorAPIView(viewsets.ModelViewSet): + """Add Junior by guardian""" + queryset = Junior.objects.all() + serializer_class = AddJuniorSerializer + permission_classes = [IsAuthenticated] + def create(self, request, *args, **kwargs): + """ junior list""" + info = {'user': request.user, 'email': request.data['email'], 'first_name': request.data['first_name'], + 'last_name': request.data['last_name']} + serializer = AddJuniorSerializer(data=request.data, context=info) + if serializer.is_valid(): + serializer.save() + return custom_response(SUCCESS_CODE['3021'], serializer.data, response_status=status.HTTP_200_OK) + return custom_error_response(serializer.error, response_status=status.HTTP_400_BAD_REQUEST) + +class InvitedJuniorAPIView(viewsets.ModelViewSet): + """Junior list of assosicated guardian""" + + serializer_class = JuniorDetailListSerializer + queryset = Junior.objects.all() + permission_classes = [IsAuthenticated] + pagination_class = PageNumberPagination + + def get_queryset(self): + """Get the queryset for the view""" + guardian = Guardian.objects.filter(user__email=self.request.user).last() + junior_queryset = Junior.objects.filter(guardian_code__icontains=str(guardian.guardian_code), + is_invited=True) + return junior_queryset + def list(self, request, *args, **kwargs): + """ junior list""" + queryset = self.get_queryset() + paginator = self.pagination_class() + paginated_queryset = paginator.paginate_queryset(queryset, request) + serializer = JuniorDetailListSerializer(paginated_queryset, many=True) + return custom_response(None, serializer.data, response_status=status.HTTP_200_OK) + + +class FilterJuniorAPIView(viewsets.ModelViewSet): + """Update guardian profile""" + serializer_class = JuniorDetailListSerializer + permission_classes = [IsAuthenticated] + pagination_class = PageNumberPagination + queryset = Junior.objects.all() + + def get_queryset(self): + """Get the queryset for the view""" + title = self.request.GET.get('title') + guardian_data = Guardian.objects.filter(user__email=self.request.user).last() + queryset = Junior.objects.filter(guardian_code__icontains=str(guardian_data.guardian_code), + is_invited=True, auth__first_name=title) + return queryset + + def list(self, request, *args, **kwargs): + """Create guardian profile""" + queryset = self.get_queryset() + paginator = self.pagination_class() + paginated_queryset = paginator.paginate_queryset(queryset, request) + serializer = JuniorDetailListSerializer(paginated_queryset, many=True) + return custom_response(None, serializer.data, response_status=status.HTTP_200_OK) + + +class RemoveJuniorAPIView(views.APIView): + """Eater Update API""" + serializer_class = RemoveJuniorSerializer + model = Junior + permission_classes = [IsAuthenticated] + + def put(self, request, format=None): + junior_id = self.request.GET.get('id') + guardian = Guardian.objects.filter(user__email=self.request.user).last() + junior_queryset = Junior.objects.filter(id=junior_id, guardian_code__icontains=str(guardian.guardian_code), + is_invited=True).last() + if junior_queryset: + serializer = RemoveJuniorSerializer(junior_queryset, data=request.data, partial=True) + if serializer.is_valid(): + serializer.save() + return custom_response(SUCCESS_CODE['3022'], serializer.data, response_status=status.HTTP_200_OK) + return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST) + else: + return custom_error_response(ERROR_CODE['2034'], response_status=status.HTTP_400_BAD_REQUEST) diff --git a/zod_bank/settings.py b/zod_bank/settings.py index f7b79dc..108542c 100644 --- a/zod_bank/settings.py +++ b/zod_bank/settings.py @@ -154,10 +154,6 @@ AUTH_PASSWORD_VALIDATORS = [ }, ] -JWT_AUTH = { - # Other JWT authentication settings - 'JWT_AUTHENTICATION': 'your_app.authentication.CustomJWTAuthentication', -} # Internationalization # https://docs.djangoproject.com/en/3.0/topics/i18n/