Fixed error cases, tests, viewsets and api paths
This commit is contained in:
parent
0a07e408c5
commit
e99c07ef19
9 changed files with 94 additions and 53 deletions
|
|
@ -1,5 +1,3 @@
|
||||||
# Generated by Django 5.2.10 on 2026-01-17 16:12
|
|
||||||
|
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
import uuid
|
import uuid
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
|
||||||
|
|
@ -33,10 +33,10 @@ class OrganizationMembershipAdmin(ModelAdmin):
|
||||||
|
|
||||||
@register(OrganizationInvitation)
|
@register(OrganizationInvitation)
|
||||||
class OrganizationInvitationAdmin(ModelAdmin):
|
class OrganizationInvitationAdmin(ModelAdmin):
|
||||||
list_display = ('id', 'token', 'organization', 'created_by', 'is_active', 'expires_at', 'max_uses', 'created_at')
|
list_display = ('id', 'token', 'organization', 'created_by', 'is_active', 'expires_at', 'max_uses', 'created_at', 'uses')
|
||||||
search_fields = ('token', 'organization__name', 'created_by__email_address')
|
search_fields = ('token', 'organization__name', 'created_by__email_address')
|
||||||
list_filter = ('is_active',)
|
list_filter = ('is_active',)
|
||||||
raw_id_fields = ('organization', 'created_by', 'used_by')
|
raw_id_fields = ('organization', 'created_by')
|
||||||
readonly_fields = ('token', 'created_at')
|
readonly_fields = ('token', 'created_at')
|
||||||
|
|
||||||
@register(Role)
|
@register(Role)
|
||||||
|
|
|
||||||
|
|
@ -36,11 +36,11 @@ class Migration(migrations.Migration):
|
||||||
('id', models.BigAutoField(primary_key=True, serialize=False)),
|
('id', models.BigAutoField(primary_key=True, serialize=False)),
|
||||||
('token', models.UUIDField(default=uuid.uuid4, editable=False, unique=True)),
|
('token', models.UUIDField(default=uuid.uuid4, editable=False, unique=True)),
|
||||||
('expires_at', models.DateTimeField()),
|
('expires_at', models.DateTimeField()),
|
||||||
|
('uses', models.IntegerField(default=0)),
|
||||||
('max_uses', models.IntegerField(default=1)),
|
('max_uses', models.IntegerField(default=1)),
|
||||||
('is_active', models.BooleanField(default=True)),
|
('is_active', models.BooleanField(default=True)),
|
||||||
('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='created_invites', to=settings.AUTH_USER_MODEL)),
|
('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='created_invites', to=settings.AUTH_USER_MODEL)),
|
||||||
('organization', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='invite_tokens', to='orgs.organization')),
|
('organization', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='invite_tokens', to='orgs.organization')),
|
||||||
('used_by', models.ManyToManyField(blank=True, related_name='used_invites', to=settings.AUTH_USER_MODEL)),
|
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'Invite Token',
|
'verbose_name': 'Invite Token',
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ class OrganizationInvitation(TimeStampMixin, Model):
|
||||||
|
|
||||||
expires_at = DateTimeField()
|
expires_at = DateTimeField()
|
||||||
|
|
||||||
used_by = ManyToManyField(User, blank = True, related_name = "used_invites")
|
uses = IntegerField(default = 0)
|
||||||
max_uses = IntegerField(default = 1)
|
max_uses = IntegerField(default = 1)
|
||||||
|
|
||||||
is_active = BooleanField(default = True)
|
is_active = BooleanField(default = True)
|
||||||
|
|
@ -63,7 +63,7 @@ class OrganizationInvitation(TimeStampMixin, Model):
|
||||||
super().save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
def is_valid(self):
|
def is_valid(self):
|
||||||
return self.is_active and not self.used_by.exists() and timezone.now() < self.expires_at
|
return self.is_active and self.uses < self.max_uses and timezone.now() < self.expires_at
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f"Invite for {self.organization.name} by {self.created_by.full_name} (expires {self.expires_at})"
|
return f"Invite for {self.organization.name} by {self.created_by.full_name} (expires {self.expires_at})"
|
||||||
|
|
|
||||||
|
|
@ -35,14 +35,13 @@ class OrganizationMembershipSerializer(ModelSerializer):
|
||||||
|
|
||||||
class OrganizationInvitationSerializer(ModelSerializer):
|
class OrganizationInvitationSerializer(ModelSerializer):
|
||||||
created_by = UserSerializer(read_only = True)
|
created_by = UserSerializer(read_only = True)
|
||||||
used_by = UserSerializer(read_only = True, many=True)
|
|
||||||
invite_url = SerializerMethodField()
|
invite_url = SerializerMethodField()
|
||||||
is_valid = SerializerMethodField()
|
is_valid = SerializerMethodField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = OrganizationInvitation
|
model = OrganizationInvitation
|
||||||
fields = ['id', 'token', 'organization', 'created_by', 'expires_at', 'used_by', 'max_uses', 'is_active', 'invite_url', 'is_valid', 'created_at', 'updated_at']
|
fields = ['id', 'token', 'organization', 'created_by', 'expires_at', 'max_uses', 'is_active', 'invite_url', 'is_valid', 'created_at', 'updated_at', 'uses']
|
||||||
read_only_fields = ['token', 'organization', 'created_by', 'used_by', 'max_uses', 'created_at', 'updated_at']
|
read_only_fields = ['token', 'organization', 'created_by', 'max_uses', 'created_at', 'updated_at', 'uses']
|
||||||
|
|
||||||
def get_invite_url(self, obj):
|
def get_invite_url(self, obj):
|
||||||
request = self.context.get('request')
|
request = self.context.get('request')
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ class OrganizationAPITests(TestCase):
|
||||||
invite_view = OrganizationViewSet.as_view({'post': 'join'})
|
invite_view = OrganizationViewSet.as_view({'post': 'join'})
|
||||||
req2 = self.factory.post('/', {})
|
req2 = self.factory.post('/', {})
|
||||||
force_authenticate(req2, user=other)
|
force_authenticate(req2, user=other)
|
||||||
resp2 = invite_view(req2, uuid=str(org.uuid), token=str(token))
|
resp2 = invite_view(req2, token=str(token))
|
||||||
self.assertIn(resp2.status_code, (HTTP_200_OK, HTTP_201_CREATED))
|
self.assertIn(resp2.status_code, (HTTP_200_OK, HTTP_201_CREATED))
|
||||||
self.assertTrue(OrganizationMembership.objects.filter(organization=org, user=other).exists())
|
self.assertTrue(OrganizationMembership.objects.filter(organization=org, user=other).exists())
|
||||||
|
|
||||||
|
|
@ -54,7 +54,7 @@ class OrganizationAPITests(TestCase):
|
||||||
force_authenticate(req, user=self.manager)
|
force_authenticate(req, user=self.manager)
|
||||||
resp = members_view(req, uuid=str(org.uuid))
|
resp = members_view(req, uuid=str(org.uuid))
|
||||||
self.assertEqual(resp.status_code, HTTP_200_OK)
|
self.assertEqual(resp.status_code, HTTP_200_OK)
|
||||||
self.assertTrue(any(m['user']['email_address'] == 'member@example.com' for m in resp.data))
|
self.assertTrue(any(m['email_address'] == 'member@example.com' for m in resp.data))
|
||||||
|
|
||||||
member.is_manager = True
|
member.is_manager = True
|
||||||
member.save()
|
member.save()
|
||||||
|
|
@ -94,7 +94,7 @@ class OrganizationAPITests(TestCase):
|
||||||
def test_role_create_forbidden_for_non_manager(self):
|
def test_role_create_forbidden_for_non_manager(self):
|
||||||
org = Organization.objects.create(name='RoleNoCreateOrg', owner=self.user)
|
org = Organization.objects.create(name='RoleNoCreateOrg', owner=self.user)
|
||||||
OrganizationMembership.objects.create(organization=org, user=self.user)
|
OrganizationMembership.objects.create(organization=org, user=self.user)
|
||||||
self.assertFalse(hasattr(OrganizationViewSet, 'role'))
|
self.assertTrue(hasattr(OrganizationViewSet, 'role'))
|
||||||
|
|
||||||
def test_role_members_post_missing_user_id_returns_400(self):
|
def test_role_members_post_missing_user_id_returns_400(self):
|
||||||
org = Organization.objects.create(name='RoleMissingParamOrg', owner=self.manager)
|
org = Organization.objects.create(name='RoleMissingParamOrg', owner=self.manager)
|
||||||
|
|
@ -201,7 +201,7 @@ class OrganizationAPITests(TestCase):
|
||||||
invite_view = OrganizationViewSet.as_view({'post': 'join'})
|
invite_view = OrganizationViewSet.as_view({'post': 'join'})
|
||||||
req = self.factory.post('/')
|
req = self.factory.post('/')
|
||||||
force_authenticate(req, user=other)
|
force_authenticate(req, user=other)
|
||||||
resp = invite_view(req, uuid=str(org.uuid), token=str(invite.token))
|
resp = invite_view(req, token=str(invite.token))
|
||||||
self.assertIn(resp.status_code, (HTTP_400_BAD_REQUEST, HTTP_404_NOT_FOUND))
|
self.assertIn(resp.status_code, (HTTP_400_BAD_REQUEST, HTTP_404_NOT_FOUND))
|
||||||
|
|
||||||
def test_remove_member_by_non_manager_forbidden(self):
|
def test_remove_member_by_non_manager_forbidden(self):
|
||||||
|
|
|
||||||
|
|
@ -25,11 +25,11 @@ class OrganizationModelTests(TestCase):
|
||||||
self.assertIsNotNone(invite.expires_at)
|
self.assertIsNotNone(invite.expires_at)
|
||||||
self.assertTrue(invite.is_valid())
|
self.assertTrue(invite.is_valid())
|
||||||
|
|
||||||
invite.used_by.add(self.user)
|
invite.uses += 1
|
||||||
invite.save()
|
invite.save()
|
||||||
self.assertFalse(invite.is_valid())
|
self.assertFalse(invite.is_valid())
|
||||||
|
|
||||||
invite.used_by.clear()
|
invite.uses = 0
|
||||||
invite.expires_at = timezone.now() - timedelta(days=1)
|
invite.expires_at = timezone.now() - timedelta(days=1)
|
||||||
invite.save()
|
invite.save()
|
||||||
self.assertFalse(invite.is_valid())
|
self.assertFalse(invite.is_valid())
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@ from rest_framework.response import Response
|
||||||
from rest_framework.status import HTTP_403_FORBIDDEN, HTTP_404_NOT_FOUND, HTTP_400_BAD_REQUEST
|
from rest_framework.status import HTTP_403_FORBIDDEN, HTTP_404_NOT_FOUND, HTTP_400_BAD_REQUEST
|
||||||
from rest_framework.decorators import action
|
from rest_framework.decorators import action
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
from apps.users.models import User
|
||||||
|
from apps.users.serializers import UserSerializer
|
||||||
|
|
||||||
|
|
||||||
class OrganizationViewSet(ModelViewSet):
|
class OrganizationViewSet(ModelViewSet):
|
||||||
|
|
@ -39,34 +41,38 @@ class OrganizationViewSet(ModelViewSet):
|
||||||
created_by = request.user,
|
created_by = request.user,
|
||||||
max_uses = max_uses
|
max_uses = max_uses
|
||||||
)
|
)
|
||||||
return Response(OrganizationInvitationSerializer(invitation).data)
|
return Response(OrganizationInvitationSerializer(invitation, context={'request': request}).data)
|
||||||
|
|
||||||
@action(detail=True, methods=['post'], url_path='join/(?P<token>[0-9a-f-]{36})')
|
@action(detail=False, methods=['post'], url_path='join/(?P<token>[0-9a-f-]{36})')
|
||||||
def join(self, request, uuid = None, token = None):
|
def join(self, request, token = None):
|
||||||
try:
|
try:
|
||||||
organization = Organization.objects.get(uuid=uuid)
|
invitation = OrganizationInvitation.objects.get(token = token)
|
||||||
except Organization.DoesNotExist:
|
|
||||||
return Response({'error': 'Organization not found'}, status=HTTP_403_FORBIDDEN)
|
|
||||||
try:
|
|
||||||
invitation = OrganizationInvitation.objects.get(token = token, organization = organization)
|
|
||||||
except OrganizationInvitation.DoesNotExist:
|
except OrganizationInvitation.DoesNotExist:
|
||||||
return Response({'error': 'Invalid invitation token'}, status = HTTP_404_NOT_FOUND)
|
return Response({'error': 'Invalid invitation token'}, status = HTTP_404_NOT_FOUND)
|
||||||
|
|
||||||
if not invitation.is_active or invitation.expires_at < timezone.now():
|
if not invitation.is_active or invitation.expires_at < timezone.now():
|
||||||
return Response({'error': 'Invitation token is no longer valid'}, status = HTTP_400_BAD_REQUEST)
|
return Response({'error': 'Invitation token is no longer valid'}, status = HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
if OrganizationMembership.objects.filter(user = request.user, organization = organization).exists():
|
if invitation.uses >= invitation.max_uses:
|
||||||
|
invitation.is_active = False
|
||||||
|
invitation.save()
|
||||||
|
return Response({'error': 'Invitation token has reached its maximum number of uses'}, status = HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
if OrganizationMembership.objects.filter(user = request.user, organization = invitation.organization).exists():
|
||||||
return Response({'error': 'You are already a member of this organization'}, status = HTTP_403_FORBIDDEN)
|
return Response({'error': 'You are already a member of this organization'}, status = HTTP_403_FORBIDDEN)
|
||||||
|
|
||||||
OrganizationMembership.objects.create(user = request.user, organization = organization)
|
OrganizationMembership.objects.create(user = request.user, organization = invitation.organization)
|
||||||
|
|
||||||
invitation.max_uses -= 1
|
invitation.uses += 1
|
||||||
if invitation.max_uses <= 0:
|
if invitation.uses >= invitation.max_uses:
|
||||||
invitation.is_active = False
|
invitation.is_active = False
|
||||||
invitation.used_by.add(request.user)
|
|
||||||
invitation.save()
|
invitation.save()
|
||||||
|
|
||||||
|
organization_data = OrganizationSerializer(invitation.organization, context={'request': request}).data
|
||||||
|
organization_data['message'] = 'Successfully joined the organization'
|
||||||
|
organization_data['success'] = True
|
||||||
|
|
||||||
return Response({'message': 'Successfully joined the organization'})
|
return Response(organization_data)
|
||||||
|
|
||||||
@action(detail=True, methods=['post'], url_path='leave')
|
@action(detail=True, methods=['post'], url_path='leave')
|
||||||
def leave(self, request, uuid = None):
|
def leave(self, request, uuid = None):
|
||||||
|
|
@ -78,7 +84,7 @@ class OrganizationViewSet(ModelViewSet):
|
||||||
|
|
||||||
if organization.owner == request.user:
|
if organization.owner == request.user:
|
||||||
return Response({'error': 'The owner cannot leave the organization. Please transfer ownership or delete the organization.'}, status = HTTP_403_FORBIDDEN)
|
return Response({'error': 'The owner cannot leave the organization. Please transfer ownership or delete the organization.'}, status = HTTP_403_FORBIDDEN)
|
||||||
|
|
||||||
membership.delete()
|
membership.delete()
|
||||||
return Response({'message': 'Successfully left the organization'})
|
return Response({'message': 'Successfully left the organization'})
|
||||||
|
|
||||||
|
|
@ -87,8 +93,8 @@ class OrganizationViewSet(ModelViewSet):
|
||||||
if not request.user.is_manager:
|
if not request.user.is_manager:
|
||||||
return Response({'error': 'Only managers can view invites'}, status = HTTP_403_FORBIDDEN)
|
return Response({'error': 'Only managers can view invites'}, status = HTTP_403_FORBIDDEN)
|
||||||
organization = self.get_object()
|
organization = self.get_object()
|
||||||
invites = OrganizationInvitation.objects.filter(organization = organization)
|
invites = OrganizationInvitation.objects.filter(organization = organization, is_active = True)
|
||||||
serializer = OrganizationInvitationSerializer(invites, many = True)
|
serializer = OrganizationInvitationSerializer(invites, many = True, context={'request': request})
|
||||||
return Response(serializer.data)
|
return Response(serializer.data)
|
||||||
|
|
||||||
@action(detail=True, methods=['get'], url_path='invite/(?P<token>[0-9a-f-]{36})')
|
@action(detail=True, methods=['get'], url_path='invite/(?P<token>[0-9a-f-]{36})')
|
||||||
|
|
@ -100,19 +106,33 @@ class OrganizationViewSet(ModelViewSet):
|
||||||
invitation = OrganizationInvitation.objects.get(token = token, organization = organization)
|
invitation = OrganizationInvitation.objects.get(token = token, organization = organization)
|
||||||
except OrganizationInvitation.DoesNotExist:
|
except OrganizationInvitation.DoesNotExist:
|
||||||
return Response({'error': 'Invalid invitation token'}, status = HTTP_403_FORBIDDEN)
|
return Response({'error': 'Invalid invitation token'}, status = HTTP_403_FORBIDDEN)
|
||||||
serializer = OrganizationInvitationSerializer(invitation)
|
serializer = OrganizationInvitationSerializer(invitation, context={'request': request})
|
||||||
return Response(serializer.data)
|
return Response(serializer.data)
|
||||||
|
|
||||||
|
@action(detail=True, methods=['post', 'delete'], url_path='invite/(?P<token>[0-9a-f-]{36})/revoke')
|
||||||
|
def revoke_invite(self, request, uuid = None, token = None):
|
||||||
|
if not request.user.is_manager:
|
||||||
|
return Response({'error': 'Only managers can revoke invites'}, status = HTTP_403_FORBIDDEN)
|
||||||
|
organization = self.get_object()
|
||||||
|
try:
|
||||||
|
invitation = OrganizationInvitation.objects.get(token = token, organization = organization)
|
||||||
|
except OrganizationInvitation.DoesNotExist:
|
||||||
|
return Response({'error': 'Invalid invitation token'}, status = HTTP_403_FORBIDDEN)
|
||||||
|
|
||||||
|
invitation.is_active = False
|
||||||
|
invitation.save()
|
||||||
|
return Response({'message': 'Invitation successfully revoked'})
|
||||||
|
|
||||||
@action(detail=True, methods=['get'], url_path='member')
|
@action(detail=True, methods=['get'], url_path='member')
|
||||||
def list_members(self, request, uuid = None):
|
def list_members(self, request, uuid = None):
|
||||||
if not request.user.is_manager:
|
if not request.user.is_manager:
|
||||||
return Response({'error': 'Only managers can view members'}, status = HTTP_403_FORBIDDEN)
|
return Response({'error': 'Only managers can view members'}, status = HTTP_403_FORBIDDEN)
|
||||||
organization = self.get_object()
|
organization = self.get_object()
|
||||||
memberships = OrganizationMembership.objects.filter(organization = organization)
|
memberships = User.objects.filter(organization_memberships__organization = organization)
|
||||||
serializer = OrganizationMembershipSerializer(memberships, many = True)
|
serializer = UserSerializer(memberships, many = True)
|
||||||
return Response(serializer.data)
|
return Response(serializer.data)
|
||||||
|
|
||||||
@action(detail=True, methods=['post'], url_path='member/(?P<user_id>\d+)/remove')
|
@action(detail=True, methods=['post'], url_path=r'member/(?P<user_id>\d+)/remove')
|
||||||
def remove_member(self, request, uuid = None, user_id = None):
|
def remove_member(self, request, uuid = None, user_id = None):
|
||||||
if not request.user.is_manager:
|
if not request.user.is_manager:
|
||||||
return Response({'error': 'Only managers can remove members'}, status = HTTP_403_FORBIDDEN)
|
return Response({'error': 'Only managers can remove members'}, status = HTTP_403_FORBIDDEN)
|
||||||
|
|
@ -128,16 +148,14 @@ class OrganizationViewSet(ModelViewSet):
|
||||||
membership.delete()
|
membership.delete()
|
||||||
return Response({'message': 'Member successfully removed from the organization'})
|
return Response({'message': 'Member successfully removed from the organization'})
|
||||||
|
|
||||||
@action(detail=True, methods=['get'], url_path='role')
|
@action(detail=True, methods=['get', 'post'], url_path='role')
|
||||||
def list_roles(self, request, uuid = None):
|
def role(self, request, uuid = None):
|
||||||
organization = self.get_object()
|
|
||||||
roles = Role.objects.filter(organization = organization)
|
|
||||||
serializer = RoleSerializer(roles, many = True)
|
|
||||||
return Response(serializer.data)
|
|
||||||
|
|
||||||
@action(detail=True, methods=['post'], url_path='role')
|
|
||||||
def create_role(self, request, uuid = None):
|
|
||||||
organization = self.get_object()
|
organization = self.get_object()
|
||||||
|
if request.method == 'GET':
|
||||||
|
roles = Role.objects.filter(organization = organization)
|
||||||
|
serializer = RoleSerializer(roles, many = True)
|
||||||
|
return Response(serializer.data)
|
||||||
|
|
||||||
if not request.user.is_manager:
|
if not request.user.is_manager:
|
||||||
return Response({'error': 'Only managers can create roles'}, status = HTTP_403_FORBIDDEN)
|
return Response({'error': 'Only managers can create roles'}, status = HTTP_403_FORBIDDEN)
|
||||||
name = request.data.get('name')
|
name = request.data.get('name')
|
||||||
|
|
@ -147,4 +165,28 @@ class OrganizationViewSet(ModelViewSet):
|
||||||
serializer = RoleSerializer(role)
|
serializer = RoleSerializer(role)
|
||||||
return Response(serializer.data)
|
return Response(serializer.data)
|
||||||
|
|
||||||
|
@action(detail=True, methods=['post'], url_path='role/(?P<role_uuid>[0-9a-f-]{36})/delete')
|
||||||
|
def delete_role(self, request, uuid = None, role_uuid = None):
|
||||||
|
if not request.user.is_manager:
|
||||||
|
return Response({'error': 'Only managers can delete roles'}, status = HTTP_403_FORBIDDEN)
|
||||||
|
organization = self.get_object()
|
||||||
|
try:
|
||||||
|
role = Role.objects.get(uuid = role_uuid, organization = organization)
|
||||||
|
except Role.DoesNotExist:
|
||||||
|
return Response({'error': 'Role not found in this organization'}, status = HTTP_404_NOT_FOUND)
|
||||||
|
|
||||||
|
role.delete()
|
||||||
|
return Response({'message': 'Role successfully deleted'})
|
||||||
|
|
||||||
|
@action(detail=True, methods=['get'], url_path='role/(?P<role_uuid>[0-9a-f-]{36})/member')
|
||||||
|
def list_role_members(self, request, uuid = None, role_uuid = None):
|
||||||
|
organization = self.get_object()
|
||||||
|
try:
|
||||||
|
role = Role.objects.get(uuid = role_uuid, organization = organization)
|
||||||
|
except Role.DoesNotExist:
|
||||||
|
return Response({'error': 'Role not found in this organization'}, status = HTTP_404_NOT_FOUND)
|
||||||
|
|
||||||
|
memberships = RoleMembership.objects.filter(role = role)
|
||||||
|
serializer = RoleMembershipSerializer(memberships, many = True)
|
||||||
|
return Response(serializer.data)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -55,13 +55,15 @@ class UserViewSet(ReadOnlyModelViewSet):
|
||||||
email_address = User.objects.normalize_email(email_address)
|
email_address = User.objects.normalize_email(email_address)
|
||||||
if User.objects.filter(email_address=email_address).exists():
|
if User.objects.filter(email_address=email_address).exists():
|
||||||
return Response({'detail': 'Email address already exists.', 'success': False}, status=HTTP_400_BAD_REQUEST)
|
return Response({'detail': 'Email address already exists.', 'success': False}, status=HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
if not data.get('first_name') or not data.get('last_name'):
|
if not data.get('first_name') or not data.get('last_name'):
|
||||||
return Response({'detail': 'First and last name(s) must be provided.', 'success': False}, status=HTTP_400_BAD_REQUEST)
|
return Response({'detail': 'First and last name(s) must be provided.', 'success': False}, status=HTTP_400_BAD_REQUEST)
|
||||||
|
if type(manager:=data.get('manager')) is not bool:
|
||||||
if not data.get('manager') or data.get('manager').lower() != 'true' and data.get('manager').lower() != 'false':
|
if manager in ['true', 'True']:
|
||||||
return Response({'detail': '"manager" field must be true or false.', 'success': False}, status=HTTP_400_BAD_REQUEST)
|
manager = True
|
||||||
|
elif manager in ['false', 'False']:
|
||||||
|
manager = False
|
||||||
|
else:
|
||||||
|
return Response({'detail': '"manager" field must be a boolean value.', 'success': False}, status=HTTP_400_BAD_REQUEST)
|
||||||
if data.get('password') != data.get('confirm_password'):
|
if data.get('password') != data.get('confirm_password'):
|
||||||
return Response({'detail': 'Passwords do not match.', 'success': False}, status=HTTP_400_BAD_REQUEST)
|
return Response({'detail': 'Passwords do not match.', 'success': False}, status=HTTP_400_BAD_REQUEST)
|
||||||
try:
|
try:
|
||||||
|
|
@ -71,7 +73,7 @@ class UserViewSet(ReadOnlyModelViewSet):
|
||||||
first_name=data.get('first_name'),
|
first_name=data.get('first_name'),
|
||||||
last_name=data.get('last_name'),
|
last_name=data.get('last_name'),
|
||||||
date_of_birth=data.get('date_of_birth'),
|
date_of_birth=data.get('date_of_birth'),
|
||||||
is_manager=data.get('manager').lower() == 'true'
|
is_manager=manager,
|
||||||
)
|
)
|
||||||
return Response({'detail': 'User account created successfully.', 'success': True}, status=HTTP_201_CREATED)
|
return Response({'detail': 'User account created successfully.', 'success': True}, status=HTTP_201_CREATED)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue