Allow deleting logos that are assigned to channels.

This commit is contained in:
SergeantPanda 2025-07-18 11:36:15 -05:00
parent 23bd5484ee
commit e7771d5b67
4 changed files with 38 additions and 21 deletions

View file

@ -1053,24 +1053,27 @@ class BulkDeleteLogosAPIView(APIView):
def delete(self, request):
logo_ids = request.data.get("logo_ids", [])
# Check if any logos are being used by channels
used_logos = Logo.objects.filter(
id__in=logo_ids,
channels__isnull=False
).distinct()
# Get logos and their usage info before deletion
logos_to_delete = Logo.objects.filter(id__in=logo_ids)
total_channels_affected = 0
for logo in logos_to_delete:
if logo.channels.exists():
channel_count = logo.channels.count()
total_channels_affected += channel_count
# Remove logo from channels
logo.channels.update(logo=None)
logger.info(f"Removed logo {logo.name} from {channel_count} channels before deletion")
if used_logos.exists():
used_names = list(used_logos.values_list('name', flat=True))
return Response(
{"error": f"Cannot delete logos that are in use: {', '.join(used_names)}"},
status=status.HTTP_400_BAD_REQUEST
)
# Delete logos
deleted_count = logos_to_delete.delete()[0]
# Delete logos that are not in use
deleted_count = Logo.objects.filter(id__in=logo_ids).delete()[0]
message = f"Successfully deleted {deleted_count} logos"
if total_channels_affected > 0:
message += f" and removed them from {total_channels_affected} channels"
return Response(
{"message": f"Successfully deleted {deleted_count} logos"},
{"message": message},
status=status.HTTP_204_NO_CONTENT
)
@ -1152,15 +1155,14 @@ class LogoViewSet(viewsets.ModelViewSet):
return super().update(request, *args, **kwargs)
def destroy(self, request, *args, **kwargs):
"""Delete a logo"""
"""Delete a logo and remove it from any channels using it"""
logo = self.get_object()
# Check if logo is being used by any channels
# Instead of preventing deletion, remove the logo from channels
if logo.channels.exists():
return Response(
{"error": f"Cannot delete logo as it is used by {logo.channels.count()} channel(s)"},
status=status.HTTP_400_BAD_REQUEST
)
channel_count = logo.channels.count()
logo.channels.update(logo=None)
logger.info(f"Removed logo {logo.name} from {channel_count} channels before deletion")
return super().destroy(request, *args, **kwargs)

View file

@ -28,6 +28,16 @@ class LogoSerializer(serializers.ModelSerializer):
model = Logo
fields = ["id", "name", "url", "cache_url", "channel_count", "is_used", "channel_names"]
def validate_url(self, value):
"""Validate that the URL is unique for creation or update"""
if self.instance and self.instance.url == value:
return value
if Logo.objects.filter(url=value).exists():
raise serializers.ValidationError("A logo with this URL already exists.")
return value
def get_cache_url(self, obj):
# return f"/api/channels/logos/{obj.id}/cache/"
request = self.context.get("request")

View file

@ -68,6 +68,8 @@ const LogoForm = ({ logo = null, isOpen, onClose }) => {
// Handle specific timeout errors
if (error.code === 'NETWORK_ERROR' || error.message?.includes('timeout')) {
errorMessage = 'Request timed out. Please try again.';
} else if (error.response?.data?.error) {
errorMessage = error.response.data.error;
}
notifications.show({

View file

@ -718,6 +718,9 @@ const LogosTable = () => {
isBulkDelete ? (
<div>
Are you sure you want to delete {selectedRows.size} selected logos?
<Text size="sm" c="dimmed" mt="xs">
Any channels using these logos will have their logo removed.
</Text>
<Text size="sm" c="dimmed" mt="xs">
This action cannot be undone.
</Text>
@ -727,7 +730,7 @@ const LogosTable = () => {
Are you sure you want to delete the logo "{logoToDelete.name}"?
{logoToDelete.channel_count > 0 && (
<Text size="sm" c="orange" mt="xs">
Warning: This logo is currently used by {logoToDelete.channel_count} channel{logoToDelete.channel_count !== 1 ? 's' : ''}.
This logo is currently used by {logoToDelete.channel_count} channel{logoToDelete.channel_count !== 1 ? 's' : ''}. They will have their logo removed.
</Text>
)}
<Text size="sm" c="dimmed" mt="xs">