from django.contrib import admin <<<<<<< HEAD from django.utils.translation import gettext_lazy as _ from dj_category.admin import BaseCategoryAdmin from ajaxdatatable.admin import AjaxDatatable from django.http import JsonResponse from django.urls import path from apps.hadis.models import * from django import forms from django.db.models import Case, When, Value @admin.register(HadisCategory) class HadisCategoryAdmin(BaseCategoryAdmin): change_form_template = 'admin/hadiscategory/change_form.html' change_list_template = 'admin/category_index.html' fields = ( 'name', 'source_type', 'category_type' , 'parent', 'is_active', 'order' ) search_fields = ['name'] def get_form(self, request, obj=None, **kwargs): form = super().get_form(request, obj, **kwargs) return form def get_urls(self): urls = super().get_urls() custom_urls = [ path('categories-ajax/hadiscategory/', self.admin_site.admin_view(self.ajax_categories), name='hadiscategory_ajax_categories'), ] return custom_urls + urls def get_categories_groupby_language(self, request=None, selected_values=(), is_multiple=False): print(f'--get_categories_groupby_language-> {selected_values}') return super().get_categories(request, selected_values, is_multiple) def ajax_update(self, request): data = request.POST src_node = self.model.objects.get(pk=int(data['srcNode'])) other_node = self.model.objects.get(pk=int(data['otherNode'])) print(f'--ajax_update-> {data}') if src_node.slug in self.base_categories or other_node.slug in self.base_categories: return JsonResponse({'data': _('This item can not be modifed')}, status=401) mode = data['hitMode'] if mode == 'over': src_node.move_to(other_node, 'first-child') elif mode == 'after': src_node.move_to(other_node, 'right') elif mode == 'before': src_node.move_to(other_node, 'left') return JsonResponse({'data': 'ok'}, safe=False) def get_categories(self, request=None, selected_values=(), is_multiple=False): """ Override the get_categories method to filter by source_type if provided in the request """ categories = super().get_categories(request, selected_values, is_multiple) # If request has source_type parameter, filter the categories if request and request.GET.get('source_type'): source_type = request.GET.get('source_type') # Filter the categories by source_type filtered_categories = [] for category in categories: # If it's a dictionary (serialized category) if isinstance(category, dict) and category.get('source_type') == source_type: filtered_categories.append(category) # If it's a model instance elif hasattr(category, 'source_type') and getattr(category, 'source_type') == source_type: filtered_categories.append(category) return filtered_categories return categories def ajax_categories(self, request): """ Handle AJAX request for categories with source_type filtering and search """ # Get source_type from request source_type = request.GET.get('source_type') # Get node_id if provided (for single node data) node_id = request.GET.get('node_id') # Get search term if provided search = request.GET.get('search') # Get parent level filter if provided parent_level = request.GET.get('parent_level') if node_id: # Return data for a specific node try: node = self.model.objects.get(pk=int(node_id)) return JsonResponse({ 'id': node.id, 'source_type': node.source_type, 'category_type': node.category_type, 'parent': node.parent_id, 'level': node.level_p # Add the level_p property }) except self.model.DoesNotExist: return JsonResponse({'error': 'Node not found'}, status=404) # Get all categories queryset = self.model.objects.all() # Annotate queryset with level_p queryset = queryset.annotate( level_pp=Case( When(parent=None, then=Value(1)), When(parent__isnull=False, parent__parent=None, then=Value(2)), default=Value(3), output_field=models.IntegerField() ) ) # Filter by source_type if provided if source_type: queryset = queryset.filter(source_type=source_type) # Filter by search term if provided if search: queryset = queryset.filter(name__icontains=search) # Filter by parent_level if provided if parent_level and parent_level.isdigit(): # Convert to integer level = int(parent_level) # Filter categories by level_p queryset = queryset.filter(level_pp=level) # Convert queryset to list of dictionaries for JSON response categories = [] for category in queryset: categories.append({ 'key': category.id, 'title': category.name, 'parent': category.parent_id, 'source_type': category.source_type, 'category_type': category.category_type, 'level': category.level_p, # Add data property to store additional information 'data': { 'parent': category.parent_id, 'level': category.level_p } }) print(f'-categories-->{categories}') return JsonResponse(categories, safe=False) def save_model(self, request, obj, form, change): print(f'SAVE_MODEL CALLED: {request}/ {obj} / {form} / {change}') print(f'POST DATA: {request.POST}') # Get the level choice from the form data level_choice = request.POST.get('level_choice_hidden') print(f'LEVEL CHOICE: {level_choice}') # Get the parent from AJAX selection if provided ajax_parent = request.POST.get('ajax_parent') if ajax_parent and ajax_parent.isdigit(): # Set the parent for the object try: parent_category = self.model.objects.get(pk=int(ajax_parent)) obj.parent = parent_category # If parent is level 1, inherit its source_type # if parent_category.level_p == 1 and level_choice == '2': # obj.source_type = parent_category.source_type print(f'AJAX PARENT SET: {parent_category.id} - {parent_category.name}') except self.model.DoesNotExist: print(f'PARENT CATEGORY NOT FOUND: {ajax_parent}') # Debug form validation if form.is_valid(): print("FORM IS VALID") else: print(f"FORM ERRORS: {form.errors}") print(f'---> {obj}') # Let the parent class handle the save super().save_model(request, obj, form, change) # Add a message to trigger tree reload via JavaScript from django.contrib import messages messages.success(request, "Category saved successfully. Tree will be reloaded.") # Set a flag in the request to redirect back to the category index page request._category_saved = True def response_add(self, request, obj, post_url_continue=None): """ Override to redirect back to the category index page after adding a new category """ if hasattr(request, '_category_saved') and request._category_saved: from django.http import HttpResponseRedirect from django.urls import reverse return HttpResponseRedirect(reverse('admin:hadis_hadiscategory_changelist')) return super().response_add(request, obj, post_url_continue) def response_change(self, request, obj): """ Override to redirect back to the category index page after editing a category """ if hasattr(request, '_category_saved') and request._category_saved: from django.http import HttpResponseRedirect from django.urls import reverse return HttpResponseRedirect(reverse('admin:hadis_hadiscategory_changelist')) return super().response_change(request, obj) ======= from django import forms from django.utils.translation import gettext_lazy as _ from django.utils.html import format_html from unfold.admin import ModelAdmin from unfold.decorators import display, action from mptt.admin import DraggableMPTTAdmin from utils.json_editor_field import JsonEditorWidget import json from utils.admin import dovoodi_admin_site from ..models import HadisSect, HadisCategory # Custom Forms for JSON Fields class HadisSectAdminForm(forms.ModelForm): """Custom form for HadisSect with JSON editor widgets""" class Meta: model = HadisSect fields = '__all__' def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Schema for title JSON field title_schema = { "type": "array", "title": "Titles", "items": { "type": "object", "title": "Title", "properties": { "language_code": { "type": "string", "title": "Language Code", "enum": ["en", "fa", "ar", "ur", "ru"], "options": { "enum_titles": ["English", "Persian", "Arabic", "Urdu", "Russian"] } }, "text": { "type": "string", "title": "Title Text" } }, "required": ["language_code", "text"] } } # Schema for description JSON field description_schema = { "type": "array", "title": "Descriptions", "items": { "type": "object", "title": "Description", "properties": { "language_code": { "type": "string", "title": "Language Code", "enum": ["en", "fa", "ar", "ur", "ru"], "options": { "enum_titles": ["English", "Persian", "Arabic", "Urdu", "Russian"] } }, "text": { "type": "string", "title": "Description Text" } }, "required": ["language_code", "text"] } } # Apply JSON editor widgets self.fields['title'].widget = JsonEditorWidget(attrs={ 'schema': json.dumps(title_schema), 'title': 'Titles' }) self.fields['description'].widget = JsonEditorWidget(attrs={ 'schema': json.dumps(description_schema), 'title': 'Descriptions' }) class HadisCategoryAdminForm(forms.ModelForm): """Custom form for HadisCategory with JSON editor widgets""" class Meta: model = HadisCategory fields = '__all__' def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Schema for title JSON field title_schema = { "type": "array", "title": "Titles", "items": { "type": "object", "title": "Title", "properties": { "language_code": { "type": "string", "title": "Language Code", "enum": ["en", "fa", "ar", "ur", "ru"], "options": { "enum_titles": ["English", "Persian", "Arabic", "Urdu", "Russian"] } }, "text": { "type": "string", "title": "Title Text" } }, "required": ["language_code", "text"] } } # Schema for description JSON field description_schema = { "type": "array", "title": "Descriptions", "items": { "type": "object", "title": "Description", "properties": { "language_code": { "type": "string", "title": "Language Code", "enum": ["en", "fa", "ar", "ur", "ru"], "options": { "enum_titles": ["English", "Persian", "Arabic", "Urdu", "Russian"] } }, "text": { "type": "string", "title": "Description Text" } }, "required": ["language_code", "text"] } } # Apply JSON editor widgets self.fields['title'].widget = JsonEditorWidget(attrs={ 'schema': json.dumps(title_schema), 'title': 'Titles' }) self.fields['description'].widget = JsonEditorWidget(attrs={ 'schema': json.dumps(description_schema), 'title': 'Descriptions' }) class HadisSectAdmin(ModelAdmin): """Admin for HadisSect model""" form = HadisSectAdminForm list_display = ('sect_type', 'display_title', 'is_active', 'order') list_filter = ('sect_type', 'is_active') search_fields = ('title',) ordering = ('order',) fieldsets = ( (None, { 'fields': ('sect_type', 'title', 'is_active', 'order','description') }), ) def display_title(self, obj): """Extracts text from the title JSON list""" try: return obj.title[0]['text'] except (IndexError, KeyError, TypeError, AttributeError): return "No Title" display_title.short_description = _('Title') class HadisCategoryAdmin(DraggableMPTTAdmin, ModelAdmin): """Admin for HadisCategory model with MPTT tree support""" form = HadisCategoryAdminForm list_display = ('indented_title', 'sect', 'source_type', 'order') list_display_links = ('indented_title',) list_filter = ('sect', 'source_type') search_fields = ('title',) ordering = ('tree_id', 'lft') fieldsets = ( (None, { 'fields': ('parent', 'sect', 'source_type', 'title', 'order','description') }), (_('Files'), { 'fields': ('xmind_file',), 'classes': ('collapse',) }), ) def indented_title(self, instance): """Display indented title for tree structure using JSON text""" try: # Extract text from the first element of the title list title_text = instance.title[0]['text'] except (IndexError, KeyError, TypeError, AttributeError): title_text = "No Title" # DraggableMPTTAdmin works best if you don't mess with the HTML too much, # but here is your requested dash indentation style combined with clean text: return f"{'—' * instance.level} {title_text}" indented_title.short_description = _('Title') # Register models with the custom admin site dovoodi_admin_site.register(HadisSect, HadisSectAdmin) dovoodi_admin_site.register(HadisCategory, HadisCategoryAdmin) >>>>>>> develop