From e4bcb9bd3bf7cab34b2afe5846c247c476c4ab7f Mon Sep 17 00:00:00 2001 From: mohsentaba Date: Sun, 28 Jun 2026 14:01:49 +0330 Subject: [PATCH] relatives and details of narrators updated --- ...ion_type_transmitters_ikhtilat_and_more.py | 28 +++++++++++ .../migrations/0026_transmitterrelative.py | 31 +++++++++++++ apps/hadis/models/transmitter.py | 46 ++++++++++++++++++- apps/hadis/serializers/hadis.py | 37 ++++++++++++++- 4 files changed, 140 insertions(+), 2 deletions(-) create mode 100644 apps/hadis/migrations/0025_transmitters_companion_type_transmitters_ikhtilat_and_more.py create mode 100644 apps/hadis/migrations/0026_transmitterrelative.py diff --git a/apps/hadis/migrations/0025_transmitters_companion_type_transmitters_ikhtilat_and_more.py b/apps/hadis/migrations/0025_transmitters_companion_type_transmitters_ikhtilat_and_more.py new file mode 100644 index 0000000..e81eaba --- /dev/null +++ b/apps/hadis/migrations/0025_transmitters_companion_type_transmitters_ikhtilat_and_more.py @@ -0,0 +1,28 @@ +# Generated by Django 5.2.12 on 2026-06-28 13:52 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('hadis', '0024_hadiscorrection_links_hadisinterpretation_links'), + ] + + operations = [ + migrations.AddField( + model_name='transmitters', + name='companion_type', + field=models.CharField(blank=True, max_length=255, null=True, verbose_name='Companion Type'), + ), + migrations.AddField( + model_name='transmitters', + name='ikhtilat', + field=models.BooleanField(default=False, verbose_name='Ikhtilat'), + ), + migrations.AddField( + model_name='transmitters', + name='tadlis', + field=models.BooleanField(default=False, verbose_name='Tadlis'), + ), + ] diff --git a/apps/hadis/migrations/0026_transmitterrelative.py b/apps/hadis/migrations/0026_transmitterrelative.py new file mode 100644 index 0000000..f765c8c --- /dev/null +++ b/apps/hadis/migrations/0026_transmitterrelative.py @@ -0,0 +1,31 @@ +# Generated by Django 5.2.12 on 2026-06-28 13:58 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('hadis', '0025_transmitters_companion_type_transmitters_ikhtilat_and_more'), + ] + + operations = [ + migrations.CreateModel( + name='TransmitterRelative', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('relation_type', models.CharField(choices=[('father', 'Father'), ('son', 'Son')], max_length=50, verbose_name='Relation Type')), + ('name', models.CharField(max_length=255, verbose_name='Name')), + ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='created at')), + ('updated_at', models.DateTimeField(auto_now=True, verbose_name='updated at')), + ('narrator', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='relative_of', to='hadis.transmitters', verbose_name='Narrator')), + ('related_to', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='relatives', to='hadis.transmitters', verbose_name='Related To')), + ], + options={ + 'verbose_name': 'Transmitter Relative', + 'verbose_name_plural': 'Transmitter Relatives', + 'ordering': ['relation_type', 'name'], + }, + ), + ] diff --git a/apps/hadis/models/transmitter.py b/apps/hadis/models/transmitter.py index 9770846..669dbf4 100644 --- a/apps/hadis/models/transmitter.py +++ b/apps/hadis/models/transmitter.py @@ -258,6 +258,12 @@ class Transmitters(models.Model): death_year_hijri = models.CharField(max_length=50, verbose_name=_("Death Year (Hijri)"), null=True, blank=True) age_at_death = models.PositiveIntegerField(verbose_name=_('Age at Death'), blank=True, null=True) generation = models.PositiveIntegerField(verbose_name=_('Generation'), blank=True, null=True) + + # New fields + tadlis = models.BooleanField(default=False, verbose_name=_('Tadlis')) + ikhtilat = models.BooleanField(default=False, verbose_name=_('Ikhtilat')) + companion_type = models.CharField(max_length=255, null=True, blank=True, verbose_name=_('Companion Type')) + # Religious & Academic Information reliability = models.ForeignKey( TransmitterReliability, @@ -789,4 +795,42 @@ class OriginalTextReferenceImage(models.Model): """Stores the screenshots for a specific Original Text Book Reference""" reference = models.ForeignKey(OriginalTextReference, related_name='images', on_delete=models.CASCADE) image = models.ImageField(upload_to='hadis/original_text_images/') - priority = models.IntegerField(default=0) \ No newline at end of file + priority = models.IntegerField(default=0) + + +class TransmitterRelative(models.Model): + class RelationTypeChoices(models.TextChoices): + FATHER = 'father', _('Father') + SON = 'son', _('Son') + + related_to = models.ForeignKey( + Transmitters, + on_delete=models.CASCADE, + verbose_name=_('Related To'), + related_name='relatives' + ) + relation_type = models.CharField( + max_length=50, + choices=RelationTypeChoices.choices, + verbose_name=_('Relation Type') + ) + name = models.CharField(max_length=255, verbose_name=_('Name')) + narrator = models.ForeignKey( + Transmitters, + on_delete=models.SET_NULL, + null=True, + blank=True, + verbose_name=_('Narrator'), + related_name='relative_of' + ) + + 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 = _('Transmitter Relative') + verbose_name_plural = _('Transmitter Relatives') + ordering = ['relation_type', 'name'] + + def __str__(self): + return f"{self.name} ({self.get_relation_type_display()} of {self.related_to})" \ No newline at end of file diff --git a/apps/hadis/serializers/hadis.py b/apps/hadis/serializers/hadis.py index f9118b5..04160e6 100644 --- a/apps/hadis/serializers/hadis.py +++ b/apps/hadis/serializers/hadis.py @@ -446,6 +446,7 @@ class TransmitterDetailSerializer(serializers.ModelSerializer): died_in = LocalizedField() description = LocalizedField() reliability = serializers.SerializerMethodField() + relatives = serializers.SerializerMethodField() share_link = serializers.CharField(read_only=True) class Meta: @@ -456,7 +457,8 @@ class TransmitterDetailSerializer(serializers.ModelSerializer): 'lived_in','died_in','birth_year_hijri', 'death_year_hijri','age_at_death','reliability', 'madhhab',"in_sahih_muslim","in_sahih_bukhari", - "description",'generation','share_link' + "description",'generation','share_link', + 'tadlis', 'ikhtilat', 'companion_type', 'relatives' ] def get_reliability(self, obj): @@ -470,6 +472,39 @@ class TransmitterDetailSerializer(serializers.ModelSerializer): } return None + def get_relatives(self, obj): + relatives_qs = obj.relatives.all() + + grouped = {} + for rel in relatives_qs: + rel_type = rel.relation_type + slug = rel.narrator.slug if rel.narrator else None + + # Use narrator's localized name if exists and name is empty, otherwise use the direct name + name = rel.name + if rel.narrator and not name: + request = self.context.get('request') + name = get_localized_text(rel.narrator.full_name, request) + + item = { + "name": name, + "slug": slug + } + + if rel_type not in grouped: + grouped[rel_type] = [] + grouped[rel_type].append(item) + + result = [] + for rel_type, items in grouped.items(): + if rel_type == 'father' and items: + result.append({"father": items[0]}) + else: + result.append({rel_type: items}) + + return result + + class TransmittersFiltersSerializer(serializers.ModelSerializer): pass