feat(media): add client and project thumbnails

This commit is contained in:
2026-05-26 12:15:09 +03:30
parent f99e883f12
commit e42e0612aa
10 changed files with 199 additions and 57 deletions

View File

@@ -6,6 +6,19 @@ from apps.projects.models import Project
from apps.workspaces.models import PriceUnit
def validate_thumbnail(value):
if value is None:
return value
max_bytes = 2 * 1024 * 1024
if getattr(value, "size", 0) > max_bytes:
raise serializers.ValidationError("Image size must be 2MB or less.")
content_type = (getattr(value, "content_type", "") or "").lower()
allowed_types = {"image/jpeg", "image/png", "image/webp"}
if content_type and content_type not in allowed_types:
raise serializers.ValidationError("Unsupported image type. Use JPG, PNG, or WebP.")
return value
class ProjectSerializer(BaseModelSerializer):
class Meta:
model = Project
@@ -14,6 +27,7 @@ class ProjectSerializer(BaseModelSerializer):
"name",
"client",
"description",
"thumbnail",
"is_archived",
"color",
)
@@ -21,12 +35,25 @@ class ProjectSerializer(BaseModelSerializer):
def to_representation(self, instance):
representation = super().to_representation(instance)
request = self.context.get("request")
if instance.thumbnail:
thumbnail_url = instance.thumbnail.url
representation["thumbnail"] = request.build_absolute_uri(thumbnail_url) if request else thumbnail_url
else:
representation["thumbnail"] = None
if instance.client:
representation['client'] = {
'id': instance.client.id,
'name': instance.client.name
}
return representation
'id': instance.client.id,
'name': instance.client.name,
'thumbnail': (
request.build_absolute_uri(instance.client.thumbnail.url)
if request and instance.client.thumbnail
else instance.client.thumbnail.url
if instance.client.thumbnail
else None
),
}
return representation
class ProjectCreateSerializer(serializers.Serializer):
@@ -34,16 +61,25 @@ class ProjectCreateSerializer(serializers.Serializer):
name = serializers.CharField(max_length=255)
client = serializers.UUIDField(required=False, allow_null=True)
description = serializers.CharField(required=False, allow_blank=True, default="")
thumbnail = serializers.ImageField(required=False, allow_null=True)
color = serializers.CharField(max_length=7, required=False, allow_blank=True, default="")
def validate_thumbnail(self, value):
return validate_thumbnail(value)
class ProjectUpdateSerializer(serializers.Serializer):
name = serializers.CharField(max_length=255, required=False)
client = serializers.UUIDField(required=False, allow_null=True)
description = serializers.CharField(required=False, allow_blank=True)
thumbnail = serializers.ImageField(required=False, allow_null=True)
clear_thumbnail = serializers.BooleanField(required=False, default=False)
color = serializers.CharField(max_length=7, required=False, allow_blank=True)
is_archived = serializers.BooleanField(required=False)
def validate_thumbnail(self, value):
return validate_thumbnail(value)
class ProjectAccessQuerySerializer(serializers.Serializer):
workspace = serializers.UUIDField()

View File

@@ -89,6 +89,9 @@ class ProjectViewSet(ModelViewSet):
if client_ids:
queryset = queryset.filter(client_id__in=client_ids)
if "is_archived" not in self.request.query_params:
queryset = queryset.filter(is_archived=False)
return queryset
def get_serializer_class(self):
@@ -120,13 +123,14 @@ class ProjectViewSet(ModelViewSet):
project = create_project(
user=request.user,
workspace=workspace,
name=serializer.validated_data["name"],
client=client,
name=serializer.validated_data["name"],
client=client,
description=serializer.validated_data.get("description", ""),
color=serializer.validated_data.get("color", "")
color=serializer.validated_data.get("color", ""),
thumbnail=serializer.validated_data.get("thumbnail"),
)
output_serializer = ProjectSerializer(project)
output_serializer = ProjectSerializer(project, context={"request": request})
return Response(output_serializer.data, status=status.HTTP_201_CREATED)
def update(self, request, *args, **kwargs):
@@ -144,7 +148,7 @@ class ProjectViewSet(ModelViewSet):
**serializer.validated_data
)
output_serializer = ProjectSerializer(updated_project)
output_serializer = ProjectSerializer(updated_project, context={"request": request})
return Response(output_serializer.data, status=status.HTTP_200_OK)
def destroy(self, request, *args, **kwargs):
@@ -169,7 +173,7 @@ class ProjectViewSet(ModelViewSet):
project = self.get_object()
updated_project = toggle_project_archive(project)
output_serializer = ProjectSerializer(updated_project)
output_serializer = ProjectSerializer(updated_project, context={"request": request})
return Response(output_serializer.data, status=status.HTTP_200_OK)
@action(detail=False, methods=["get"], url_path="access")