From 362777bf7cfe1d69a559e77d03825e89cca4479a Mon Sep 17 00:00:00 2001 From: Parsa Nazer Date: Sun, 23 Feb 2025 14:34:42 +0330 Subject: [PATCH] file attechment for ticket --- ..._message_attachments_ticket_attachments.py | 33 +++++++++++++++++++ backend/ticket/models.py | 18 ++++++++++ backend/ticket/serializers.py | 23 +++++++++++-- backend/ticket/urls.py | 6 ++-- backend/ticket/views.py | 26 ++++++++++++++- 5 files changed, 101 insertions(+), 5 deletions(-) create mode 100644 backend/ticket/migrations/0013_attachment_message_attachments_ticket_attachments.py diff --git a/backend/ticket/migrations/0013_attachment_message_attachments_ticket_attachments.py b/backend/ticket/migrations/0013_attachment_message_attachments_ticket_attachments.py new file mode 100644 index 0000000..5057bad --- /dev/null +++ b/backend/ticket/migrations/0013_attachment_message_attachments_ticket_attachments.py @@ -0,0 +1,33 @@ +# Generated by Django 5.1.2 on 2025-02-22 22:50 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ticket', '0012_alter_ticket_status'), + ] + + operations = [ + migrations.CreateModel( + name='Attachment', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('file', models.FileField(upload_to='attachments')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('size', models.PositiveIntegerField(blank=True, null=True)), + ('name', models.CharField(blank=True, max_length=400, null=True)), + ], + ), + migrations.AddField( + model_name='message', + name='attachments', + field=models.ManyToManyField(blank=True, related_name='messages', to='ticket.attachment'), + ), + migrations.AddField( + model_name='ticket', + name='attachments', + field=models.ManyToManyField(blank=True, related_name='tickets', to='ticket.attachment'), + ), + ] diff --git a/backend/ticket/models.py b/backend/ticket/models.py index edfd231..72f9502 100644 --- a/backend/ticket/models.py +++ b/backend/ticket/models.py @@ -3,6 +3,22 @@ from account.models import User from order.models import OrderModel from django_jalali.db import models as jmodels + + +class Attachment(models.Model): + file = models.FileField(upload_to='attachments') + created_at = models.DateTimeField(auto_now_add=True) + size = models.PositiveIntegerField(null=True, blank=True) + name = models.CharField(max_length=400, null=True, blank=True) + def __str__(self): + return self.file.name + + def save(self, *args, **kwargs): + if self.file: + self.size = self.file.size + self.name = self.file.name + super(Attachment, self).save(*args, **kwargs) + class Ticket(models.Model): objects = jmodels.jManager() STATUS_CHOICES = [ @@ -28,6 +44,7 @@ class Ticket(models.Model): created_at = jmodels.jDateTimeField(auto_now_add=True, verbose_name='ساخته شده در') updated_at = jmodels.jDateTimeField(auto_now=True, verbose_name='اپدیت شده در') order = models.ForeignKey(OrderModel ,blank=True, null=True, on_delete=models.SET_NULL) + attachments = models.ManyToManyField(Attachment, related_name='tickets', blank=True) def __str__(self): return self.subject @@ -44,6 +61,7 @@ class Message(models.Model): sender = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='ارسال کننده') content = models.TextField(verbose_name='محتوای پیام') created_at = jmodels.jDateTimeField(auto_now_add=True, verbose_name='ساخته شده در') + attachments = models.ManyToManyField(Attachment, related_name='messages', blank=True) def __str__(self): return f"Message by {self.sender.full_name} on {self.ticket.subject}" diff --git a/backend/ticket/serializers.py b/backend/ticket/serializers.py index 5c7c24f..bd7611f 100644 --- a/backend/ticket/serializers.py +++ b/backend/ticket/serializers.py @@ -1,7 +1,10 @@ from rest_framework import serializers -from .models import Ticket, Message +from .models import Ticket, Message, Attachment from django.utils.timezone import localtime from account.serializers import ProfileSerializer + + + class MessageSerializer(serializers.ModelSerializer): class Meta: model = Message @@ -20,4 +23,20 @@ class TicketListSerializer(serializers.ModelSerializer): class Meta: model = Ticket exclude = ('customer', 'admin', 'order', 'content') - read_only_fields = ('status',) \ No newline at end of file + read_only_fields = ('status',) + + +class AttachmentSerializer(serializers.ModelSerializer): + file = serializers.FileField(write_only=True) + link = serializers.SerializerMethodField() + + class Meta: + model = Attachment + fields = ['id', 'name', 'file','link' , 'created_at', 'size'] + read_only_fields = ('size', 'name', ) + + def get_link(self, obj): + request = self.context.get('request') + if request is not None: + return request.build_absolute_uri(obj.file.url) + return obj.file.url \ No newline at end of file diff --git a/backend/ticket/urls.py b/backend/ticket/urls.py index 7ad437e..759ac06 100644 --- a/backend/ticket/urls.py +++ b/backend/ticket/urls.py @@ -4,12 +4,14 @@ from .views import ( TicketListView, TicketDetailView, MessageCreateView, - UpdateTicketStatusView + UpdateTicketStatusView, + AttachmentUploadView ) urlpatterns = [ path('create', TicketCreateView.as_view(), name='ticket-create'), path('', TicketListView.as_view(), name='ticket-list'), path('', TicketDetailView.as_view(), name='ticket-detail'), - path('message/', MessageCreateView.as_view(), name='message-create'), + path('message/create', MessageCreateView.as_view(), name='message-create'), + path('attachment/create', AttachmentUploadView.as_view(), name='attachment-upload'), ] \ No newline at end of file diff --git a/backend/ticket/views.py b/backend/ticket/views.py index d8a5e10..4c3b3d9 100644 --- a/backend/ticket/views.py +++ b/backend/ticket/views.py @@ -2,9 +2,33 @@ from rest_framework import generics, permissions from rest_framework.response import Response from rest_framework.views import APIView from .models import Ticket, Message -from .serializers import TicketListSerializer, MessageSerializer, TicketSerializer +from .serializers import TicketListSerializer, MessageSerializer, TicketSerializer, AttachmentSerializer from utils.pagination import StructurePagination from drf_spectacular.utils import extend_schema, OpenApiParameter, OpenApiTypes +from rest_framework.permissions import IsAuthenticated +from rest_framework.parsers import MultiPartParser, FormParser +from drf_spectacular.utils import extend_schema, OpenApiParameter, OpenApiExample, OpenApiTypes, OpenApiResponse +from rest_framework import status + + + +class AttachmentUploadView(APIView): + permission_classes = [IsAuthenticated] + parser_classes = [MultiPartParser, FormParser] + + @extend_schema( + request=AttachmentSerializer, + responses={201: AttachmentSerializer}, + description="upload an attachment (file).", + ) + def post(self, request, *args, **kwargs): + serializer = AttachmentSerializer(data=request.data, context={'request': request}) + if serializer.is_valid(): + serializer.save() + return Response(serializer.data, status=status.HTTP_201_CREATED) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + class TicketCreateView(generics.CreateAPIView): queryset = Ticket.objects.all()