Browse Source

feat(transmitters): add Madhhab model, relational field, and admin endpoints

- Introduce Madhhab model with multilingual title, description, and slug

- Link madhhabs relation (ManyToMany) to Transmitters

- Add migration 0062 to create table and populate from existing madhab JSON data

- Register DovodiMadhhabViewSet and admin routes in urls.py

- Update RELATIONAL_MIGRATION_ROADMAP.md for madhhab migration
master
Mohsen Taba 1 week ago
parent
commit
5ae61a2b10
  1. 49
      RELATIONAL_MIGRATION_ROADMAP.md
  2. 112
      apps/hadis/migrations/0062_madhhab_and_relations.py
  3. 47
      apps/hadis/models/transmitter.py
  4. 58
      apps/hadis/serializers/serializers_admin.py
  5. 5
      apps/hadis/urls.py
  6. 20
      apps/hadis/views_admin.py

49
RELATIONAL_MIGRATION_ROADMAP.md

@ -9,8 +9,8 @@
- [x] **فاز ۱: کنیه‌ها (`Kunya` / `kunyas`)** — رابطه چندبه‌چند (M2M) *(تکمیل شده)* - [x] **فاز ۱: کنیه‌ها (`Kunya` / `kunyas`)** — رابطه چندبه‌چند (M2M) *(تکمیل شده)*
- [x] **فاز ۲: ویژگی‌ها (`TransmitterFeature` / `features`)** — رابطه چندبه‌چند (M2M) *(تکمیل شده)* - [x] **فاز ۲: ویژگی‌ها (`TransmitterFeature` / `features`)** — رابطه چندبه‌چند (M2M) *(تکمیل شده)*
- [x] **فاز ۳: وضعیت صحابی بودن (`SahabiStatus` / `sahabi_status`)** — رابطه تک‌به‌چند (ForeignKey / M2O) *(تکمیل شده)* - [x] **فاز ۳: وضعیت صحابی بودن (`SahabiStatus` / `sahabi_status`)** — رابطه تک‌به‌چند (ForeignKey / M2O) *(تکمیل شده)*
- [ ] **فاز ۴: تفکیک و اتصال رتبه‌بندی و وثاقت راوی و مؤلف (`NarratorRating` و `reliability_levels`)** — رابطه ForeignKey / M2O
- [ ] **فاز ۵: مذاهب راویان (`Madhhab` / `madhabs`)** — رابطه چندبه‌چند (M2M)
- [—] **فاز ۴: تفکیک و اتصال رتبه‌بندی و وثاقت راوی و مؤلف (`NarratorRating` و `reliability_levels`)** — *(صرف‌نظر شد / اسکیپ طبق تصمیم: سیستم فعلی نظرات رجالی پاسخگو است)*
- [x] **فاز ۵: مذاهب راویان (`Madhhab` / `madhabs`)** — رابطه چندبه‌چند (M2M) *(تکمیل شده)*
- [ ] **فاز ۶: داینامیک‌سازی انواع نسبت‌های خویشاوندی (`RelationType` / `relatives`)** — رابطه ForeignKey در `TransmitterRelative` - [ ] **فاز ۶: داینامیک‌سازی انواع نسبت‌های خویشاوندی (`RelationType` / `relatives`)** — رابطه ForeignKey در `TransmitterRelative`
- [ ] **فاز ۷: مکان‌های چندگانه راوی (`narrators_locations`)** — اتصال چندبه‌چند با ترتیب (`sort_order`) و نوع (`residence` / `death`) - [ ] **فاز ۷: مکان‌های چندگانه راوی (`narrators_locations`)** — اتصال چندبه‌چند با ترتیب (`sort_order`) و نوع (`residence` / `death`)
@ -104,33 +104,34 @@
--- ---
### فاز ۴: تفکیک و اتصال رتبه‌بندی و وثاقت راوی و مؤلف (`NarratorRating` و `reliability_levels`) 🔲
* **وضعیت:** `در انتظار اجرا`
* **نوع رابطه طبق داکیومنت کارفرما:** **ForeignKey (M2O) برای هر دو فیلد**
> در معماری کارفرما دو مفهوم متمایز وجود دارد:
> ۱. `reliability_levels` (۱۱ سطح کلی اعتبار: ثقه، صدوق، ضعیف، کذاب و...)
> ۲. `narrator_ratings` (عبارت دقیق ارزیابی رجالی: ثقة ثبت، صدوق له أوهام و...)
* **مراحل پیاده‌سازی بک‌اند:**
1. بررسی و یکپارچه‌سازی مدل `TransmitterReliability` فعلی با ۱۱ سطح استاندارد کارفرما.
2. استفاده از مدل `BookAuthorRutba` یا ایجاد مدل عمومی `NarratorRating` (با ترجمه چندزبانه) و اتصال آن به عنوان `rating` هم به راوی (`Transmitters.narrator_rating`) و هم به مؤلف (`BookAuthor.rutba_status`).
3. تبدیل فیلد `BookAuthor.scholarity` به کلید خارجی به `TransmitterReliability`.
4. سریالایزرها و مایگریشن داده‌ها.
* **مراحل پیاده‌سازی داشبورد:**
1. نمایش هر دو دراپ‌داون «سطح وثاقت کلی» و «عبارت ارزیابی نهایی» در فرم راوی و مؤلف.
### فاز ۴: تفکیک و اتصال رتبه‌بندی و وثاقت راوی و مؤلف (`NarratorRating` و `reliability_levels`) ⏭️
* **وضعیت:** `صرف‌نظر شد (Skipped)`
* **علت و تصمیم اتخاذشده:**
- مفهوم ارزیابی‌های تفصیلی رجالی (`narrator_ratings`) در حال حاضر به‌صورت کامل، غنی و پیشرفته در سیستم نظرات رجالی (`TransmitterOpinion` و `ScholarlyOpinion`) پیاده‌سازی شده و نیازی به تغییر یا مدل تکراری ندارد.
- برای فیلدهای وثاقت و رتبه راوی و مؤلف (`TransmitterReliability` و `BookAuthorRutba`) دست ادمین باز گذاشته شده تا بر اساس نیاز خود رتبه‌ها را مدیریت و انتخاب کنند و نیازی به ادغام جبری آنها نیست.
- بنابراین این فاز طبق تصمیم اسکیپ شده و منطق فعلی بدون تغییر حفظ گردید.
--- ---
### فاز ۵: مذاهب راویان (`Madhhab` / `madhabs`) 🔲
* **وضعیت:** `در انتظار اجرا`
### فاز ۵: مذاهب راویان (`Madhhab` / `madhabs`) ✅
* **وضعیت:** `تکمیل شده`
* **نوع رابطه طبق داکیومنت کارفرما:** **Many-to-Many (M2M)** * **نوع رابطه طبق داکیومنت کارفرما:** **Many-to-Many (M2M)**
> کارفرما مذهب را به عنوان یک کالکشن چندزبانه با رابطه M2M در نظر گرفته است (تا بتوان مذاهب گوناگون، انشعابات، یا نظرات متعدد درباره مذهب راوی را ثبت کرد). > کارفرما مذهب را به عنوان یک کالکشن چندزبانه با رابطه M2M در نظر گرفته است (تا بتوان مذاهب گوناگون، انشعابات، یا نظرات متعدد درباره مذهب راوی را ثبت کرد).
* **مراحل پیاده‌سازی بک‌اند:**
1. ساخت مدل `Madhhab` با فیلد چندزبانه `name`/`title` و `slug`.
2. ایجاد رابطه M2M روی `Transmitters.madhhabs = models.ManyToManyField('Madhhab', blank=True, related_name='transmitters')`.
3. حفظ فیلد `madhhab` قبلی (`shia`/`sunni`) و سینک مقدار پیش‌فرض با آن.
4. ساخت Seed داده‌های مذاهب و ثبت API ادمین.
* **مراحل پیاده‌سازی داشبورد:**
1. تبدیل سلکتور دو گزینه‌ای به دراپ‌داون داینامیک چندزبانه.
* **کارهای انجام‌شده:**
1. **مدل `Madhhab`:** ایجاد در `transmitter.py` با فیلدهای `title` (چندزبانه به ۴ زبان: fa, ar, en, ru)، `slug` (یکتا و خودکار)، متد هلپر `get_title()` و تایم‌استمپ‌ها.
2. **اتصال رابطه چندبه‌چند به `Transmitters`:** افزودن فیلد `madhhabs = models.ManyToManyField('hadis.Madhhab', blank=True, related_name='transmitters')` به همراه حفظ کامل فیلد متنی قدیمی `madhhab` جهت سازگاری به عقب (backward compatibility).
3. **مایگریشن و Seeding دیتابیس (۰۶۲):**
- ساخت جدول و Seed کردن ۵ مذهب استاندارد داکیومنت کارفرما (شیعه، اهل سنت، زیدی، واقفی، فتحی).
- انتقال خودکار دیتای تمام ۷۹ راوی موجود در دیتابیس به رکوردهای متناظر M2M (۳۴ راوی شیعه و ۴۵ راوی سنی).
4. **سریالایزرها و APIها:**
- ساخت `AdminMadhhabSerializer` و `DovodiMadhhabViewSet`.
- ثبت روت‌های `/api/hadis/admin/dovodi-madhhabs/` و `/api/hadis/admin/madhhabs/`.
- پشتیبانی کامل در `AdminTransmitterListSerializer` و `AdminTransmitterDetailSerializer` با ارسال فیلدهای `madhhabs` (لیست شناسه‌ها) و `madhhabs_detail` و همگام‌سازی دوطرفه خودکار با فیلد استرینگ قدیمی `madhhab` در توابع `create` و `update`.
5. **داشبورد (فرانت‌اند):**
- ساخت کامپوننت ماژولار چندانتخابی `MadhhabSelect.tsx` (با کش ماژولار در سطح حافظه، قابلیت افزودن سریع و ریموو بج‌های مذهب، سرچ و فیلتر Popover و مودال ایجاد مذهب جدید با هدر چندزبانه، نمایش کامل توضیحات بدون برش و فوتر چسبان).
- جایگزینی سلکتور استاتیک قبلی با `MadhhabSelect` در سایدبار فرم راوی (`TransmitterSidebar.tsx` و `TransmitterFormPage.tsx`) با همگام‌سازی داینامیک.
- افزودن تب مدیریت «مذاهب»، ستون‌های جدول و دیالوگ مدیریت کامل (`MadhhabDialog`) در صفحه آرشیو راویان (`TransmittersArchivePage.tsx`).
- بیلد نهایی پروژه فرانت‌اند با موفقیت انجام شد (بدون هیچ خطای TypeScript یا کامپایل).
--- ---

