Skip to content

Commit 067e2a6

Browse files
committed
feat: add APIs for uploading images and file
- Created endpoints to handle image and PDF uploads - Added validation for file type and size in serializers - Linked uploaded files to related chat messages Signed-off-by: drona-gyawali <dronarajgyawali@gmail.com>
1 parent faf52ae commit 067e2a6

File tree

11 files changed

+476
-44
lines changed

11 files changed

+476
-44
lines changed

management/chat/api/viewset.py

Lines changed: 158 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,26 @@
11
from chat.models import ChatGroup, GroupMessage
2-
from chat.serializers import ChatSerializers, GroupSerializers
3-
from core.permissions import CanEditOwnOrAdmin
2+
from chat.serializers import (
3+
ChatSerializers,
4+
FileAttachmentSerializers,
5+
GroupSerializers,
6+
ImageAttachmentSerializer,
7+
)
48
from rest_framework import authentication, generics, permissions, status
9+
from rest_framework.decorators import APIView
510
from rest_framework.response import Response
611

712

813
class ChatMessageView(generics.ListAPIView):
14+
"""
15+
API endpoint to retrieve chat groups and their messages.
16+
17+
Request Method: GET /chat/messages/
18+
19+
Responses:
20+
- 200 OK: Returns a list of chat groups and their messages.
21+
- 401 Unauthorized: Authentication failed.
22+
"""
23+
924
queryset = ChatGroup.objects.all()
1025
permission_classes = [permissions.IsAuthenticated]
1126
authentication_classes = [authentication.SessionAuthentication]
@@ -28,6 +43,17 @@ def list(self, request, *args, **kwargs):
2843

2944

3045
class ChatCreateView(generics.CreateAPIView):
46+
"""
47+
API endpoint to create a group message.
48+
49+
Request Method: POST /chat/messages/create
50+
51+
Responses:
52+
- 201 Created: Message has been created.
53+
- 400 Bad Request: Unable to create message.
54+
- 401 Unauthorized: Authentication failed.
55+
"""
56+
3157
permission_classes = [permissions.IsAuthenticated]
3258
serializer_class = GroupSerializers
3359
authentication_classes = [authentication.SessionAuthentication]
@@ -37,7 +63,19 @@ class ChatCreateView(generics.CreateAPIView):
3763

3864

3965
class ChatDeleteView(generics.DestroyAPIView):
40-
permission_classes = [permissions.IsAdminUser | CanEditOwnOrAdmin]
66+
"""
67+
API endpoint to delete a group message by its ID.
68+
69+
Request Method: DELETE /chat/messsages/int:id/delete/
70+
71+
Responses:
72+
- 200 OK: Message has been deleted.
73+
- 401 Unauthorized: Authentication failed.
74+
- 403 Forbidden: User does not have permission to delete the message.
75+
- 404 Not Found: Message with the specified ID does not exist.
76+
"""
77+
78+
permission_classes = [permissions.IsAdminUser]
4179
serializer_class = GroupSerializers
4280
queryset = GroupMessage.objects.all()
4381
lookup_field = "id"
@@ -53,28 +91,142 @@ def destroy(self, request, *args, **kwargs):
5391

5492

5593
class ChatUpdateView(generics.UpdateAPIView):
94+
"""
95+
API endpoint to update a group message by its ID.
96+
97+
Request Method: PUT chat/messages/<int:id>/update/
98+
99+
Responses:
100+
- 200 OK: Message has been updated.
101+
- 400 Bad Request: Unable to update message.
102+
- 401 Unauthorized: Authentication failed.
103+
- 404 Not Found: Message with the specified ID does not exist.
104+
"""
105+
56106
serializer_class = GroupSerializers
57-
authentication_classes = [authentication.SessionAuthentication | CanEditOwnOrAdmin]
107+
authentication_classes = [authentication.SessionAuthentication]
108+
queryset = GroupMessage.objects.all()
58109
lookup_field = "id"
59110

60111

61112
msg_update = ChatUpdateView.as_view()
62113

63114

64115
class GroupNameCreate(generics.CreateAPIView):
116+
"""
117+
API endpoint to create a new chat group.
118+
119+
Request Method: POST /chat/groups/create/
120+
121+
Responses:
122+
- 201 Created: Chat group created successfully.
123+
- 400 Bad Request: Invalid data provided.
124+
- 401 Unauthorized: Authentication credentials were not provided or are invalid.
125+
"""
126+
65127
serializer_class = ChatSerializers
66128
queryset = ChatGroup.objects.all()
67-
permission_classes = [permissions.IsAuthenticatedOrReadOnly | CanEditOwnOrAdmin]
129+
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
68130

69131

70132
group_create = GroupNameCreate.as_view()
71133

72134

