Browse Source

feat(hadis): add bulk assign topics action to admin viewset

- Add bulk-assign-topics endpoint to AdminHadisViewSet supporting add/replace modes
- Validate leaf topic constraints and handle both M2M categories and primary category_id
- Invalidate hadis cache on bulk updates and return updated count with category details
- Expand AdminHadisBriefSerializer with slug, title_narrator, and status fields
master
Mohsen Taba 2 weeks ago
parent
commit
85b11c31a7
  1. 3
      apps/hadis/serializers/serializers_admin.py
  2. 101
      apps/hadis/views_admin.py

3
apps/hadis/serializers/serializers_admin.py

@ -782,7 +782,8 @@ class AdminHadisDetailSerializer(serializers.ModelSerializer):
class AdminHadisBriefSerializer(serializers.ModelSerializer):
class Meta:
model = Hadis
fields = ["id", "number", "title"]
fields = ["id", "number", "slug", "title", "title_narrator", "status"]
class AdminHadisCategoryBriefSerializer(serializers.ModelSerializer):

101
apps/hadis/views_admin.py

@ -443,6 +443,107 @@ class AdminHadisViewSet(ModelViewSet):
return queryset.order_by("-id")
@action(detail=False, methods=["post"], url_path="bulk-assign-topics")
def bulk_assign_topics(self, request):
from django.db import transaction
from rest_framework import status as drf_status
from .signals import invalidate_hadis_cache
hadis_ids = request.data.get("hadis_ids") or []
category_ids = request.data.get("category_ids") or []
mode = request.data.get("mode", "add")
if not isinstance(hadis_ids, list) or not hadis_ids:
return Response(
{"detail": "hadis_ids must be a non-empty list."},
status=drf_status.HTTP_400_BAD_REQUEST,
)
if not isinstance(category_ids, list) or not category_ids:
return Response(
{"detail": "category_ids must be a non-empty list."},
status=drf_status.HTTP_400_BAD_REQUEST,
)
try:
hadis_ids = [int(hid) for hid in hadis_ids]
category_ids = list(dict.fromkeys(int(cid) for cid in category_ids))
except (ValueError, TypeError):
return Response(
{"detail": "Invalid ID format."},
status=drf_status.HTTP_400_BAD_REQUEST,
)
categories = list(HadisCategory.objects.filter(id__in=category_ids))
if len(categories) != len(category_ids):
return Response(
{"detail": "One or more selected topics do not exist."},
status=drf_status.HTTP_400_BAD_REQUEST,
)
for cat in categories:
if cat.children.exists():
return Response(
{"detail": "Cannot assign items to a parent category that has subcategories. Please select leaf topics only."},
status=drf_status.HTTP_400_BAD_REQUEST,
)
hadises_qs = Hadis.objects.filter(id__in=hadis_ids)
existing_hadis_ids = list(hadises_qs.values_list("id", flat=True))
if not existing_hadis_ids:
return Response(
{"detail": "No matching items found."},
status=drf_status.HTTP_404_NOT_FOUND,
)
through_model = Hadis.categories.through
first_category_id = category_ids[0]
with transaction.atomic():
if mode == "replace":
through_model.objects.filter(hadis_id__in=existing_hadis_ids).delete()
relations = [
through_model(hadis_id=hid, hadiscategory_id=cid)
for hid in existing_hadis_ids
for cid in category_ids
]
through_model.objects.bulk_create(relations, ignore_conflicts=True)
Hadis.objects.filter(id__in=existing_hadis_ids).update(category_id=first_category_id)
else:
relations = [
through_model(hadis_id=hid, hadiscategory_id=cid)
for hid in existing_hadis_ids
for cid in category_ids
]
through_model.objects.bulk_create(relations, ignore_conflicts=True)
Hadis.objects.filter(id__in=existing_hadis_ids, category__isnull=True).update(
category_id=first_category_id
)
invalidate_hadis_cache()
cat_map = {c.id: c for c in categories}
ordered_cats = [cat_map[cid] for cid in category_ids if cid in cat_map]
categories_detail = [
{
"id": c.id,
"title": c.title,
"slug": c.slug,
"source_type": c.source_type,
"is_topic": True,
}
for c in ordered_cats
]
return Response(
{
"updated_count": len(existing_hadis_ids),
"mode": mode,
"categories_detail": categories_detail,
},
status=drf_status.HTTP_200_OK,
)
class AdminHadisCorrectionViewSet(ModelViewSet):
serializer_class = AdminHadisCorrectionSerializer

Loading…
Cancel
Save