112
apps/hadis/migrations/0062_madhhab_and_relations.py

@ -0,0 +1,112 @@
# Generated by Django 4.2.30 on 2026-10-20 15:23
from django.db import migrations, models
MADHHAB_DATA = [
{
"slug": "shia",
"title": [
{"language_code": "ar", "text": "شيعي", "title": "شيعي"},
{"language_code": "fa", "text": "شیعه", "title": "شیعه"},
{"language_code": "en", "text": "Shia", "title": "Shia"},
{"language_code": "ru", "text": "Шиит", "title": "Шиит"},
],
},
{
"slug": "sunni",
"title": [
{"language_code": "ar", "text": "سني", "title": "سني"},
{"language_code": "fa", "text": "سنی", "title": "سنی"},
{"language_code": "en", "text": "Sunni", "title": "Sunni"},
{"language_code": "ru", "text": "Суннит", "title": "Суннит"},
],
},
{
"slug": "zaydi",
"title": [
{"language_code": "ar", "text": "زيدي", "title": "زيدي"},
{"language_code": "fa", "text": "زیدی", "title": "زیدی"},
{"language_code": "en", "text": "Zaydi", "title": "Zaydi"},
{"language_code": "ru", "text": "Зейдит", "title": "Зейдит"},
],
},
{
"slug": "waqifi",
"title": [
{"language_code": "ar", "text": "واقفي", "title": "واقفي"},
{"language_code": "fa", "text": "واقفی", "title": "واقفی"},
{"language_code": "en", "text": "Waqifi", "title": "Waqifi"},
{"language_code": "ru", "text": "Вакифит", "title": "Вакифит"},
],
},
{
"slug": "fathi",
"title": [
{"language_code": "ar", "text": "فطحي", "title": "فطحي"},
{"language_code": "fa", "text": "فطحی", "title": "فطحی"},
{"language_code": "en", "text": "Fathi", "title": "Fathi"},
{"language_code": "ru", "text": "Фатхит", "title": "Фатхиت"},
],
},
]
def seed_and_migrate_madhhabs(apps, schema_editor):
Madhhab = apps.get_model('hadis', 'Madhhab')
Transmitters = apps.get_model('hadis', 'Transmitters')
madhhab_map = {}
for item in MADHHAB_DATA:
obj, _ = Madhhab.objects.get_or_create(
slug=item["slug"],
defaults={"title": item["title"]},
)
madhhab_map[item["slug"]] = obj
shia_obj = madhhab_map.get("shia")
sunni_obj = madhhab_map.get("sunni")
if shia_obj:
for t in Transmitters.objects.filter(madhhab='shia'):
t.madhhabs.add(shia_obj)
if sunni_obj:
for t in Transmitters.objects.filter(madhhab='sunni'):
t.madhhabs.add(sunni_obj)
def reverse_seed(apps, schema_editor):
pass
class Migration(migrations.Migration):
dependencies = [
('hadis', '0061_sahabi_status_and_relation'),
]
operations = [
migrations.CreateModel(
name='Madhhab',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.JSONField(default=list, verbose_name='Title')),
('slug', models.SlugField(allow_unicode=True, blank=True, max_length=255, unique=True, verbose_name='slug')),
('description', models.JSONField(blank=True, default=list, verbose_name='Description')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')),
('updated_at', models.DateTimeField(auto_now=True, verbose_name='Updated At')),
],
options={
'verbose_name': 'Madhhab',
'verbose_name_plural': 'Madhhabs',
'ordering': ['slug', 'id'],
},
),
migrations.AddField(
model_name='transmitters',
name='madhhabs',
field=models.ManyToManyField(blank=True, related_name='transmitters', to='hadis.madhhab', verbose_name='Madhhabs (M2M)'),
),
migrations.RunPython(seed_and_migrate_madhhabs, reverse_seed),
]

