2 Commits

  1. 8
      apps/hadis/models/category.py
  2. 9
      apps/hadis/models/hadis.py
  3. 18
      apps/hadis/serializers/category.py
  4. 53
      apps/hadis/serializers/serializers_admin.py
  5. 1
      apps/hadis/views/category.py
  6. 17
      apps/hadis/views_admin.py

8
apps/hadis/models/category.py

@ -99,6 +99,14 @@ class HadisCategory(LowercaseSlugMixin, MPTTModel):
f'Parent source_type: {self.parent.source_type}, '
f'Your source_type: {self.source_type}')
)
if self.parent and self.parent.hadis_set.exists():
raise ValidationError(
_('Cannot set parent to a category that already contains hadiths/content items.')
)
if self.pk and self.hadis_set.exists() and self.children.exists():
raise ValidationError(
_('A category cannot have both hadiths and subcategories.')
)
slug_source_field = 'title'

9
apps/hadis/models/hadis.py

@ -2,6 +2,7 @@ from enum import unique
from typing import Optional
from django.db import models
from django.db.models import F, ForeignKey
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _
from django.conf import settings
from django.utils.text import slugify
@ -186,7 +187,15 @@ class Hadis(LowercaseSlugMixin, models.Model):
title = self.title[0]['text'] if self.title else f"Hadis {self.number}"
return f"{self.number} - {title}"
def clean(self):
super().clean()
if self.category and self.category.children.exists():
raise ValidationError(
_('Cannot assign hadith to a category that has subcategories. Only leaf categories can contain hadiths.')
)
def save(self, *args, **kwargs):
self.clean()
# Generate slug if not already set or if slug is empty
if not self.slug or (isinstance(self.slug, str) and self.slug.strip() == ''):
title_text = None

18
apps/hadis/serializers/category.py

@ -166,13 +166,17 @@ class HadisCategorySelectSerializer(serializers.ModelSerializer):
children_count = serializers.SerializerMethodField()
has_hadis = serializers.SerializerMethodField()
hadis_count= serializers.SerializerMethodField()
has_parent = serializers.SerializerMethodField()
title = LocalizedField()
description =LocalizedField()
class Meta:
model = HadisCategory
fields = ['id', 'title', 'source_type','slug', 'sect_id',
'sect_type','description','children_count','has_hadis','hadis_count', 'share_link', 'xmind_share_link']
'sect_type','description','children_count','has_hadis','hadis_count', 'has_parent', 'share_link', 'xmind_share_link']
def get_has_parent(self, obj):
return obj.parent_id is not None
def get_has_hadis(self, obj):
"""Check if category can have hadis (no active children) and has hadis"""
@ -212,6 +216,7 @@ class HadisCategorySelectSourceSerializer(serializers.ModelSerializer):
children_count = serializers.SerializerMethodField()
has_hadis = serializers.SerializerMethodField()
hadis_count = serializers.SerializerMethodField()
has_parent = serializers.SerializerMethodField()
title = LocalizedField()
description = LocalizedField()
@ -219,7 +224,10 @@ class HadisCategorySelectSourceSerializer(serializers.ModelSerializer):
class Meta:
model = HadisCategory
fields = ['id', 'title', 'source_type','slug', 'sect_id',
'sect_type','description','children_count','has_hadis','hadis_count', 'share_link', 'xmind_share_link']
'sect_type','description','children_count','has_hadis','hadis_count', 'has_parent', 'share_link', 'xmind_share_link']
def get_has_parent(self, obj):
return obj.parent_id is not None
def get_has_hadis(self, obj):
"""Check if category can have hadis (no active children) and has hadis"""
@ -253,6 +261,7 @@ class CategorySerializer(serializers.ModelSerializer):
children_count = serializers.SerializerMethodField()
has_hadis =serializers.SerializerMethodField()
hadis_count=serializers.SerializerMethodField()
has_parent = serializers.SerializerMethodField()
title = LocalizedField()
description = LocalizedField()
@ -261,7 +270,10 @@ class CategorySerializer(serializers.ModelSerializer):
model = HadisCategory
fields = ['id', 'title', 'sect_id', 'sect_type','source_type',
'description','slug',
'children_count','has_hadis','hadis_count', 'share_link', 'xmind_share_link']
'children_count','has_hadis','hadis_count', 'has_parent', 'share_link', 'xmind_share_link']
def get_has_parent(self, obj):
return obj.parent_id is not None
def get_children_count(self, obj):
# """Get count of active children categories that have children or hadis"""

53
apps/hadis/serializers/serializers_admin.py

