mirror of
https://github.com/HamzaSha1/zod-backend.git
synced 2025-11-27 00:54:54 +00:00
@ -2,7 +2,7 @@
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
"""Import django app"""
|
"""Import django app"""
|
||||||
from .models import UserEmailOtp, UserPhoneOtp, DefaultTaskImages, UserNotification, UserDelete
|
from .models import UserEmailOtp, DefaultTaskImages, UserNotification, UserDelete
|
||||||
# Register your models here.
|
# Register your models here.
|
||||||
|
|
||||||
@admin.register(UserDelete)
|
@admin.register(UserDelete)
|
||||||
@ -37,11 +37,3 @@ class UserEmailOtpAdmin(admin.ModelAdmin):
|
|||||||
"""Return object in email and otp format"""
|
"""Return object in email and otp format"""
|
||||||
return self.email + '-' + self.otp
|
return self.email + '-' + self.otp
|
||||||
|
|
||||||
@admin.register(UserPhoneOtp)
|
|
||||||
class UserPhoneOtpAdmin(admin.ModelAdmin):
|
|
||||||
"""User Phone otp admin"""
|
|
||||||
list_display = ['phone']
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
"""Return object in phone number and otp format"""
|
|
||||||
return self.phone + '-' + self.otp
|
|
||||||
|
|||||||
@ -0,0 +1,21 @@
|
|||||||
|
# Generated by Django 4.2.2 on 2023-07-14 09:34
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('account', '0006_alter_useremailotp_options_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='defaulttaskimages',
|
||||||
|
options={'verbose_name': 'Default Task images', 'verbose_name_plural': 'Default Task images'},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='userdelete',
|
||||||
|
options={'verbose_name': 'Deleted User', 'verbose_name_plural': 'Deleted User'},
|
||||||
|
),
|
||||||
|
]
|
||||||
@ -90,6 +90,8 @@ class DefaultTaskImages(models.Model):
|
|||||||
class Meta(object):
|
class Meta(object):
|
||||||
""" Meta information """
|
""" Meta information """
|
||||||
db_table = 'default_task_image'
|
db_table = 'default_task_image'
|
||||||
|
verbose_name = 'Default Task images'
|
||||||
|
verbose_name_plural = 'Default Task images'
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
"""return phone as an object"""
|
"""return phone as an object"""
|
||||||
@ -112,6 +114,8 @@ class UserDelete(models.Model):
|
|||||||
class Meta(object):
|
class Meta(object):
|
||||||
""" Meta information """
|
""" Meta information """
|
||||||
db_table = 'user_delete_information'
|
db_table = 'user_delete_information'
|
||||||
|
verbose_name = 'Deleted User'
|
||||||
|
verbose_name_plural = 'Deleted User'
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.user.email
|
return self.user.email
|
||||||
|
|||||||
@ -125,12 +125,17 @@ class GuardianSerializer(serializers.ModelSerializer):
|
|||||||
first_name = serializers.SerializerMethodField('get_first_name')
|
first_name = serializers.SerializerMethodField('get_first_name')
|
||||||
last_name = serializers.SerializerMethodField('get_last_name')
|
last_name = serializers.SerializerMethodField('get_last_name')
|
||||||
auth_token = serializers.SerializerMethodField('get_auth_token')
|
auth_token = serializers.SerializerMethodField('get_auth_token')
|
||||||
|
refresh_token = serializers.SerializerMethodField('get_refresh_token')
|
||||||
|
|
||||||
def get_auth_token(self, obj):
|
def get_auth_token(self, obj):
|
||||||
refresh = RefreshToken.for_user(obj.user)
|
refresh = RefreshToken.for_user(obj.user)
|
||||||
access_token = str(refresh.access_token)
|
access_token = str(refresh.access_token)
|
||||||
return access_token
|
return access_token
|
||||||
|
|
||||||
|
def get_refresh_token(self, obj):
|
||||||
|
refresh = RefreshToken.for_user(obj.user)
|
||||||
|
refresh_token = str(refresh)
|
||||||
|
return refresh_token
|
||||||
|
|
||||||
def get_user_type(self, obj):
|
def get_user_type(self, obj):
|
||||||
"""user type"""
|
"""user type"""
|
||||||
@ -154,9 +159,9 @@ class GuardianSerializer(serializers.ModelSerializer):
|
|||||||
class Meta(object):
|
class Meta(object):
|
||||||
"""Meta info"""
|
"""Meta info"""
|
||||||
model = Guardian
|
model = Guardian
|
||||||
fields = ['id', 'auth_token', 'email', 'first_name', 'last_name', 'country_code', 'phone', 'family_name',
|
fields = ['id', 'auth_token', 'refresh_token', 'email', 'first_name', 'last_name', 'country_code',
|
||||||
'gender', 'dob', 'referral_code', 'is_active', 'is_complete_profile', 'passcode', 'image',
|
'phone', 'family_name', 'gender', 'dob', 'referral_code', 'is_active',
|
||||||
'created_at', 'updated_at', 'user_type', 'country_name']
|
'is_complete_profile', 'passcode', 'image', 'created_at', 'updated_at', 'user_type', 'country_name']
|
||||||
|
|
||||||
|
|
||||||
class JuniorSerializer(serializers.ModelSerializer):
|
class JuniorSerializer(serializers.ModelSerializer):
|
||||||
@ -166,15 +171,21 @@ class JuniorSerializer(serializers.ModelSerializer):
|
|||||||
first_name = serializers.SerializerMethodField('get_first_name')
|
first_name = serializers.SerializerMethodField('get_first_name')
|
||||||
last_name = serializers.SerializerMethodField('get_last_name')
|
last_name = serializers.SerializerMethodField('get_last_name')
|
||||||
auth_token = serializers.SerializerMethodField('get_auth_token')
|
auth_token = serializers.SerializerMethodField('get_auth_token')
|
||||||
|
refresh_token = serializers.SerializerMethodField('get_refresh_token')
|
||||||
|
|
||||||
def get_auth_token(self, obj):
|
def get_auth_token(self, obj):
|
||||||
refresh = RefreshToken.for_user(obj.auth)
|
refresh = RefreshToken.for_user(obj.auth)
|
||||||
access_token = str(refresh.access_token)
|
access_token = str(refresh.access_token)
|
||||||
return access_token
|
return access_token
|
||||||
|
|
||||||
|
def get_refresh_token(self, obj):
|
||||||
|
refresh = RefreshToken.for_user(obj.user)
|
||||||
|
refresh_token = str(refresh)
|
||||||
|
return refresh_token
|
||||||
|
|
||||||
def get_user_type(self, obj):
|
def get_user_type(self, obj):
|
||||||
email_verified = UserEmailOtp.objects.filter(email=obj.auth.username).last()
|
email_verified = UserEmailOtp.objects.filter(email=obj.auth.username).last()
|
||||||
if email_verified and email_verified.user_type != None:
|
if email_verified and email_verified.user_type is not None:
|
||||||
return email_verified.user_type
|
return email_verified.user_type
|
||||||
return '1'
|
return '1'
|
||||||
|
|
||||||
@ -190,9 +201,9 @@ class JuniorSerializer(serializers.ModelSerializer):
|
|||||||
class Meta(object):
|
class Meta(object):
|
||||||
"""Meta info"""
|
"""Meta info"""
|
||||||
model = Junior
|
model = Junior
|
||||||
fields = ['id', 'auth_token', 'email', 'first_name', 'last_name', 'country_code', 'phone', 'gender', 'dob',
|
fields = ['id', 'auth_token', 'refresh_token', 'email', 'first_name', 'last_name', 'country_code',
|
||||||
'guardian_code', 'referral_code','is_active', 'is_complete_profile', 'created_at', 'image',
|
'phone', 'gender', 'dob', 'guardian_code', 'referral_code','is_active',
|
||||||
'updated_at', 'user_type', 'country_name','is_invited']
|
'is_complete_profile', 'created_at', 'image', 'updated_at', 'user_type', 'country_name','is_invited']
|
||||||
|
|
||||||
class EmailVerificationSerializer(serializers.ModelSerializer):
|
class EmailVerificationSerializer(serializers.ModelSerializer):
|
||||||
"""Email verification serializer"""
|
"""Email verification serializer"""
|
||||||
|
|||||||
@ -8,7 +8,7 @@ from .views import (UserLogin, SendPhoneOtp, UserPhoneVerification, UserEmailVer
|
|||||||
ForgotPasswordAPIView, ResetPasswordAPIView, ChangePasswordAPIView, UpdateProfileImage,
|
ForgotPasswordAPIView, ResetPasswordAPIView, ChangePasswordAPIView, UpdateProfileImage,
|
||||||
GoogleLoginViewSet, SigninWithApple, ProfileAPIViewSet, UploadImageAPIViewSet,
|
GoogleLoginViewSet, SigninWithApple, ProfileAPIViewSet, UploadImageAPIViewSet,
|
||||||
DefaultImageAPIViewSet, DeleteUserProfileAPIViewSet, UserNotificationAPIViewSet,
|
DefaultImageAPIViewSet, DeleteUserProfileAPIViewSet, UserNotificationAPIViewSet,
|
||||||
UpdateUserNotificationAPIViewSet, SendSupportEmail, LogoutAPIView)
|
UpdateUserNotificationAPIViewSet, SendSupportEmail, LogoutAPIView, AccessTokenAPIView)
|
||||||
"""Router"""
|
"""Router"""
|
||||||
router = routers.SimpleRouter()
|
router = routers.SimpleRouter()
|
||||||
|
|
||||||
@ -45,5 +45,6 @@ urlpatterns = [
|
|||||||
path('api/v1/update-profile-image/', UpdateProfileImage.as_view()),
|
path('api/v1/update-profile-image/', UpdateProfileImage.as_view()),
|
||||||
path('api/v1/apple-login/', SigninWithApple.as_view(), name='signup_with_apple'),
|
path('api/v1/apple-login/', SigninWithApple.as_view(), name='signup_with_apple'),
|
||||||
path('api/v1/send-support-email/', SendSupportEmail.as_view(), name='send-support-email'),
|
path('api/v1/send-support-email/', SendSupportEmail.as_view(), name='send-support-email'),
|
||||||
path('api/v1/logout/', LogoutAPIView.as_view(), name='logout')
|
path('api/v1/logout/', LogoutAPIView.as_view(), name='logout'),
|
||||||
|
path('api/v1/generate-token/', AccessTokenAPIView.as_view(), name='generate-token')
|
||||||
]
|
]
|
||||||
|
|||||||
@ -183,13 +183,15 @@ def get_token(data: dict = dict):
|
|||||||
|
|
||||||
|
|
||||||
def generate_alphanumeric_code(length):
|
def generate_alphanumeric_code(length):
|
||||||
|
"""Generate alphanumeric code"""
|
||||||
alphabet = string.ascii_letters + string.digits
|
alphabet = string.ascii_letters + string.digits
|
||||||
code = ''.join(secrets.choice(alphabet) for _ in range(length))
|
code = ''.join(secrets.choice(alphabet) for _ in range(length))
|
||||||
return code
|
return code
|
||||||
|
|
||||||
|
|
||||||
def generate_code(value, user_id):
|
def generate_code(value, user_id):
|
||||||
alphabet = value + user_id.zfill(3)
|
"""generate referral, junior and guardian code"""
|
||||||
return alphabet
|
code = value + str(user_id).zfill(3)
|
||||||
|
return code
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -25,10 +25,10 @@ from .serializers import (SuperUserSerializer, GuardianSerializer, JuniorSeriali
|
|||||||
UserNotificationSerializer, UpdateUserNotificationSerializer, UserPhoneOtpSerializer)
|
UserNotificationSerializer, UpdateUserNotificationSerializer, UserPhoneOtpSerializer)
|
||||||
from rest_framework_simplejwt.tokens import RefreshToken
|
from rest_framework_simplejwt.tokens import RefreshToken
|
||||||
from base.messages import ERROR_CODE, SUCCESS_CODE
|
from base.messages import ERROR_CODE, SUCCESS_CODE
|
||||||
from base.constants import NUMBER
|
from base.constants import NUMBER, ZOD, JUN, GRD
|
||||||
from guardian.tasks import generate_otp
|
from guardian.tasks import generate_otp
|
||||||
from account.utils import (send_otp_email, send_support_email, custom_response, custom_error_response,
|
from account.utils import (send_otp_email, send_support_email, custom_response, custom_error_response,
|
||||||
generate_alphanumeric_code)
|
generate_code)
|
||||||
from rest_framework.permissions import IsAuthenticated
|
from rest_framework.permissions import IsAuthenticated
|
||||||
from templated_email import send_templated_mail
|
from templated_email import send_templated_mail
|
||||||
import google.oauth2.credentials
|
import google.oauth2.credentials
|
||||||
@ -37,11 +37,13 @@ from rest_framework import status
|
|||||||
import requests
|
import requests
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from rest_framework_simplejwt.tokens import RefreshToken
|
||||||
from junior.serializers import JuniorProfileSerializer
|
from junior.serializers import JuniorProfileSerializer
|
||||||
from guardian.serializers import GuardianProfileSerializer
|
from guardian.serializers import GuardianProfileSerializer
|
||||||
|
|
||||||
class GoogleLoginMixin:
|
class GoogleLoginMixin:
|
||||||
"""google login mixin"""
|
"""google login mixin"""
|
||||||
|
@staticmethod
|
||||||
def google_login(self, request):
|
def google_login(self, request):
|
||||||
"""google login function"""
|
"""google login function"""
|
||||||
access_token = request.data.get('access_token')
|
access_token = request.data.get('access_token')
|
||||||
@ -90,15 +92,15 @@ class GoogleLoginMixin:
|
|||||||
if str(user_type) == '1':
|
if str(user_type) == '1':
|
||||||
junior_query = Junior.objects.create(auth=user_obj, is_verified=True, is_active=True,
|
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=generate_alphanumeric_code(6),
|
junior_code=generate_code(JUN, user_obj.id),
|
||||||
referral_code=generate_alphanumeric_code(6)
|
referral_code=generate_code(ZOD, user_obj.id)
|
||||||
)
|
)
|
||||||
serializer = JuniorSerializer(junior_query)
|
serializer = JuniorSerializer(junior_query)
|
||||||
if str(user_type) == '2':
|
if str(user_type) == '2':
|
||||||
guardian_query = Guardian.objects.create(user=user_obj, is_verified=True, is_active=True,
|
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=generate_alphanumeric_code(6),
|
guardian_code=generate_code(GRD, user_obj.id),
|
||||||
referral_code=generate_alphanumeric_code(6)
|
referral_code=generate_code(ZOD, user_obj.id)
|
||||||
)
|
)
|
||||||
serializer = GuardianSerializer(guardian_query)
|
serializer = GuardianSerializer(guardian_query)
|
||||||
# Return a JSON response with the user's email and name
|
# Return a JSON response with the user's email and name
|
||||||
@ -141,14 +143,14 @@ class SigninWithApple(views.APIView):
|
|||||||
if str(user_type) == '1':
|
if str(user_type) == '1':
|
||||||
junior_query = Junior.objects.create(auth=user, is_verified=True, is_active=True,
|
junior_query = Junior.objects.create(auth=user, is_verified=True, is_active=True,
|
||||||
signup_method='3',
|
signup_method='3',
|
||||||
junior_code=generate_alphanumeric_code(6),
|
junior_code=generate_code(JUN, user.id),
|
||||||
referral_code=generate_alphanumeric_code(6))
|
referral_code=generate_code(ZOD, user.id))
|
||||||
serializer = JuniorSerializer(junior_query)
|
serializer = JuniorSerializer(junior_query)
|
||||||
if str(user_type) == '2':
|
if str(user_type) == '2':
|
||||||
guardian_query = Guardian.objects.create(user=user, is_verified=True, is_active=True,
|
guardian_query = Guardian.objects.create(user=user, is_verified=True, is_active=True,
|
||||||
signup_method='3',
|
signup_method='3',
|
||||||
guardian_code=generate_alphanumeric_code(6),
|
guardian_code=generate_code(GRD, user.id),
|
||||||
referral_code=generate_alphanumeric_code(6))
|
referral_code=generate_code(ZOD, user.id))
|
||||||
serializer = GuardianSerializer(guardian_query)
|
serializer = GuardianSerializer(guardian_query)
|
||||||
return custom_response(SUCCESS_CODE['3003'], serializer.data,
|
return custom_response(SUCCESS_CODE['3003'], serializer.data,
|
||||||
response_status=status.HTTP_200_OK)
|
response_status=status.HTTP_200_OK)
|
||||||
@ -181,6 +183,7 @@ class UpdateProfileImage(views.APIView):
|
|||||||
return custom_response(SUCCESS_CODE['3017'], serializer.data, response_status=status.HTTP_200_OK)
|
return custom_response(SUCCESS_CODE['3017'], serializer.data, response_status=status.HTTP_200_OK)
|
||||||
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
|
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
logging.error(e)
|
||||||
return custom_error_response(ERROR_CODE['2036'],response_status=status.HTTP_400_BAD_REQUEST)
|
return custom_error_response(ERROR_CODE['2036'],response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
class ChangePasswordAPIView(views.APIView):
|
class ChangePasswordAPIView(views.APIView):
|
||||||
@ -300,7 +303,8 @@ class UserLogin(viewsets.ViewSet):
|
|||||||
email_verified = UserEmailOtp.objects.filter(email=username).last()
|
email_verified = UserEmailOtp.objects.filter(email=username).last()
|
||||||
refresh = RefreshToken.for_user(user)
|
refresh = RefreshToken.for_user(user)
|
||||||
access_token = str(refresh.access_token)
|
access_token = str(refresh.access_token)
|
||||||
data = {"auth_token":access_token, "is_profile_complete": False,
|
refresh_token = str(refresh)
|
||||||
|
data = {"auth_token":access_token, "refresh_token":refresh_token, "is_profile_complete": False,
|
||||||
"user_type": email_verified.user_type,
|
"user_type": email_verified.user_type,
|
||||||
}
|
}
|
||||||
is_verified = False
|
is_verified = False
|
||||||
@ -334,7 +338,8 @@ class UserLogin(viewsets.ViewSet):
|
|||||||
logging.error(e)
|
logging.error(e)
|
||||||
refresh = RefreshToken.for_user(user)
|
refresh = RefreshToken.for_user(user)
|
||||||
access_token = str(refresh.access_token)
|
access_token = str(refresh.access_token)
|
||||||
data = {"auth_token": access_token, "user_role": '3'}
|
refresh_token = str(refresh)
|
||||||
|
data = {"auth_token": access_token, "refresh_token":refresh_token, "user_type": '3'}
|
||||||
return custom_response(None, data, response_status=status.HTTP_200_OK)
|
return custom_response(None, data, response_status=status.HTTP_200_OK)
|
||||||
|
|
||||||
class UserEmailVerification(viewsets.ModelViewSet):
|
class UserEmailVerification(viewsets.ModelViewSet):
|
||||||
@ -371,7 +376,8 @@ class UserEmailVerification(viewsets.ModelViewSet):
|
|||||||
guardian_data.save()
|
guardian_data.save()
|
||||||
refresh = RefreshToken.for_user(user_obj)
|
refresh = RefreshToken.for_user(user_obj)
|
||||||
access_token = str(refresh.access_token)
|
access_token = str(refresh.access_token)
|
||||||
return custom_response(SUCCESS_CODE['3011'], {"auth_token":access_token},
|
refresh_token = str(refresh)
|
||||||
|
return custom_response(SUCCESS_CODE['3011'], {"auth_token":access_token, "refresh_token":refresh_token},
|
||||||
response_status=status.HTTP_200_OK)
|
response_status=status.HTTP_200_OK)
|
||||||
else:
|
else:
|
||||||
return custom_error_response(ERROR_CODE["2008"], response_status=status.HTTP_400_BAD_REQUEST)
|
return custom_error_response(ERROR_CODE["2008"], response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
@ -512,9 +518,24 @@ class SendSupportEmail(views.APIView):
|
|||||||
|
|
||||||
|
|
||||||
class LogoutAPIView(views.APIView):
|
class LogoutAPIView(views.APIView):
|
||||||
|
"""Log out API"""
|
||||||
permission_classes = (IsAuthenticated,)
|
permission_classes = (IsAuthenticated,)
|
||||||
|
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
logout(request)
|
logout(request)
|
||||||
request.session.flush()
|
request.session.flush()
|
||||||
return custom_response(SUCCESS_CODE['3020'], response_status=status.HTTP_200_OK)
|
return custom_response(SUCCESS_CODE['3020'], response_status=status.HTTP_200_OK)
|
||||||
|
|
||||||
|
|
||||||
|
class AccessTokenAPIView(views.APIView):
|
||||||
|
"""generate access token API"""
|
||||||
|
|
||||||
|
def post(self, request):
|
||||||
|
# Assuming you have a refresh_token string
|
||||||
|
refresh_token = request.data['refresh_token']
|
||||||
|
# Create a RefreshToken instance from the refresh token string
|
||||||
|
refresh = RefreshToken(refresh_token)
|
||||||
|
# Generate a new access token
|
||||||
|
access_token = str(refresh.access_token)
|
||||||
|
data = {"auth_token": access_token}
|
||||||
|
return custom_response(None, data, response_status=status.HTTP_200_OK)
|
||||||
|
|||||||
@ -6,6 +6,9 @@ import os
|
|||||||
# GOOGLE_URL used for interact with google server to verify user existence.
|
# GOOGLE_URL used for interact with google server to verify user existence.
|
||||||
#GOOGLE_URL = "https://www.googleapis.com/plus/v1/"
|
#GOOGLE_URL = "https://www.googleapis.com/plus/v1/"
|
||||||
|
|
||||||
|
ZOD = 'ZOD'
|
||||||
|
JUN = 'JUN'
|
||||||
|
GRD = 'GRD'
|
||||||
NUMBER = {
|
NUMBER = {
|
||||||
'point_zero': 0.0,
|
'point_zero': 0.0,
|
||||||
'zero': 0,
|
'zero': 0,
|
||||||
|
|||||||
@ -61,6 +61,7 @@ ERROR_CODE = {
|
|||||||
"2035": "Image should not be 0 kb",
|
"2035": "Image should not be 0 kb",
|
||||||
"2036": "Choose valid user"
|
"2036": "Choose valid user"
|
||||||
}
|
}
|
||||||
|
"""Success message code"""
|
||||||
SUCCESS_CODE = {
|
SUCCESS_CODE = {
|
||||||
# Success code for password
|
# Success code for password
|
||||||
"3001": "Sign up successfully",
|
"3001": "Sign up successfully",
|
||||||
@ -97,7 +98,7 @@ SUCCESS_CODE = {
|
|||||||
"3023": "Approved junior successfully",
|
"3023": "Approved junior successfully",
|
||||||
"3024": "Reject junior request successfully"
|
"3024": "Reject junior request successfully"
|
||||||
}
|
}
|
||||||
|
"""status code error"""
|
||||||
STATUS_CODE_ERROR = {
|
STATUS_CODE_ERROR = {
|
||||||
# Status code for Invalid Input
|
# Status code for Invalid Input
|
||||||
"4001": ["Invalid input."],
|
"4001": ["Invalid input."],
|
||||||
|
|||||||
@ -0,0 +1,21 @@
|
|||||||
|
# Generated by Django 4.2.2 on 2023-07-14 09:34
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('guardian', '0014_guardian_signup_method'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='guardian',
|
||||||
|
options={'verbose_name': 'Guardian', 'verbose_name_plural': 'Guardian'},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='juniortask',
|
||||||
|
options={'verbose_name': 'Junior Task', 'verbose_name_plural': 'Junior Task'},
|
||||||
|
),
|
||||||
|
]
|
||||||
@ -4,6 +4,7 @@ from django.db import models
|
|||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
"""Import Django app"""
|
"""Import Django app"""
|
||||||
from base.constants import GENDERS, TASK_STATUS, PENDING, TASK_POINTS, SIGNUP_METHODS
|
from base.constants import GENDERS, TASK_STATUS, PENDING, TASK_POINTS, SIGNUP_METHODS
|
||||||
|
"""import Junior model"""
|
||||||
from junior.models import Junior
|
from junior.models import Junior
|
||||||
"""Add user model"""
|
"""Add user model"""
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
@ -15,23 +16,31 @@ class Guardian(models.Model):
|
|||||||
"""Contact details"""
|
"""Contact details"""
|
||||||
country_code = models.IntegerField(blank=True, null=True)
|
country_code = models.IntegerField(blank=True, null=True)
|
||||||
phone = models.CharField(max_length=31, null=True, blank=True, default=None)
|
phone = models.CharField(max_length=31, null=True, blank=True, default=None)
|
||||||
|
"""country name of the guardian"""
|
||||||
country_name = models.CharField(max_length=100, null=True, blank=True, default=None)
|
country_name = models.CharField(max_length=100, null=True, blank=True, default=None)
|
||||||
"""Image info"""
|
"""Image info"""
|
||||||
image = models.URLField(null=True, blank=True, default=None)
|
image = models.URLField(null=True, blank=True, default=None)
|
||||||
"""Personal info"""
|
"""Personal info"""
|
||||||
family_name = models.CharField(max_length=50, null=True, blank=True, default=None)
|
family_name = models.CharField(max_length=50, null=True, blank=True, default=None)
|
||||||
|
"""gender of the guardian"""
|
||||||
gender = models.CharField(choices=GENDERS, max_length=15, null=True, blank=True, default=None)
|
gender = models.CharField(choices=GENDERS, max_length=15, null=True, blank=True, default=None)
|
||||||
|
"""date of birth of the guardian"""
|
||||||
dob = models.DateField(max_length=15, null=True, blank=True, default=None)
|
dob = models.DateField(max_length=15, null=True, blank=True, default=None)
|
||||||
"""Profile activity"""
|
"""Profile activity"""
|
||||||
is_active = models.BooleanField(default=True)
|
is_active = models.BooleanField(default=True)
|
||||||
|
"""guardian is verified or not"""
|
||||||
is_verified = models.BooleanField(default=False)
|
is_verified = models.BooleanField(default=False)
|
||||||
|
"""guardian profile is complete or not"""
|
||||||
is_complete_profile = models.BooleanField(default=False)
|
is_complete_profile = models.BooleanField(default=False)
|
||||||
|
"""passcode of the guardian profile"""
|
||||||
passcode = models.IntegerField(null=True, blank=True, default=None)
|
passcode = models.IntegerField(null=True, blank=True, default=None)
|
||||||
"""Sign up method"""
|
"""Sign up method"""
|
||||||
signup_method = models.CharField(max_length=31, choices=SIGNUP_METHODS, default='1')
|
signup_method = models.CharField(max_length=31, choices=SIGNUP_METHODS, default='1')
|
||||||
"""Codes"""
|
"""Guardian Codes"""
|
||||||
guardian_code = models.CharField(max_length=10, null=True, blank=True, default=None)
|
guardian_code = models.CharField(max_length=10, null=True, blank=True, default=None)
|
||||||
|
"""Referral code"""
|
||||||
referral_code = models.CharField(max_length=10, null=True, blank=True, default=None)
|
referral_code = models.CharField(max_length=10, null=True, blank=True, default=None)
|
||||||
|
"""Referral code that is used by guardian while signup"""
|
||||||
referral_code_used = 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)
|
||||||
"""Profile created and updated time"""
|
"""Profile created and updated time"""
|
||||||
created_at = models.DateTimeField(auto_now_add=True)
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
@ -40,7 +49,10 @@ class Guardian(models.Model):
|
|||||||
class Meta(object):
|
class Meta(object):
|
||||||
""" Meta class """
|
""" Meta class """
|
||||||
db_table = 'guardians'
|
db_table = 'guardians'
|
||||||
|
"""verbose name of the model"""
|
||||||
verbose_name = 'Guardian'
|
verbose_name = 'Guardian'
|
||||||
|
"""change another name"""
|
||||||
|
verbose_name_plural = 'Guardian'
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
"""Return email id"""
|
"""Return email id"""
|
||||||
@ -51,19 +63,23 @@ class JuniorTask(models.Model):
|
|||||||
guardian = models.ForeignKey(Guardian, on_delete=models.CASCADE, related_name='guardian', verbose_name='Guardian')
|
guardian = models.ForeignKey(Guardian, on_delete=models.CASCADE, related_name='guardian', verbose_name='Guardian')
|
||||||
"""task details"""
|
"""task details"""
|
||||||
task_name = models.CharField(max_length=100)
|
task_name = models.CharField(max_length=100)
|
||||||
|
"""task description"""
|
||||||
task_description = models.CharField(max_length=500)
|
task_description = models.CharField(max_length=500)
|
||||||
"""points of the task"""
|
"""points of the task"""
|
||||||
points = models.IntegerField(default=TASK_POINTS)
|
points = models.IntegerField(default=TASK_POINTS)
|
||||||
|
"""last date of the task"""
|
||||||
due_date = models.DateField(auto_now_add=False, null=True, blank=True)
|
due_date = models.DateField(auto_now_add=False, null=True, blank=True)
|
||||||
"""Images of task"""
|
"""Images of task that is upload by guardian"""
|
||||||
default_image = models.URLField(null=True, blank=True, default=None)
|
default_image = models.URLField(null=True, blank=True, default=None)
|
||||||
|
"""image that is uploaded by junior"""
|
||||||
image = models.URLField(null=True, blank=True, default=None)
|
image = models.URLField(null=True, blank=True, default=None)
|
||||||
"""associated junior with the task"""
|
"""associated junior with the task"""
|
||||||
junior = models.ForeignKey(Junior, on_delete=models.CASCADE, related_name='junior', verbose_name='Junior')
|
junior = models.ForeignKey(Junior, on_delete=models.CASCADE, related_name='junior', verbose_name='Junior')
|
||||||
"""task status"""
|
"""task status"""
|
||||||
task_status = models.CharField(choices=TASK_STATUS, max_length=15, default=PENDING)
|
task_status = models.CharField(choices=TASK_STATUS, max_length=15, default=PENDING)
|
||||||
"""task stage"""
|
"""task is active or not"""
|
||||||
is_active = models.BooleanField(default=True)
|
is_active = models.BooleanField(default=True)
|
||||||
|
"""Task is approved or not"""
|
||||||
is_approved = models.BooleanField(default=False)
|
is_approved = models.BooleanField(default=False)
|
||||||
"""Profile created and updated time"""
|
"""Profile created and updated time"""
|
||||||
created_at = models.DateTimeField(auto_now_add=True)
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
@ -72,7 +88,9 @@ class JuniorTask(models.Model):
|
|||||||
class Meta(object):
|
class Meta(object):
|
||||||
""" Meta class """
|
""" Meta class """
|
||||||
db_table = 'junior_task'
|
db_table = 'junior_task'
|
||||||
|
"""verbose name of the model"""
|
||||||
verbose_name = 'Junior Task'
|
verbose_name = 'Junior Task'
|
||||||
|
verbose_name_plural = 'Junior Task'
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
"""Return email id"""
|
"""Return email id"""
|
||||||
|
|||||||
@ -9,11 +9,11 @@ from django.contrib.auth.models import User
|
|||||||
"""Import Django app"""
|
"""Import Django app"""
|
||||||
from .models import Guardian, JuniorTask
|
from .models import Guardian, JuniorTask
|
||||||
from account.models import UserProfile, UserEmailOtp, UserNotification
|
from account.models import UserProfile, UserEmailOtp, UserNotification
|
||||||
from account.utils import generate_alphanumeric_code
|
from account.utils import generate_code
|
||||||
from account.serializers import JuniorSerializer
|
from account.serializers import JuniorSerializer
|
||||||
from junior.serializers import JuniorDetailSerializer
|
from junior.serializers import JuniorDetailSerializer
|
||||||
from base.messages import ERROR_CODE, SUCCESS_CODE
|
from base.messages import ERROR_CODE, SUCCESS_CODE
|
||||||
from base.constants import NUMBER
|
from base.constants import NUMBER, JUN, ZOD, GRD
|
||||||
from junior.models import Junior, JuniorPoints
|
from junior.models import Junior, JuniorPoints
|
||||||
class UserSerializer(serializers.ModelSerializer):
|
class UserSerializer(serializers.ModelSerializer):
|
||||||
"""User serializer"""
|
"""User serializer"""
|
||||||
@ -39,11 +39,11 @@ class UserSerializer(serializers.ModelSerializer):
|
|||||||
user = User.objects.create_user(username=email, email=email, password=password)
|
user = User.objects.create_user(username=email, email=email, password=password)
|
||||||
UserNotification.objects.create(user=user)
|
UserNotification.objects.create(user=user)
|
||||||
if user_type == '1':
|
if user_type == '1':
|
||||||
Junior.objects.create(auth=user, junior_code=generate_alphanumeric_code(6),
|
Junior.objects.create(auth=user, junior_code=generate_code(JUN, user.id),
|
||||||
referral_code=generate_alphanumeric_code(6))
|
referral_code=generate_code(ZOD, user.id))
|
||||||
if user_type == '2':
|
if user_type == '2':
|
||||||
Guardian.objects.create(user=user, guardian_code=generate_alphanumeric_code(6),
|
Guardian.objects.create(user=user, guardian_code=generate_code(GRD, user.id),
|
||||||
referral_code=generate_alphanumeric_code(6))
|
referral_code=generate_code(ZOD, user.id))
|
||||||
return user
|
return user
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
"""Error handling"""
|
"""Error handling"""
|
||||||
|
|||||||
@ -114,7 +114,6 @@ class CreateTaskAPIView(viewsets.ModelViewSet):
|
|||||||
def create(self, request, *args, **kwargs):
|
def create(self, request, *args, **kwargs):
|
||||||
image = request.data['default_image']
|
image = request.data['default_image']
|
||||||
data = request.data
|
data = request.data
|
||||||
print("data===>",data,'==>',type(data))
|
|
||||||
if 'https' in str(image):
|
if 'https' in str(image):
|
||||||
image_data = image
|
image_data = image
|
||||||
else:
|
else:
|
||||||
|
|||||||
@ -0,0 +1,21 @@
|
|||||||
|
# Generated by Django 4.2.2 on 2023-07-14 09:34
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('junior', '0012_junior_is_invited'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='junior',
|
||||||
|
options={'verbose_name': 'Junior', 'verbose_name_plural': 'Junior'},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='juniorpoints',
|
||||||
|
options={'verbose_name': 'Junior Task Points', 'verbose_name_plural': 'Junior Task Points'},
|
||||||
|
),
|
||||||
|
]
|
||||||
@ -1,10 +1,13 @@
|
|||||||
"""Junior model """
|
"""Junior model """
|
||||||
"""Import django"""
|
"""Import django"""
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
"""Import get_user_model function"""
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
|
"""Import ArrayField"""
|
||||||
from django.contrib.postgres.fields import ArrayField
|
from django.contrib.postgres.fields import ArrayField
|
||||||
"""Import django app"""
|
"""Import django app"""
|
||||||
from base.constants import GENDERS, SIGNUP_METHODS, RELATIONSHIP
|
from base.constants import GENDERS, SIGNUP_METHODS, RELATIONSHIP
|
||||||
|
"""Define User model"""
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
# Create your models here.
|
# Create your models here.
|
||||||
|
|
||||||
@ -14,10 +17,13 @@ class Junior(models.Model):
|
|||||||
"""Contact details"""
|
"""Contact details"""
|
||||||
phone = models.CharField(max_length=31, null=True, blank=True, default=None)
|
phone = models.CharField(max_length=31, null=True, blank=True, default=None)
|
||||||
country_code = models.IntegerField(blank=True, null=True)
|
country_code = models.IntegerField(blank=True, null=True)
|
||||||
|
"""country name of the guardian"""
|
||||||
country_name = models.CharField(max_length=100, null=True, blank=True, default=None)
|
country_name = models.CharField(max_length=100, null=True, blank=True, default=None)
|
||||||
"""Personal info"""
|
"""Personal info"""
|
||||||
gender = models.CharField(max_length=10, choices=GENDERS, null=True, blank=True, default=None)
|
gender = models.CharField(max_length=10, choices=GENDERS, null=True, blank=True, default=None)
|
||||||
|
"""Date of birth"""
|
||||||
dob = models.DateField(max_length=15, null=True, blank=True, default=None)
|
dob = models.DateField(max_length=15, null=True, blank=True, default=None)
|
||||||
|
"""Image of the junior"""
|
||||||
image = models.URLField(null=True, blank=True, default=None)
|
image = models.URLField(null=True, blank=True, default=None)
|
||||||
"""relationship"""
|
"""relationship"""
|
||||||
relationship = models.CharField(max_length=31, choices=RELATIONSHIP, null=True, blank=True,
|
relationship = models.CharField(max_length=31, choices=RELATIONSHIP, null=True, blank=True,
|
||||||
@ -26,15 +32,21 @@ class Junior(models.Model):
|
|||||||
signup_method = models.CharField(max_length=31, choices=SIGNUP_METHODS, default='1')
|
signup_method = models.CharField(max_length=31, choices=SIGNUP_METHODS, default='1')
|
||||||
"""Codes"""
|
"""Codes"""
|
||||||
junior_code = models.CharField(max_length=10, null=True, blank=True, default=None)
|
junior_code = models.CharField(max_length=10, null=True, blank=True, default=None)
|
||||||
|
"""Guardian Codes"""
|
||||||
guardian_code = ArrayField(models.CharField(max_length=10, null=True, blank=True, default=None),null=True)
|
guardian_code = ArrayField(models.CharField(max_length=10, null=True, blank=True, default=None),null=True)
|
||||||
|
"""Referral code"""
|
||||||
referral_code = models.CharField(max_length=10, null=True, blank=True, default=None)
|
referral_code = models.CharField(max_length=10, null=True, blank=True, default=None)
|
||||||
|
"""Referral code that is used by junior while signup"""
|
||||||
referral_code_used = 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"""
|
"""invited junior"""
|
||||||
is_invited = models.BooleanField(default=False)
|
is_invited = models.BooleanField(default=False)
|
||||||
"""Profile activity"""
|
"""Profile activity"""
|
||||||
is_active = models.BooleanField(default=True)
|
is_active = models.BooleanField(default=True)
|
||||||
|
"""junior profile is complete or not"""
|
||||||
is_complete_profile = models.BooleanField(default=False)
|
is_complete_profile = models.BooleanField(default=False)
|
||||||
|
"""passcode of the junior profile"""
|
||||||
passcode = models.IntegerField(null=True, blank=True, default=None)
|
passcode = models.IntegerField(null=True, blank=True, default=None)
|
||||||
|
"""junior is verified or not"""
|
||||||
is_verified = models.BooleanField(default=False)
|
is_verified = models.BooleanField(default=False)
|
||||||
"""Profile created and updated time"""
|
"""Profile created and updated time"""
|
||||||
created_at = models.DateTimeField(auto_now_add=True)
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
@ -44,6 +56,8 @@ class Junior(models.Model):
|
|||||||
""" Meta class """
|
""" Meta class """
|
||||||
db_table = 'junior'
|
db_table = 'junior'
|
||||||
verbose_name = 'Junior'
|
verbose_name = 'Junior'
|
||||||
|
"""another name of the model"""
|
||||||
|
verbose_name_plural = 'Junior'
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
"""Return email id"""
|
"""Return email id"""
|
||||||
@ -64,6 +78,8 @@ class JuniorPoints(models.Model):
|
|||||||
""" Meta class """
|
""" Meta class """
|
||||||
db_table = 'junior_task_points'
|
db_table = 'junior_task_points'
|
||||||
verbose_name = 'Junior Task Points'
|
verbose_name = 'Junior Task Points'
|
||||||
|
"""another name of the model"""
|
||||||
|
verbose_name_plural = 'Junior Task Points'
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
"""Return email id"""
|
"""Return email id"""
|
||||||
|
|||||||
@ -8,11 +8,11 @@ from django.utils import timezone
|
|||||||
from rest_framework_simplejwt.tokens import RefreshToken
|
from rest_framework_simplejwt.tokens import RefreshToken
|
||||||
|
|
||||||
"""Import django app"""
|
"""Import django app"""
|
||||||
from account.utils import send_otp_email, generate_alphanumeric_code
|
from account.utils import send_otp_email, generate_code
|
||||||
from junior.models import Junior, JuniorPoints
|
from junior.models import Junior, JuniorPoints
|
||||||
from guardian.tasks import generate_otp
|
from guardian.tasks import generate_otp
|
||||||
from base.messages import ERROR_CODE, SUCCESS_CODE
|
from base.messages import ERROR_CODE, SUCCESS_CODE
|
||||||
from base.constants import PENDING, IN_PROGRESS, REJECTED, REQUESTED, COMPLETED, NUMBER
|
from base.constants import PENDING, IN_PROGRESS, REJECTED, REQUESTED, COMPLETED, NUMBER, JUN, ZOD
|
||||||
from guardian.models import Guardian, JuniorTask
|
from guardian.models import Guardian, JuniorTask
|
||||||
from account.models import UserEmailOtp
|
from account.models import UserEmailOtp
|
||||||
from junior.utils import junior_notification_email, junior_approval_mail
|
from junior.utils import junior_notification_email, junior_approval_mail
|
||||||
@ -275,8 +275,8 @@ class AddJuniorSerializer(serializers.ModelSerializer):
|
|||||||
junior_data = Junior.objects.create(auth=user_data, gender=validated_data.get('gender'),
|
junior_data = Junior.objects.create(auth=user_data, gender=validated_data.get('gender'),
|
||||||
dob=validated_data.get('dob'), is_invited=True,
|
dob=validated_data.get('dob'), is_invited=True,
|
||||||
relationship=validated_data.get('relationship'),
|
relationship=validated_data.get('relationship'),
|
||||||
junior_code=generate_alphanumeric_code(6),
|
junior_code=generate_code(JUN, user_data.id),
|
||||||
referral_code=generate_alphanumeric_code(6),
|
referral_code=generate_code(ZOD, user_data.id),
|
||||||
referral_code_used=guardian_data.referral_code)
|
referral_code_used=guardian_data.referral_code)
|
||||||
"""Generate otp"""
|
"""Generate otp"""
|
||||||
otp_value = generate_otp()
|
otp_value = generate_otp()
|
||||||
|
|||||||
@ -1,28 +1,17 @@
|
|||||||
"""Account utils"""
|
"""Account utils"""
|
||||||
"""Import django"""
|
"""Import django"""
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from rest_framework import viewsets, status
|
|
||||||
from rest_framework.response import Response
|
|
||||||
"""Third party Django app"""
|
"""Third party Django app"""
|
||||||
from templated_email import send_templated_mail
|
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):
|
def junior_notification_email(recipient_email, full_name, email, password):
|
||||||
"""Notification email"""
|
"""Notification email"""
|
||||||
from_email = settings.EMAIL_FROM_ADDRESS
|
from_email = settings.EMAIL_FROM_ADDRESS
|
||||||
|
"""recipient email"""
|
||||||
recipient_list = [recipient_email]
|
recipient_list = [recipient_email]
|
||||||
|
"""use send template mail for sending email"""
|
||||||
send_templated_mail(
|
send_templated_mail(
|
||||||
template_name='junior_notification_email.email',
|
template_name='junior_notification_email.email',
|
||||||
from_email=from_email,
|
from_email=from_email,
|
||||||
@ -40,6 +29,7 @@ def junior_approval_mail(guardian, full_name):
|
|||||||
"""junior approval mail"""
|
"""junior approval mail"""
|
||||||
from_email = settings.EMAIL_FROM_ADDRESS
|
from_email = settings.EMAIL_FROM_ADDRESS
|
||||||
recipient_list = [guardian]
|
recipient_list = [guardian]
|
||||||
|
"""use send tempolate mail for sending email"""
|
||||||
send_templated_mail(
|
send_templated_mail(
|
||||||
template_name='junior_approval_mail.email',
|
template_name='junior_approval_mail.email',
|
||||||
from_email=from_email,
|
from_email=from_email,
|
||||||
|
|||||||
@ -97,8 +97,8 @@ REST_FRAMEWORK = {
|
|||||||
'PAGE_SIZE': 5,
|
'PAGE_SIZE': 5,
|
||||||
}
|
}
|
||||||
SIMPLE_JWT = {
|
SIMPLE_JWT = {
|
||||||
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=50),
|
'ACCESS_TOKEN_LIFETIME': timedelta(hours=2, minutes=59, seconds=59, microseconds=999999),
|
||||||
'REFRESH_TOKEN_LIFETIME': timedelta(days=7),
|
'REFRESH_TOKEN_LIFETIME': timedelta(hours=71, minutes=59, seconds=59, microseconds=999999),
|
||||||
}
|
}
|
||||||
# Database
|
# Database
|
||||||
# https://docs.djangoproject.com/en/3.0/ref/settings/#databases
|
# https://docs.djangoproject.com/en/3.0/ref/settings/#databases
|
||||||
|
|||||||
Reference in New Issue
Block a user