47
apps/hadis/models/transmitter.py

@ -289,6 +289,47 @@ class SahabiStatus(LowercaseSlugMixin, models.Model):
return None return None
class Madhhab(LowercaseSlugMixin, models.Model):
slug_source_field = 'title'
title = models.JSONField(default=list, verbose_name=_('Title'))
slug = models.SlugField(max_length=255, verbose_name=_('slug'), blank=True, unique=True, allow_unicode=True)
description = models.JSONField(default=list, blank=True, verbose_name=_('Description'))
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_('Created At'))
updated_at = models.DateTimeField(auto_now=True, verbose_name=_('Updated At'))
class Meta:
verbose_name = _('Madhhab')
verbose_name_plural = _('Madhhabs')
ordering = ['slug', 'id']
def __str__(self):
if not self.title or not isinstance(self.title, list):
return str(self.id)
for tr in self.title:
if isinstance(tr, dict) and tr.get('language_code') == 'en':
return tr.get('text', '') or tr.get('title', '')
first = self.title[0]
if isinstance(first, dict):
return first.get('text', '') or first.get('title', '')
return str(self.id)
def get_title(self, lang: str = 'en') -> Optional[str]:
if not self.title or not isinstance(self.title, list):
return None
for tr in self.title:
if isinstance(tr, dict) and tr.get('language_code') == lang:
return tr.get('text', '') or tr.get('title', '')
for tr in self.title:
if isinstance(tr, dict) and tr.get('language_code') == 'en':
return tr.get('text', '') or tr.get('title', '')
if isinstance(self.title[0], dict):
return self.title[0].get('text', '') or self.title[0].get('title', '')
return None
class Transmitters(LowercaseSlugMixin, models.Model): class Transmitters(LowercaseSlugMixin, models.Model):
slug_source_field = 'full_name' slug_source_field = 'full_name'
# class ReliabilityLevel(models.TextChoices): # class ReliabilityLevel(models.TextChoices):
@ -406,6 +447,12 @@ class Transmitters(LowercaseSlugMixin, models.Model):
default=MadhhabChoices.SHIA, default=MadhhabChoices.SHIA,
verbose_name=_('Madhhab/School of Thought') verbose_name=_('Madhhab/School of Thought')
) )
madhhabs = models.ManyToManyField(
'hadis.Madhhab',
blank=True,
related_name='transmitters',
verbose_name=_('Madhhabs (M2M)')
)
# Presence in Famous Collections # Presence in Famous Collections
in_sahih_muslim = models.BooleanField( in_sahih_muslim = models.BooleanField(

58
apps/hadis/serializers/serializers_admin.py

@ -10,7 +10,7 @@ from ..models import (
Hadis, HadisCategory, HadisStatus, HadisTag, HadisCorrection, HadisInterpretation, HadisCollection, HadisSect, HadisInCollection, HadisTransmitter, HadisReference, ReferenceImage, Hadis, HadisCategory, HadisStatus, HadisTag, HadisCorrection, HadisInterpretation, HadisCollection, HadisSect, HadisInCollection, HadisTransmitter, HadisReference, ReferenceImage,
BookReference, BookReferenceImage, BookReferenceDocument, BookAuthor, BookAuthorRutba, BookAttribute, BookSubjectArea, BookType, BookTag, BookEdition, BookVolume, BookResearcher, BookEditor, BookReference, BookReferenceImage, BookReferenceDocument, BookAuthor, BookAuthorRutba, BookAttribute, BookSubjectArea, BookType, BookTag, BookEdition, BookVolume, BookResearcher, BookEditor,
Transmitters, TransmitterReliability, NarratorLayer, OpinionStatus, ScholarlyOpinion, TransmitterOpinion, TransmitterOriginalText, Transmitters, TransmitterReliability, NarratorLayer, OpinionStatus, ScholarlyOpinion, TransmitterOpinion, TransmitterOriginalText,
City, Kunya, TransmitterFeature, SahabiStatus, YearValue, NarratorDate,
City, Kunya, TransmitterFeature, SahabiStatus, Madhhab, YearValue, NarratorDate,
CorrectionReference, CorrectionReferenceImage, InterpretationReference, InterpretationReferenceImage, CorrectionReference, CorrectionReferenceImage, InterpretationReference, InterpretationReferenceImage,
OriginalTextReference, OriginalTextReferenceImage, OriginalTextReference, OriginalTextReferenceImage,
ContentRelease ContentRelease
@ -2058,6 +2058,18 @@ class AdminSahabiStatusSerializer(serializers.ModelSerializer):
read_only_fields = ["id", "slug", "created_at", "updated_at"] read_only_fields = ["id", "slug", "created_at", "updated_at"]
class AdminMadhhabSerializer(serializers.ModelSerializer):
title = serializers.JSONField()
description = serializers.JSONField(required=False, default=list)
class Meta:
model = Madhhab
fields = ["id", "title", "slug", "description", "created_at", "updated_at"]
read_only_fields = ["id", "slug", "created_at", "updated_at"]
def _build_admin_date_preview(obj, date_type: str, raw_val: str): def _build_admin_date_preview(obj, date_type: str, raw_val: str):
if not raw_val or not str(raw_val).strip(): if not raw_val or not str(raw_val).strip():
return None return None
@ -2073,6 +2085,7 @@ class AdminTransmitterListSerializer(serializers.ModelSerializer):
kunyas_detail = AdminKunyaSerializer(source="kunyas", many=True, read_only=True) kunyas_detail = AdminKunyaSerializer(source="kunyas", many=True, read_only=True)
features_detail = AdminTransmitterFeatureSerializer(source="features_list", many=True, read_only=True) features_detail = AdminTransmitterFeatureSerializer(source="features_list", many=True, read_only=True)
sahabi_status_detail = AdminSahabiStatusSerializer(source="sahabi_status", read_only=True) sahabi_status_detail = AdminSahabiStatusSerializer(source="sahabi_status", read_only=True)
madhhabs_detail = AdminMadhhabSerializer(source="madhhabs", many=True, read_only=True)
full_name = serializers.JSONField() full_name = serializers.JSONField()
known_as = serializers.JSONField() known_as = serializers.JSONField()
nickname = serializers.JSONField() nickname = serializers.JSONField()
@ -2104,6 +2117,7 @@ class AdminTransmitterListSerializer(serializers.ModelSerializer):
"reliability", "reliability",
"reliability_detail", "reliability_detail",
"madhhab", "madhhab",
"madhhabs_detail",
"generation", "generation",
"thumbnail", "thumbnail",
"share_link", "share_link",
@ -2145,6 +2159,12 @@ class AdminTransmitterDetailSerializer(serializers.ModelSerializer):
allow_null=True, allow_null=True,
) )
sahabi_status_detail = AdminSahabiStatusSerializer(source="sahabi_status", read_only=True) sahabi_status_detail = AdminSahabiStatusSerializer(source="sahabi_status", read_only=True)
madhhabs = serializers.PrimaryKeyRelatedField(
queryset=Madhhab.objects.all(),
many=True,
required=False,
)
madhhabs_detail = AdminMadhhabSerializer(source="madhhabs", many=True, read_only=True)
companion_type = serializers.JSONField(required=False, default=list) companion_type = serializers.JSONField(required=False, default=list)
known_as = serializers.JSONField() known_as = serializers.JSONField()
nickname = serializers.JSONField() nickname = serializers.JSONField()
@ -2203,7 +2223,7 @@ class AdminTransmitterDetailSerializer(serializers.ModelSerializer):
"kunyas_detail", "kunyas_detail",
"features", "features",
"features_list", "features_list",
"features_detail", "sahabi_status_detail",
"features_detail", "sahabi_status_detail", "madhhabs_detail",
"sahabi_status", "sahabi_status",
"sahabi_status_detail", "sahabi_status_detail",
"companion_type", "companion_type",
@ -2224,6 +2244,8 @@ class AdminTransmitterDetailSerializer(serializers.ModelSerializer):
"reliability", "reliability",
"reliability_detail", "reliability_detail",
"madhhab", "madhhab",
"madhhabs",
"madhhabs_detail",
"in_sahih_muslim", "in_sahih_muslim",
"in_sahih_bukhari", "in_sahih_bukhari",
"origin", "origin",
@ -2358,6 +2380,14 @@ class AdminTransmitterDetailSerializer(serializers.ModelSerializer):
except ValueError: except ValueError:
pass pass
if "madhhabs" in data:
val = data["madhhabs"]
if isinstance(val, str):
try:
data["madhhabs"] = json.loads(val)
except ValueError:
pass
return super().to_internal_value(data) return super().to_internal_value(data)
def validate(self, attrs): def validate(self, attrs):
@ -2386,6 +2416,7 @@ class AdminTransmitterDetailSerializer(serializers.ModelSerializer):
sons_ids = validated_data.pop("sons_ids", None) sons_ids = validated_data.pop("sons_ids", None)
kunyas_objs = validated_data.pop("kunyas", None) kunyas_objs = validated_data.pop("kunyas", None)
features_objs = validated_data.pop("features_list", None) features_objs = validated_data.pop("features_list", None)
madhhabs_objs = validated_data.pop("madhhabs", None)
instance = super().create(validated_data) instance = super().create(validated_data)
if sons_ids is not None: if sons_ids is not None:
Transmitters.objects.filter(id__in=sons_ids).update(father=instance) Transmitters.objects.filter(id__in=sons_ids).update(father=instance)
@ -2411,6 +2442,17 @@ class AdminTransmitterDetailSerializer(serializers.ModelSerializer):
st = validated_data.get("sahabi_status") st = validated_data.get("sahabi_status")
instance.companion_type = st.title if st else [] instance.companion_type = st.title if st else []
instance.save(update_fields=['companion_type']) instance.save(update_fields=['companion_type'])
if madhhabs_objs is not None:
instance.madhhabs.set(madhhabs_objs)
for m in madhhabs_objs:
if m.slug == "sunni":
instance.madhhab = "sunni"
instance.save(update_fields=["madhhab"])
break
elif m.slug == "shia":
instance.madhhab = "shia"
instance.save(update_fields=["madhhab"])
break
return instance return instance
def update(self, instance, validated_data): def update(self, instance, validated_data):
@ -2418,6 +2460,7 @@ class AdminTransmitterDetailSerializer(serializers.ModelSerializer):
sons_ids = validated_data.pop("sons_ids", None) sons_ids = validated_data.pop("sons_ids", None)
kunyas_objs = validated_data.pop("kunyas", None) kunyas_objs = validated_data.pop("kunyas", None)
features_objs = validated_data.pop("features_list", None) features_objs = validated_data.pop("features_list", None)
madhhabs_objs = validated_data.pop("madhhabs", None)
if remove_thumbnail and instance.thumbnail: if remove_thumbnail and instance.thumbnail:
instance.thumbnail.delete(save=False) instance.thumbnail.delete(save=False)
instance.thumbnail = None instance.thumbnail = None
@ -2447,6 +2490,17 @@ class AdminTransmitterDetailSerializer(serializers.ModelSerializer):
st = validated_data.get("sahabi_status") st = validated_data.get("sahabi_status")
instance.companion_type = st.title if st else [] instance.companion_type = st.title if st else []
instance.save(update_fields=['companion_type']) instance.save(update_fields=['companion_type'])
if madhhabs_objs is not None:
instance.madhhabs.set(madhhabs_objs)
for m in madhhabs_objs:
if m.slug == "sunni":
instance.madhhab = "sunni"
instance.save(update_fields=["madhhab"])
break
elif m.slug == "shia":
instance.madhhab = "shia"
instance.save(update_fields=["madhhab"])
break
return instance return instance

5
apps/hadis/urls.py

@ -47,7 +47,8 @@ from .views_admin import (
DovodiCityViewSet, DovodiCityViewSet,
DovodiKunyaViewSet, DovodiKunyaViewSet,
DovodiTransmitterFeatureViewSet, DovodiTransmitterFeatureViewSet,
DovodiSahabiStatusViewSet
DovodiSahabiStatusViewSet,
DovodiMadhhabViewSet
) )
@ -152,6 +153,8 @@ admin_router.register(r'dovodi-transmitter-features', DovodiTransmitterFeatureVi
admin_router.register(r'transmitter-features', DovodiTransmitterFeatureViewSet, basename='admin-transmitter-features') admin_router.register(r'transmitter-features', DovodiTransmitterFeatureViewSet, basename='admin-transmitter-features')
admin_router.register(r'dovodi-sahabi-statuses', DovodiSahabiStatusViewSet, basename='dovodi-sahabi-statuses') admin_router.register(r'dovodi-sahabi-statuses', DovodiSahabiStatusViewSet, basename='dovodi-sahabi-statuses')
admin_router.register(r'sahabi-statuses', DovodiSahabiStatusViewSet, basename='admin-sahabi-statuses') admin_router.register(r'sahabi-statuses', DovodiSahabiStatusViewSet, basename='admin-sahabi-statuses')
admin_router.register(r'dovodi-madhhabs', DovodiMadhhabViewSet, basename='dovodi-madhhabs')
admin_router.register(r'madhhabs', DovodiMadhhabViewSet, basename='admin-madhhabs')

20
apps/hadis/views_admin.py

@ -41,6 +41,7 @@ from .models import (
Kunya, Kunya,
TransmitterFeature, TransmitterFeature,
SahabiStatus, SahabiStatus,
Madhhab,
) )
@ -131,6 +132,7 @@ from .serializers.serializers_admin import (
AdminKunyaSerializer, AdminKunyaSerializer,
AdminTransmitterFeatureSerializer, AdminTransmitterFeatureSerializer,
AdminSahabiStatusSerializer, AdminSahabiStatusSerializer,
AdminMadhhabSerializer,
) )
@ -2957,3 +2959,21 @@ class DovodiSahabiStatusViewSet(ModelViewSet):
| Q(slug__icontains=search_query) | Q(slug__icontains=search_query)
) )
return queryset return queryset
class DovodiMadhhabViewSet(ModelViewSet):
serializer_class = AdminMadhhabSerializer
permission_classes = [IsAuthenticated, IsSuperAdmin]
authentication_classes = [TokenAuthentication]
pagination_class = OptionalPagination
parser_classes = (JSONParser, MultiPartParser, FormParser)
def get_queryset(self):
queryset = Madhhab.objects.all().order_by("slug", "id")
search_query = self.request.query_params.get("search")
if search_query:
queryset = queryset.filter(
Q(title__icontains=search_query)
| Q(slug__icontains=search_query)
)
return queryset
Loading…
Cancel
Save