73135
class GroupNameUpdate(generics.UpdateAPIView):
136+
"""
137+
API endpoint to update the group name of a chat group.
138+
139+
Request Method: PUT/PATCH /chat/groups/str:<group_name>/update/
140+
141+
Path Parameters:
142+
- group_name (str): The current name of the chat group to be updated.
143+
144+
Request Body:
145+
- group_name (str): The new name for the chat group.
146+
147+
Responses:
148+
- 200 OK: Group name updated successfully.
149+
- 400 Bad Request: Invalid data provided.
150+
- 401 Unauthorized: Authentication credentials were not provided or are invalid.
151+
- 404 Not Found: Chat group with the specified group_name does not exist.
152+
"""
153+
74154
serializer_class = ChatSerializers
75155
queryset = ChatGroup.objects.all()
76-
permission_classes = [permissions.IsAuthenticatedOrReadOnly | CanEditOwnOrAdmin]
156+
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
77157
lookup_field = "group_name"
78158

79159

80160
group_update = GroupNameUpdate.as_view()
161+
162+
163+
class FileAttachment(APIView):
164+
"""
165+
API endpoint to upload the file such as `.pdf`.
166+
167+
Request Method: POST /chat/upload_file/
168+
169+
Responses:
170+
- 200 OK: File uploaded
171+
- 400 Bad Request: Unable to upload file.
172+
- 401 Unauthorized: Authentication failed.
173+
"""
174+
175+
permission_classes = [permissions.IsAuthenticated]
176+
177+
def post(self, request, format=None):
178+
client = request.user
179+
if not client:
180+
return Response(
181+
{"error": "Unauthorized to upload file "},
182+
status=status.HTTP_401_UNAUTHORIZED,
183+
)
184+
185+
serializer = FileAttachmentSerializers(data=request.data)
186+
if serializer.is_valid():
187+
serializer.save()
188+
return Response({"success": "File uploaded"}, status=status.HTTP_200_OK)
189+
print("Serializer errors:", serializer.errors)
190+
return Response(
191+
{"failed": "Unable to upload file", "detail_error": serializer.errors},
192+
status=status.HTTP_400_BAD_REQUEST,
193+
)
194+
195+
196+
upload_file = FileAttachment.as_view()
197+
198+
199+
class ImageAttachment(APIView):
200+
"""
201+
API endpoint to upload the image file.
202+
203+
Request Method: POST /chat/upload_image/
204+
205+
Responses:
206+
- 200 OK:Image uploaded
207+
- 400 Bad Request: Unable to upload image.
208+
- 401 Unauthorized: Authentication failed.
209+
"""
210+
211+
permission_classes = [permissions.IsAuthenticated]
212+
213+
def post(self, request, format=None):
214+
client = request.user
215+
if not client:
216+
return Response(
217+
{"error": "Unauthorized to upload images"},
218+
status=status.HTTP_401_UNAUTHORIZED,
219+
)
220+
221+
serializer = ImageAttachmentSerializer(data=request.data)
222+
if serializer.is_valid():
223+
serializer.save()
224+
return Response({"success": "Image uploaded"}, status=status.HTTP_200_OK)
225+
print("Serializer errors:", serializer.errors)
226+
return Response(
227+
{"failed": "Unable to upload image", "detail_error": serializer.errors},
228+
status=status.HTTP_400_BAD_REQUEST,
229+
)
230+
231+
232+
upload_image = ImageAttachment.as_view()
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Generated by Django 5.2.1 on 2025-05-26 17:08
2+
3+
import django.db.models.deletion
4+
from django.db import migrations, models
5+
6+
7+
class Migration(migrations.Migration):
8+
9+
dependencies = [
10+
("chat", "0003_groupmessage_tag_groupmessage_updated_at_and_more"),
11+
]
12+
13+
operations = [
14+
migrations.CreateModel(
15+
name="FileAttachment",
16+
fields=[
17+
(
18+
"id",
19+
models.BigAutoField(
20+
auto_created=True,
21+
primary_key=True,
22+
serialize=False,
23+
verbose_name="ID",
24+
),
25+
),
26+
("file_image", models.FileField(upload_to="file/")),
27+
("file_name", models.CharField(blank=True, max_length=30)),
28+
("uploaded_at", models.DateTimeField(auto_now_add=True)),
29+
("updated_at", models.DateTimeField(auto_now=True)),
30+
(
31+
"message_file",
32+
models.ForeignKey(
33+
on_delete=django.db.models.deletion.CASCADE,
34+
related_name="file_attachment",
35+
to="chat.groupmessage",
36+
),
37+
),
38+
],
39+
),
40+
migrations.CreateModel(
41+
name="ImageAttachement",
42+
fields=[
43+
(
44+
"id",
45+
models.BigAutoField(
46+
auto_created=True,
47+
primary_key=True,
48+
serialize=False,
49+
verbose_name="ID",
50+
),
51+
),
52+
("chat_image", models.ImageField(upload_to="image/")),
53+
("uploaded_at", models.DateTimeField(auto_now_add=True)),
54+
("updated_at", models.DateTimeField(auto_now=True)),
55+
(
56+
"messages_chat_file",
57+
models.ForeignKey(
58+
on_delete=django.db.models.deletion.CASCADE,
59+
related_name="chat_attachment",
60+
to="chat.groupmessage",
61+
),
62+
),
63+
],
64+
),
65+
migrations.DeleteModel(
66+
name="Attachement",
67+
),
68+
]

