feat(workspaces): add thumbnail upload and lifecycle support

This commit is contained in:
2026-04-28 11:38:35 +03:30
parent 76f02dc259
commit 315f2ca728
4 changed files with 91 additions and 17 deletions

View File

@@ -1,4 +1,5 @@
from decimal import Decimal
import json
from rest_framework import serializers
@@ -16,7 +17,8 @@ class WorkspaceMemberInputSerializer(serializers.Serializer):
class WorkspaceSerializer(BaseModelSerializer):
members = WorkspaceMemberInputSerializer(many=True, write_only=True, required=False)
members = serializers.JSONField(write_only=True, required=False)
clear_thumbnail = serializers.BooleanField(write_only=True, required=False, default=False)
my_role = serializers.SerializerMethodField()
class Meta:
@@ -24,6 +26,8 @@ class WorkspaceSerializer(BaseModelSerializer):
fields = BaseModelSerializer.Meta.fields + (
"name",
"description",
"thumbnail",
"clear_thumbnail",
"owner",
"my_role",
"members",
@@ -39,8 +43,41 @@ class WorkspaceSerializer(BaseModelSerializer):
).first()
return getattr(membership, "role", None)
def validate_thumbnail(self, 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
def to_representation(self, instance):
data = super().to_representation(instance)
request = self.context.get("request")
if instance.thumbnail:
thumbnail_url = instance.thumbnail.url
data["thumbnail"] = request.build_absolute_uri(thumbnail_url) if request else thumbnail_url
else:
data["thumbnail"] = None
data.pop("clear_thumbnail", None)
return data
def create(self, validated_data):
members_data = validated_data.pop('members', [])
members_data = validated_data.pop("members", [])
if isinstance(members_data, str):
try:
members_data = json.loads(members_data)
except json.JSONDecodeError as exc:
raise serializers.ValidationError({"members": "Invalid members format."}) from exc
if members_data:
members_serializer = WorkspaceMemberInputSerializer(data=members_data, many=True)
members_serializer.is_valid(raise_exception=True)
members_data = members_serializer.validated_data
validated_data.pop("clear_thumbnail", None)
workspace = super().create(validated_data)
@@ -75,6 +112,23 @@ class WorkspaceSerializer(BaseModelSerializer):
return workspace
def update(self, instance, validated_data):
clear_thumbnail = validated_data.pop("clear_thumbnail", False)
old_thumbnail_name = instance.thumbnail.name if instance.thumbnail else None
if clear_thumbnail and instance.thumbnail:
instance.thumbnail.delete(save=False)
instance.thumbnail = None
updated_workspace = super().update(instance, validated_data)
if old_thumbnail_name and updated_workspace.thumbnail and updated_workspace.thumbnail.name != old_thumbnail_name:
storage = updated_workspace.thumbnail.storage
if storage.exists(old_thumbnail_name):
storage.delete(old_thumbnail_name)
return updated_workspace
class WorkspaceMembershipSerializer(BaseModelSerializer):
user = serializers.SerializerMethodField()

View File

@@ -7,6 +7,7 @@ from rest_framework.filters import OrderingFilter, SearchFilter
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.viewsets import ModelViewSet
from rest_framework.permissions import IsAuthenticated
from rest_framework.parsers import FormParser, MultiPartParser, JSONParser
from apps.notifications.services import (
notify_workspace_membership_added,
@@ -45,6 +46,7 @@ from core.paginations.limit_offset import CustomLimitOffsetPagination
class WorkspaceViewSet(ModelViewSet):
serializer_class = WorkspaceSerializer
parser_classes = [MultiPartParser, FormParser, JSONParser]
pagination_class = CustomLimitOffsetPagination
filter_backends = (DjangoFilterBackend, OrderingFilter, SearchFilter)
filterset_class = WorkspaceFilter

View File

@@ -0,0 +1,17 @@
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("workspaces", "0005_remove_priceunit_priceunit_id_idx_and_more"),
]
operations = [
migrations.AddField(
model_name="workspace",
name="thumbnail",
field=models.ImageField(blank=True, null=True, upload_to="profile/workspaces/"),
),
]

View File

@@ -9,6 +9,7 @@ User = get_user_model()
class Workspace(BaseModel):
name = models.CharField(max_length=255)
description = models.TextField(blank=True)
thumbnail = models.ImageField(upload_to="profile/workspaces/", blank=True, null=True)
owner = models.ForeignKey(
User,
on_delete=models.PROTECT,