@ -1,5 +1,6 @@
from rest_framework import serializers
from django.core.files.uploadedfile import SimpleUploadedFile
from django.utils.translation import gettext_lazy as _
from utils.image_compression import maybe_compress_uploaded_file
from utils import absolute_https_url
@ -41,11 +42,30 @@ class AdminHadisCategorySerializer(serializers.ModelSerializer):
sect_type = serializers.CharField(source='sect.sect_type', read_only=True)
title = LocalizedField()
description = LocalizedField()
has_children = serializers.SerializerMethodField()
is_leaf = serializers.SerializerMethodField()
has_hadith = serializers.SerializerMethodField()
def get_has_children(self, obj):
if hasattr(obj, "annotated_has_children"):
return bool(obj.annotated_has_children)
return obj.children.exists()
def get_is_leaf(self, obj):
if hasattr(obj, "annotated_has_children"):
return not bool(obj.annotated_has_children)
return not obj.children.exists()
def get_has_hadith(self, obj):
if hasattr(obj, "annotated_has_hadith"):
return bool(obj.annotated_has_hadith)
return obj.hadis_set.exists()
class Meta:
model = HadisCategory
fields = [
"id",
"parent",
"title",
"slug",
"source_type",
@ -53,6 +73,9 @@ class AdminHadisCategorySerializer(serializers.ModelSerializer):
"sect",
"sect_type",
"description",
"has_children",
"is_leaf",
"has_hadith",
]
@ -240,6 +263,13 @@ class AdminHadisDetailSerializer(serializers.ModelSerializer):
from apps.bookmark.models.bookmark import Bookmark
return Bookmark.objects.filter(service=Bookmark.ServiceChoices.HADITH, content_id=obj.id, status=True).count()
def validate_category(self, value):
if value and value.children.exists():
raise serializers.ValidationError(
_("Cannot assign a hadith/item to a category that has subcategories. Only leaf categories can contain hadiths.")
)
return value
def get_collections_detail(self, obj):
collections = [item.collection for item in obj.collection_items.all()]
return AdminHadisCollectionSerializer(collections, many=True).data
@ -607,6 +637,8 @@ class AdminHadisCategoryArchiveSerializer(serializers.ModelSerializer):
xmind_file = AbsoluteFileField(required=False, allow_null=True)
remove_xmind_file = serializers.BooleanField(write_only=True, required=False, default=False)
has_hadith = serializers.SerializerMethodField()
has_children = serializers.SerializerMethodField()
is_leaf = serializers.SerializerMethodField()
parent = serializers.PrimaryKeyRelatedField(
queryset=HadisCategory.objects.all(),
@ -619,8 +651,27 @@ class AdminHadisCategoryArchiveSerializer(serializers.ModelSerializer):
)
def get_has_hadith(self, obj):
if hasattr(obj, "annotated_has_hadith"):
return bool(obj.annotated_has_hadith)
return obj.hadis_set.exists()
def get_has_children(self, obj):
if hasattr(obj, "annotated_has_children"):
return bool(obj.annotated_has_children)
return obj.children.exists()
def get_is_leaf(self, obj):
if hasattr(obj, "annotated_has_children"):
return not bool(obj.annotated_has_children)
return not obj.children.exists()
def validate_parent(self, value):
if value and value.hadis_set.exists():
raise serializers.ValidationError(
_("Cannot set parent to a category that already contains hadiths/content items.")
)
return value
class Meta:
model = HadisCategory
fields = [
@ -639,6 +690,8 @@ class AdminHadisCategoryArchiveSerializer(serializers.ModelSerializer):
"share_link",
"xmind_share_link",
"has_hadith",
"has_children",
"is_leaf",
]
read_only_fields = ["id", "share_link", "xmind_share_link"]

1
apps/hadis/views/category.py

@ -196,6 +196,7 @@ class HadisCategoryTreeView(ListAPIView):
'hadis_count': category.direct_hadis_count,
'children_count': recursive_counts.get(category.id, 0),
'has_hadis': has_hadis,
'has_parent': category.parent_id is not None,
'hadis_ids': hadis_ids_map.get(category.id, []) if has_hadis else [],
# 👇 کلید جدید: تزریق دیتای قرآن دقیقاً داخل نودِ کتگوری
'quran_data': quran_data_map.get(category.id, None) if category.source_type == HadisCategory.SourceType.QURAN else None,

17
apps/hadis/views_admin.py

@ -1,5 +1,4 @@
from django.db.models import Q
from django.db.models import Q
from django.db.models import Q, Exists, OuterRef
from rest_framework.authentication import TokenAuthentication
from rest_framework.decorators import action
from rest_framework.parsers import FormParser, JSONParser, MultiPartParser
@ -55,7 +54,12 @@ class AdminHadisCategoryViewSet(ReadOnlyModelViewSet):
pagination_class = None
def get_queryset(self):
queryset = HadisCategory.objects.all()
hadis_subquery = Hadis.objects.filter(category=OuterRef("pk"))
children_subquery = HadisCategory.objects.filter(parent=OuterRef("pk"))
queryset = HadisCategory.objects.select_related("parent", "sect").annotate(
annotated_has_hadith=Exists(hadis_subquery),
annotated_has_children=Exists(children_subquery),
)
source_type = self.request.query_params.get("source_type")
if source_type:
queryset = queryset.filter(source_type=source_type)
@ -89,7 +93,12 @@ class DovodiHadisCategoryViewSet(ModelViewSet):
parser_classes = (JSONParser, MultiPartParser, FormParser)
def get_queryset(self):
queryset = HadisCategory.objects.select_related("parent", "sect")
hadis_subquery = Hadis.objects.filter(category=OuterRef("pk"))
children_subquery = HadisCategory.objects.filter(parent=OuterRef("pk"))
queryset = HadisCategory.objects.select_related("parent", "sect").annotate(
annotated_has_hadith=Exists(hadis_subquery),
annotated_has_children=Exists(children_subquery),
)
search_query = self.request.query_params.get("search")
if search_query:
queryset = queryset.filter(

Loading…
Cancel
Save