management/chat/models.py

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
from django.db import models
21
from core.models import User
2+
from django.db import models
33

44

55
class ChatGroup(models.Model):
@@ -8,6 +8,7 @@ class ChatGroup(models.Model):
88
def __str__(self):
99
return self.group_name
1010

11+
1112
class GroupMessage(models.Model):
1213
group = models.ForeignKey(
1314
ChatGroup, related_name="chat_messages", on_delete=models.CASCADE
@@ -31,26 +32,27 @@ class Meta:
3132
def __str__(self):
3233
return f"{self.author.username}: {self.body}"
3334

34-
class Attachement(models.Model):
35-
discussion= models.ForeignKey(GroupMessage, on_delete=models.CASCADE, related_name = 'attachment')
36-
chat_image = models.ImageField(upload_to="chat_image/")
37-
file_image = models.ImageField(upload_to='attachment/')
35+
36+
class ImageAttachement(models.Model):
37+
messages_chat_file = models.ForeignKey(
38+
GroupMessage, on_delete=models.CASCADE, related_name="chat_attachment"
39+
)
40+
chat_image = models.ImageField(upload_to="image/")
41+
uploaded_at = models.DateTimeField(auto_now_add=True)
42+
updated_at = models.DateTimeField(auto_now=True)
43+
44+
def __str__(self):
45+
return f"{self.messages_chat_file.author.username} uploaded an image"
46+
47+
48+
class FileAttachment(models.Model):
49+
message_file = models.ForeignKey(
50+
GroupMessage, on_delete=models.CASCADE, related_name="file_attachment"
51+
)
52+
file_image = models.FileField(upload_to="file/")
3853
file_name = models.CharField(max_length=30, blank=True)
39-
link = models.URLField(blank=True)
40-
reaction = models.CharField(max_length=50,blank=True)
4154
uploaded_at = models.DateTimeField(auto_now_add=True)
4255
updated_at = models.DateTimeField(auto_now=True)
4356

4457
def __str__(self):
45-
return (
46-
f'{{'
47-
f'"discussion": "{self.discussion.author.username}", '
48-
f'"chat_image": "{self.chat_image.url if self.chat_image else ""}", '
49-
f'"file_image": "{self.file_image.url if self.file_image else ""}", '
50-
f'"file_name": "{self.file_name}", '
51-
f'"link": "{self.link}", '
52-
f'"reaction": "{self.reaction}", '
53-
f'"uploaded_at": "{self.uploaded_at}", '
54-
f'"updated_at": "{self.updated_at}"'
55-
f'}}'
56-
)
58+
return f"{self.message_file.author.username} uploaded {self.file_name}"

management/chat/serializers.py

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
from chat.models import ChatGroup, GroupMessage
1+
from chat.models import ChatGroup, FileAttachment, GroupMessage, ImageAttachement
2+
from core.dumps import (
3+
FileAttachemenSize,
4+
FileAttachementExt,
5+
ImageAttachemenSize,
6+
ImageAttachementExt,
7+
)
28
from rest_framework import serializers
39

410

@@ -29,3 +35,37 @@ class ChatSerializers(serializers.ModelSerializer):
2935
class Meta:
3036
model = ChatGroup
3137
fields = "__all__"
38+
39+
40+
class ImageAttachmentSerializer(serializers.ModelSerializer):
41+
class Meta:
42+
model = ImageAttachement
43+
fields = "__all__"
44+
45+
def validate_chat_image(self, value):
46+
if not value.name.lower().endswith(tuple(ImageAttachementExt)):
47+
raise serializers.ValidationError(
48+
f"Your file has a {value.name} extension, please use one of {ImageAttachementExt}."
49+
)
50+
if value.size > ImageAttachemenSize:
51+
raise serializers.ValidationError(
52+
f"Your file size is {value.size} bytes, maximum allowed is 5MB."
53+
)
54+
return value
55+
56+
57+
class FileAttachmentSerializers(serializers.ModelSerializer):
58+
class Meta:
59+
model = FileAttachment
60+
fields = "__all__"
61+
62+
def validate_file_image(self, value):
63+
if not value.name.lower().endswith(tuple(FileAttachementExt)):
64+
raise serializers.ValidationError(
65+
f"Your file has a {value.name} extension, please use `.pdf` only."
66+
)
67+
if value.size > FileAttachemenSize:
68+
raise serializers.ValidationError(
69+
f"Your file size is {value.size} bytes, maximum allowed is 20MB."
70+
)
71+
return value

0 commit comments

Comments
 (0)