Browse Source
feat(transmitters): introduce City and Kunya models, geographical relations, and admin viewsets
feat(transmitters): introduce City and Kunya models, geographical relations, and admin viewsets
- Add City model and connect origin_city, lived_in_city, and died_in_city to Transmitters and BookAuthors - Connect city_of_publication_city to BookEdition - Add Kunya model and connect many-to-many kunyas relation to Transmitters - Implement DovodiCityViewSet and DovodiKunyaViewSet and register routes in admin router - Add migrations 0057, 0058, and 0059 to create tables and migrate existing string/JSON datamaster
8 changed files with 2336 additions and 11 deletions
-
213apps/hadis/migrations/0057_city_and_location_relations.py
-
48apps/hadis/migrations/0058_rename_hadis_booka_date_ty_9a1e3c_idx_hadis_booka_date_ty_99a484_idx_and_more.py
-
102apps/hadis/migrations/0059_kunya_and_relations.py
-
48apps/hadis/models/reference.py
-
116apps/hadis/models/transmitter.py
-
157apps/hadis/serializers/serializers_admin.py
-
167apps/hadis/urls.py
-
1490apps/hadis/views_admin.py
@ -0,0 +1,213 @@ |
|||||
|
# Generated by Django 4.2.30 on 2026-10-20 10:35 |
||||
|
|
||||
|
from django.db import migrations, models |
||||
|
import django.db.models.deletion |
||||
|
from django.utils.text import slugify |
||||
|
import utils.mixins |
||||
|
|
||||
|
|
||||
|
def populate_cities_from_existing_records(apps, schema_editor): |
||||
|
City = apps.get_model('hadis', 'City') |
||||
|
Transmitters = apps.get_model('hadis', 'Transmitters') |
||||
|
BookEdition = apps.get_model('hadis', 'BookEdition') |
||||
|
|
||||
|
# Cache cities by normalized text key -> City instance |
||||
|
city_by_text = {} |
||||
|
for c in City.objects.all(): |
||||
|
if isinstance(c.title, list): |
||||
|
for entry in c.title: |
||||
|
if isinstance(entry, dict): |
||||
|
txt = (entry.get('text') or entry.get('title') or '').strip().lower() |
||||
|
if txt: |
||||
|
city_by_text[txt] = c |
||||
|
|
||||
|
def get_or_create_city(json_list): |
||||
|
if not json_list or not isinstance(json_list, list): |
||||
|
return None |
||||
|
cleaned = [] |
||||
|
lookup_keys = [] |
||||
|
for entry in json_list: |
||||
|
if isinstance(entry, dict): |
||||
|
lang = (entry.get('language_code') or '').strip() |
||||
|
txt = (entry.get('text') or entry.get('title') or '').strip() |
||||
|
if lang and txt: |
||||
|
cleaned.append({'language_code': lang, 'text': txt, 'title': txt}) |
||||
|
lookup_keys.append(txt.lower()) |
||||
|
if not cleaned: |
||||
|
return None |
||||
|
|
||||
|
for key in lookup_keys: |
||||
|
if key in city_by_text: |
||||
|
existing = city_by_text[key] |
||||
|
# Merge any missing language translations into existing city |
||||
|
existing_langs = { |
||||
|
item.get('language_code'): item |
||||
|
for item in (existing.title or []) |
||||
|
if isinstance(item, dict) and item.get('language_code') |
||||
|
} |
||||
|
updated = False |
||||
|
for item in cleaned: |
||||
|
if item['language_code'] not in existing_langs: |
||||
|
existing_langs[item['language_code']] = item |
||||
|
updated = True |
||||
|
if updated: |
||||
|
existing.title = list(existing_langs.values()) |
||||
|
existing.save(update_fields=['title']) |
||||
|
for k in lookup_keys: |
||||
|
city_by_text[k] = existing |
||||
|
return existing |
||||
|
|
||||
|
first_text = cleaned[0]['text'] |
||||
|
slug_val = slugify(first_text, allow_unicode=True)[:240] or 'city' |
||||
|
city_obj = City.objects.create(title=cleaned, slug=slug_val) |
||||
|
for k in lookup_keys: |
||||
|
city_by_text[k] = city_obj |
||||
|
return city_obj |
||||
|
|
||||
|
for tr in Transmitters.objects.all().iterator(): |
||||
|
changed_fields = [] |
||||
|
for json_attr, fk_attr in ( |
||||
|
('origin', 'origin_city'), |
||||
|
('lived_in', 'lived_in_city'), |
||||
|
('died_in', 'died_in_city'), |
||||
|
): |
||||
|
if getattr(tr, f'{fk_attr}_id', None) is None: |
||||
|
city_obj = get_or_create_city(getattr(tr, json_attr, None)) |
||||
|
if city_obj: |
||||
|
setattr(tr, fk_attr, city_obj) |
||||
|
changed_fields.append(fk_attr) |
||||
|
if changed_fields: |
||||
|
tr.save(update_fields=changed_fields) |
||||
|
|
||||
|
for ed in BookEdition.objects.all().iterator(): |
||||
|
if ed.city_of_publication_city_id is None: |
||||
|
city_obj = get_or_create_city(ed.city_of_publication) |
||||
|
if city_obj: |
||||
|
ed.city_of_publication_city = city_obj |
||||
|
ed.save(update_fields=['city_of_publication_city']) |
||||
|
|
||||
|
|
||||
|
class Migration(migrations.Migration): |
||||
|
|
||||
|
dependencies = [ |
||||
|
('hadis', '0056_bookauthor_date_fields_and_bookauthordate'), |
||||
|
] |
||||
|
|
||||
|
operations = [ |
||||
|
migrations.CreateModel( |
||||
|
name='City', |
||||
|
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, null=True, verbose_name='slug')), |
||||
|
('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': 'City', |
||||
|
'verbose_name_plural': 'Cities', |
||||
|
'ordering': ('-created_at', '-id'), |
||||
|
}, |
||||
|
bases=(utils.mixins.LowercaseSlugMixin, models.Model), |
||||
|
), |
||||
|
migrations.AddField( |
||||
|
model_name='transmitters', |
||||
|
name='origin_city', |
||||
|
field=models.ForeignKey( |
||||
|
blank=True, |
||||
|
null=True, |
||||
|
on_delete=django.db.models.deletion.SET_NULL, |
||||
|
related_name='origin_transmitters', |
||||
|
to='hadis.city', |
||||
|
verbose_name='Origin City', |
||||
|
), |
||||
|
), |
||||
|
migrations.AddField( |
||||
|
model_name='transmitters', |
||||
|
name='lived_in_city', |
||||
|
field=models.ForeignKey( |
||||
|
blank=True, |
||||
|
null=True, |
||||
|
on_delete=django.db.models.deletion.SET_NULL, |
||||
|
related_name='lived_in_transmitters', |
||||
|
to='hadis.city', |
||||
|
verbose_name='Lived in City', |
||||
|
), |
||||
|
), |
||||
|
migrations.AddField( |
||||
|
model_name='transmitters', |
||||
|
name='died_in_city', |
||||
|
field=models.ForeignKey( |
||||
|
blank=True, |
||||
|
null=True, |
||||
|
on_delete=django.db.models.deletion.SET_NULL, |
||||
|
related_name='died_in_transmitters', |
||||
|
to='hadis.city', |
||||
|
verbose_name='Died in City', |
||||
|
), |
||||
|
), |
||||
|
migrations.AddField( |
||||
|
model_name='bookauthor', |
||||
|
name='origin', |
||||
|
field=models.JSONField(blank=True, default=list, verbose_name='Origin'), |
||||
|
), |
||||
|
migrations.AddField( |
||||
|
model_name='bookauthor', |
||||
|
name='lived_in', |
||||
|
field=models.JSONField(blank=True, default=list, verbose_name='Lived in'), |
||||
|
), |
||||
|
migrations.AddField( |
||||
|
model_name='bookauthor', |
||||
|
name='died_in', |
||||
|
field=models.JSONField(blank=True, default=list, verbose_name='Died in'), |
||||
|
), |
||||
|
migrations.AddField( |
||||
|
model_name='bookauthor', |
||||
|
name='origin_city', |
||||
|
field=models.ForeignKey( |
||||
|
blank=True, |
||||
|
null=True, |
||||
|
on_delete=django.db.models.deletion.SET_NULL, |
||||
|
related_name='origin_authors', |
||||
|
to='hadis.city', |
||||
|
verbose_name='Origin City', |
||||
|
), |
||||
|
), |
||||
|
migrations.AddField( |
||||
|
model_name='bookauthor', |
||||
|
name='lived_in_city', |
||||
|
field=models.ForeignKey( |
||||
|
blank=True, |
||||
|
null=True, |
||||
|
on_delete=django.db.models.deletion.SET_NULL, |
||||
|
related_name='lived_in_authors', |
||||
|
to='hadis.city', |
||||
|
verbose_name='Lived in City', |
||||
|
), |
||||
|
), |
||||
|
migrations.AddField( |
||||
|
model_name='bookauthor', |
||||
|
name='died_in_city', |
||||
|
field=models.ForeignKey( |
||||
|
blank=True, |
||||
|
null=True, |
||||
|
on_delete=django.db.models.deletion.SET_NULL, |
||||
|
related_name='died_in_authors', |
||||
|
to='hadis.city', |
||||
|
verbose_name='Died in City', |
||||
|
), |
||||
|
), |
||||
|
migrations.AddField( |
||||
|
model_name='bookedition', |
||||
|
name='city_of_publication_city', |
||||
|
field=models.ForeignKey( |
||||
|
blank=True, |
||||
|
null=True, |
||||
|
on_delete=django.db.models.deletion.SET_NULL, |
||||
|
related_name='publication_editions', |
||||
|
to='hadis.city', |
||||
|
verbose_name='City of Publication (City)', |
||||
|
), |
||||
|
), |
||||
|
migrations.RunPython(populate_cities_from_existing_records, migrations.RunPython.noop), |
||||
|
] |
||||
@ -0,0 +1,48 @@ |
|||||
|
# Generated by Django 4.2.30 on 2026-10-20 11:52 |
||||
|
|
||||
|
from django.db import migrations |
||||
|
|
||||
|
|
||||
|
class Migration(migrations.Migration): |
||||
|
|
||||
|
dependencies = [ |
||||
|
('hadis', '0057_city_and_location_relations'), |
||||
|
] |
||||
|
|
||||
|
operations = [ |
||||
|
migrations.RenameIndex( |
||||
|
model_name='bookauthordate', |
||||
|
new_name='hadis_booka_date_ty_99a484_idx', |
||||
|
old_name='hadis_booka_date_ty_9a1e3c_idx', |
||||
|
), |
||||
|
migrations.RenameIndex( |
||||
|
model_name='bookauthordate', |
||||
|
new_name='hadis_booka_author__8e6b59_idx', |
||||
|
old_name='hadis_booka_author__6c2f1b_idx', |
||||
|
), |
||||
|
migrations.RenameIndex( |
||||
|
model_name='narratordate', |
||||
|
new_name='hadis_narra_date_ty_3906be_idx', |
||||
|
old_name='hadis_narra_date_ty_idx', |
||||
|
), |
||||
|
migrations.RenameIndex( |
||||
|
model_name='narratordate', |
||||
|
new_name='hadis_narra_transmi_9111bf_idx', |
||||
|
old_name='hadis_narra_transmi_idx', |
||||
|
), |
||||
|
migrations.RenameIndex( |
||||
|
model_name='yearvalue', |
||||
|
new_name='hadis_yearv_categor_a95c95_idx', |
||||
|
old_name='hadis_yearv_categor_idx', |
||||
|
), |
||||
|
migrations.RenameIndex( |
||||
|
model_name='yearvalue', |
||||
|
new_name='hadis_yearv_categor_eebdd3_idx', |
||||
|
old_name='hadis_yearv_cat_min_max_idx', |
||||
|
), |
||||
|
migrations.RenameIndex( |
||||
|
model_name='yearvalue', |
||||
|
new_name='hadis_yearv_parse_t_1d6351_idx', |
||||
|
old_name='hadis_yearv_parse_t_idx', |
||||
|
), |
||||
|
] |
||||
@ -0,0 +1,102 @@ |
|||||
|
# Generated by Django 4.2.30 on 2026-10-20 13:21 |
||||
|
|
||||
|
from django.db import migrations, models |
||||
|
from django.utils.text import slugify |
||||
|
|
||||
|
|
||||
|
def populate_kunyas_from_existing_records(apps, schema_editor): |
||||
|
Kunya = apps.get_model('hadis', 'Kunya') |
||||
|
Transmitters = apps.get_model('hadis', 'Transmitters') |
||||
|
|
||||
|
# Cache kunyas by normalized text key -> Kunya instance |
||||
|
kunya_by_text = {} |
||||
|
for k in Kunya.objects.all(): |
||||
|
if isinstance(k.title, list): |
||||
|
for entry in k.title: |
||||
|
if isinstance(entry, dict): |
||||
|
txt = (entry.get('text') or entry.get('title') or '').strip().lower() |
||||
|
if txt: |
||||
|
kunya_by_text[txt] = k |
||||
|
|
||||
|
def get_or_create_kunya(json_list): |
||||
|
if not json_list or not isinstance(json_list, list): |
||||
|
return None |
||||
|
cleaned = [] |
||||
|
lookup_keys = [] |
||||
|
for entry in json_list: |
||||
|
if isinstance(entry, dict): |
||||
|
lang = (entry.get('language_code') or '').strip() |
||||
|
txt = (entry.get('text') or entry.get('title') or '').strip() |
||||
|
if lang and txt: |
||||
|
cleaned.append({'language_code': lang, 'text': txt, 'title': txt}) |
||||
|
lookup_keys.append(txt.lower()) |
||||
|
if not cleaned: |
||||
|
return None |
||||
|
|
||||
|
for key in lookup_keys: |
||||
|
if key in kunya_by_text: |
||||
|
existing = kunya_by_text[key] |
||||
|
# Merge missing languages into existing kunya |
||||
|
existing_langs = { |
||||
|
item.get('language_code'): item |
||||
|
for item in (existing.title or []) |
||||
|
if isinstance(item, dict) and item.get('language_code') |
||||
|
} |
||||
|
updated = False |
||||
|
for item in cleaned: |
||||
|
if item['language_code'] not in existing_langs: |
||||
|
existing_langs[item['language_code']] = item |
||||
|
updated = True |
||||
|
if updated: |
||||
|
existing.title = list(existing_langs.values()) |
||||
|
existing.save(update_fields=['title']) |
||||
|
for k in lookup_keys: |
||||
|
kunya_by_text[k] = existing |
||||
|
return existing |
||||
|
|
||||
|
first_text = cleaned[0]['text'] |
||||
|
slug_val = slugify(first_text, allow_unicode=True)[:240] or 'kunya' |
||||
|
kunya_obj = Kunya.objects.create(title=cleaned, slug=slug_val) |
||||
|
for k in lookup_keys: |
||||
|
kunya_by_text[k] = kunya_obj |
||||
|
return kunya_obj |
||||
|
|
||||
|
for tr in Transmitters.objects.all().iterator(): |
||||
|
if tr.kunya and isinstance(tr.kunya, list): |
||||
|
k_obj = get_or_create_kunya(tr.kunya) |
||||
|
if k_obj: |
||||
|
tr.kunyas.add(k_obj) |
||||
|
|
||||
|
|
||||
|
class Migration(migrations.Migration): |
||||
|
|
||||
|
dependencies = [ |
||||
|
('hadis', '0058_rename_hadis_booka_date_ty_9a1e3c_idx_hadis_booka_date_ty_99a484_idx_and_more'), |
||||
|
] |
||||
|
|
||||
|
operations = [ |
||||
|
migrations.CreateModel( |
||||
|
name='Kunya', |
||||
|
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, null=True, verbose_name='slug')), |
||||
|
('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': 'Kunya', |
||||
|
'verbose_name_plural': 'Kunyas', |
||||
|
'ordering': ('-created_at', '-id'), |
||||
|
}, |
||||
|
), |
||||
|
migrations.AddField( |
||||
|
model_name='transmitters', |
||||
|
name='kunyas', |
||||
|
field=models.ManyToManyField(blank=True, related_name='transmitters', to='hadis.kunya', verbose_name='Kunyas'), |
||||
|
), |
||||
|
migrations.RunPython( |
||||
|
populate_kunyas_from_existing_records, |
||||
|
reverse_code=migrations.RunPython.noop, |
||||
|
), |
||||
|
] |
||||
@ -1,156 +1,321 @@ |
|||||
from django.urls import path, include |
from django.urls import path, include |
||||
|
|
||||
from rest_framework.routers import SimpleRouter |
from rest_framework.routers import SimpleRouter |
||||
|
|
||||
from .views.category import HadisCategorySectListView, HadisCategoryTreeView, CategoriesView, CategoriesBySectView, HadisCategorySelectBySectView, HadisCategorySelectBySectSourceView , HadisCategoryTreeNormalView ,test_deploy,debug_headers,HadisCategoryXMindView |
from .views.category import HadisCategorySectListView, HadisCategoryTreeView, CategoriesView, CategoriesBySectView, HadisCategorySelectBySectView, HadisCategorySelectBySectSourceView , HadisCategoryTreeNormalView ,test_deploy,debug_headers,HadisCategoryXMindView |
||||
|
|
||||
from .views.hadis import ( |
from .views.hadis import ( |
||||
|
|
||||
HadisCollectionListView, HadisListView, HadisBasicView, HadisDetailView, HadisSyncView, HadisTransmittersView, HadisCorrectionsView, CategoryHadisCorrectionsView, HadisMainListView, HadisFiltersView, HadisLayersView,PinnedHadisCollectionListView, HadisFiltersSyncAPIView, |
HadisCollectionListView, HadisListView, HadisBasicView, HadisDetailView, HadisSyncView, HadisTransmittersView, HadisCorrectionsView, CategoryHadisCorrectionsView, HadisMainListView, HadisFiltersView, HadisLayersView,PinnedHadisCollectionListView, HadisFiltersSyncAPIView, |
||||
|
|
||||
HadisCorrectionDetailView, HadisInterpretationDetailView, HadisInterpretsListView, HadisSourceDetailsView, HadisTagListView |
HadisCorrectionDetailView, HadisInterpretationDetailView, HadisInterpretsListView, HadisSourceDetailsView, HadisTagListView |
||||
|
|
||||
) |
) |
||||
|
|
||||
from .views.transmitter import ( |
from .views.transmitter import ( |
||||
|
|
||||
TransmitterView ,TransmitterDetailView, TransmitterSyncView,TransmitterOpinionView, TransmitterOriginalTextView, TransmitterFiltersView, |
TransmitterView ,TransmitterDetailView, TransmitterSyncView,TransmitterOpinionView, TransmitterOriginalTextView, TransmitterFiltersView, |
||||
|
|
||||
NarratorArgumentsListView , NarratorTeachersView, NarratorStudentsView, TransmitterOriginalTextDetailView |
NarratorArgumentsListView , NarratorTeachersView, NarratorStudentsView, TransmitterOriginalTextDetailView |
||||
|
|
||||
) |
) |
||||
|
|
||||
from .views.reference import BookDetailView, BookReferencesView, BookReferenceSyncView, BookAttributeView, ReferenceExcerptsListView |
from .views.reference import BookDetailView, BookReferencesView, BookReferenceSyncView, BookAttributeView, ReferenceExcerptsListView |
||||
|
|
||||
from .views.reference_v2 import BookReferenceV2DetailView, BookReferenceV2SyncView, BookReferenceV2ListView |
from .views.reference_v2 import BookReferenceV2DetailView, BookReferenceV2SyncView, BookReferenceV2ListView |
||||
|
|
||||
from .views.version import ContentReleaseSyncView, HadisDatabaseVersionView |
from .views.version import ContentReleaseSyncView, HadisDatabaseVersionView |
||||
|
|
||||
from .views.info import HadisInfoView |
from .views.info import HadisInfoView |
||||
|
|
||||
from .views_admin import ( |
from .views_admin import ( |
||||
|
|
||||
AdminHadisCategoryViewSet, AdminHadisViewSet, AdminHadisCorrectionViewSet, AdminCorrectionReferenceViewSet, AdminHadisReferenceViewSet, AdminInterpretationReferenceViewSet, AdminHadisInterpretationViewSet, AdminHadisStatusViewSet, AdminHadisTagViewSet, AdminHadisCollectionViewSet, |
AdminHadisCategoryViewSet, AdminHadisViewSet, AdminHadisCorrectionViewSet, AdminCorrectionReferenceViewSet, AdminHadisReferenceViewSet, AdminInterpretationReferenceViewSet, AdminHadisInterpretationViewSet, AdminHadisStatusViewSet, AdminHadisTagViewSet, AdminHadisCollectionViewSet, |
||||
|
|
||||
AdminBookSubjectAreaViewSet, AdminBookTypeViewSet, AdminBookTagViewSet, AdminBookAuthorViewSet, DovodiBookAuthorRutbaViewSet, AdminBookAttributeViewSet, AdminBookReferenceImageViewSet, AdminHadisReferenceImageViewSet, AdminBookReferenceDocumentViewSet, AdminBookReferenceViewSet, |
AdminBookSubjectAreaViewSet, AdminBookTypeViewSet, AdminBookTagViewSet, AdminBookAuthorViewSet, DovodiBookAuthorRutbaViewSet, AdminBookAttributeViewSet, AdminBookReferenceImageViewSet, AdminHadisReferenceImageViewSet, AdminBookReferenceDocumentViewSet, AdminBookReferenceViewSet, |
||||
|
|
||||
AdminTransmitterViewSet, AdminTransmitterReliabilityViewSet, DovodiBookSubjectAreaViewSet, DovodiBookTypeViewSet, |
AdminTransmitterViewSet, AdminTransmitterReliabilityViewSet, DovodiBookSubjectAreaViewSet, DovodiBookTypeViewSet, |
||||
|
|
||||
DovodiNarratorLayerViewSet, DovodiTransmitterReliabilityViewSet, DovodiOpinionStatusViewSet, DovodiScholarlyOpinionViewSet, DovodiYearValueViewSet, |
DovodiNarratorLayerViewSet, DovodiTransmitterReliabilityViewSet, DovodiOpinionStatusViewSet, DovodiScholarlyOpinionViewSet, DovodiYearValueViewSet, |
||||
|
|
||||
DovodiHadisSectViewSet, DovodiHadisCategoryViewSet, AdminCategoryQuranVerseViewSet, AdminHadisTransmitterViewSet, |
DovodiHadisSectViewSet, DovodiHadisCategoryViewSet, AdminCategoryQuranVerseViewSet, AdminHadisTransmitterViewSet, |
||||
|
|
||||
AdminTransmitterOpinionViewSet, AdminTransmitterOriginalTextViewSet, AdminOriginalTextReferenceViewSet, AdminBookEditionViewSet, AdminBookVolumeViewSet, |
AdminTransmitterOpinionViewSet, AdminTransmitterOriginalTextViewSet, AdminOriginalTextReferenceViewSet, AdminBookEditionViewSet, AdminBookVolumeViewSet, |
||||
AdminContentReleaseViewSet |
|
||||
|
|
||||
|
AdminContentReleaseViewSet, |
||||
|
|
||||
|
DovodiCityViewSet, |
||||
|
DovodiKunyaViewSet |
||||
|
|
||||
) |
) |
||||
|
|
||||
from .views.reference_v2 import BookAuthorListView, BookAuthorDetailView, AuthorReferencesListView, BookAuthorSyncView |
from .views.reference_v2 import BookAuthorListView, BookAuthorDetailView, AuthorReferencesListView, BookAuthorSyncView |
||||
|
|
||||
from utils.cache import smart_cached_view |
from utils.cache import smart_cached_view |
||||
|
|
||||
|
|
||||
|
|
||||
# Helper function to cache public views with smart invalidation and force-refresh support |
# Helper function to cache public views with smart invalidation and force-refresh support |
||||
|
|
||||
cached_view = smart_cached_view(60 * 60 * 2, key_prefix='hadis_api') |
cached_view = smart_cached_view(60 * 60 * 2, key_prefix='hadis_api') |
||||
|
|
||||
|
|
||||
|
|
||||
admin_router = SimpleRouter() |
admin_router = SimpleRouter() |
||||
|
|
||||
admin_router.register(r'hadises', AdminHadisViewSet, basename='admin-hadises') |
admin_router.register(r'hadises', AdminHadisViewSet, basename='admin-hadises') |
||||
|
|
||||
admin_router.register(r'categories', AdminHadisCategoryViewSet, basename='admin-hadis-categories') |
admin_router.register(r'categories', AdminHadisCategoryViewSet, basename='admin-hadis-categories') |
||||
|
|
||||
admin_router.register(r'quran-verses', AdminCategoryQuranVerseViewSet, basename='admin-category-quran-verses') |
admin_router.register(r'quran-verses', AdminCategoryQuranVerseViewSet, basename='admin-category-quran-verses') |
||||
|
|
||||
admin_router.register(r'dovodi-sects', DovodiHadisSectViewSet, basename='dovodi-hadis-sects') |
admin_router.register(r'dovodi-sects', DovodiHadisSectViewSet, basename='dovodi-hadis-sects') |
||||
|
|
||||
admin_router.register(r'dovodi-categories', DovodiHadisCategoryViewSet, basename='dovodi-hadis-categories') |
admin_router.register(r'dovodi-categories', DovodiHadisCategoryViewSet, basename='dovodi-hadis-categories') |
||||
|
|
||||
admin_router.register(r'corrections', AdminHadisCorrectionViewSet, basename='admin-hadis-corrections') |
admin_router.register(r'corrections', AdminHadisCorrectionViewSet, basename='admin-hadis-corrections') |
||||
|
|
||||
admin_router.register(r'correction-references', AdminCorrectionReferenceViewSet, basename='admin-correction-references') |
admin_router.register(r'correction-references', AdminCorrectionReferenceViewSet, basename='admin-correction-references') |
||||
|
|
||||
admin_router.register(r'hadis-references', AdminHadisReferenceViewSet, basename='admin-hadis-references') |
admin_router.register(r'hadis-references', AdminHadisReferenceViewSet, basename='admin-hadis-references') |
||||
|
|
||||
admin_router.register(r'interpretations', AdminHadisInterpretationViewSet, basename='admin-hadis-interpretations') |
admin_router.register(r'interpretations', AdminHadisInterpretationViewSet, basename='admin-hadis-interpretations') |
||||
|
|
||||
admin_router.register(r'interpretation-references', AdminInterpretationReferenceViewSet, basename='admin-interpretation-references') |
admin_router.register(r'interpretation-references', AdminInterpretationReferenceViewSet, basename='admin-interpretation-references') |
||||
|
|
||||
admin_router.register(r'original-text-references', AdminOriginalTextReferenceViewSet, basename='admin-original-text-references') |
admin_router.register(r'original-text-references', AdminOriginalTextReferenceViewSet, basename='admin-original-text-references') |
||||
|
|
||||
admin_router.register(r'statuses', AdminHadisStatusViewSet, basename='admin-hadis-statuses') |
admin_router.register(r'statuses', AdminHadisStatusViewSet, basename='admin-hadis-statuses') |
||||
|
|
||||
admin_router.register(r'tags', AdminHadisTagViewSet, basename='admin-hadis-tags') |
admin_router.register(r'tags', AdminHadisTagViewSet, basename='admin-hadis-tags') |
||||
|
|
||||
admin_router.register(r'collections', AdminHadisCollectionViewSet, basename='admin-hadis-collections') |
admin_router.register(r'collections', AdminHadisCollectionViewSet, basename='admin-hadis-collections') |
||||
|
|
||||
admin_router.register(r'references', AdminBookReferenceViewSet, basename='admin-book-references') |
admin_router.register(r'references', AdminBookReferenceViewSet, basename='admin-book-references') |
||||
|
|
||||
admin_router.register(r'subject-areas', AdminBookSubjectAreaViewSet, basename='admin-book-subject-areas') |
admin_router.register(r'subject-areas', AdminBookSubjectAreaViewSet, basename='admin-book-subject-areas') |
||||
|
|
||||
admin_router.register(r'book-types', AdminBookTypeViewSet, basename='admin-book-types') |
admin_router.register(r'book-types', AdminBookTypeViewSet, basename='admin-book-types') |
||||
|
|
||||
admin_router.register(r'book-tags', AdminBookTagViewSet, basename='admin-book-tags') |
admin_router.register(r'book-tags', AdminBookTagViewSet, basename='admin-book-tags') |
||||
|
|
||||
admin_router.register(r'dovodi-subject-areas', DovodiBookSubjectAreaViewSet, basename='dovodi-book-subject-areas') |
admin_router.register(r'dovodi-subject-areas', DovodiBookSubjectAreaViewSet, basename='dovodi-book-subject-areas') |
||||
|
|
||||
admin_router.register(r'dovodi-book-types', DovodiBookTypeViewSet, basename='dovodi-book-types') |
admin_router.register(r'dovodi-book-types', DovodiBookTypeViewSet, basename='dovodi-book-types') |
||||
|
|
||||
admin_router.register(r'dovodi-author-rutbas', DovodiBookAuthorRutbaViewSet, basename='dovodi-author-rutbas') |
admin_router.register(r'dovodi-author-rutbas', DovodiBookAuthorRutbaViewSet, basename='dovodi-author-rutbas') |
||||
|
|
||||
admin_router.register(r'book-authors', AdminBookAuthorViewSet, basename='admin-book-authors') |
admin_router.register(r'book-authors', AdminBookAuthorViewSet, basename='admin-book-authors') |
||||
|
|
||||
admin_router.register(r'book-attributes', AdminBookAttributeViewSet, basename='admin-book-attributes') |
admin_router.register(r'book-attributes', AdminBookAttributeViewSet, basename='admin-book-attributes') |
||||
|
|
||||
admin_router.register(r'book-images', AdminBookReferenceImageViewSet, basename='admin-book-images') |
admin_router.register(r'book-images', AdminBookReferenceImageViewSet, basename='admin-book-images') |
||||
|
|
||||
admin_router.register(r'hadis-reference-images', AdminHadisReferenceImageViewSet, basename='admin-hadis-reference-images') |
admin_router.register(r'hadis-reference-images', AdminHadisReferenceImageViewSet, basename='admin-hadis-reference-images') |
||||
|
|
||||
admin_router.register(r'book-documents', AdminBookReferenceDocumentViewSet, basename='admin-book-documents') |
admin_router.register(r'book-documents', AdminBookReferenceDocumentViewSet, basename='admin-book-documents') |
||||
|
|
||||
admin_router.register(r'transmitters', AdminTransmitterViewSet, basename='admin-transmitters') |
admin_router.register(r'transmitters', AdminTransmitterViewSet, basename='admin-transmitters') |
||||
|
|
||||
admin_router.register(r'transmitter-reliabilities', AdminTransmitterReliabilityViewSet, basename='admin-transmitter-reliabilities') |
admin_router.register(r'transmitter-reliabilities', AdminTransmitterReliabilityViewSet, basename='admin-transmitter-reliabilities') |
||||
|
|
||||
admin_router.register(r'dovodi-narrator-layers', DovodiNarratorLayerViewSet, basename='dovodi-narrator-layers') |
admin_router.register(r'dovodi-narrator-layers', DovodiNarratorLayerViewSet, basename='dovodi-narrator-layers') |
||||
|
|
||||
admin_router.register(r'dovodi-transmitter-reliabilities', DovodiTransmitterReliabilityViewSet, basename='dovodi-transmitter-reliabilities') |
admin_router.register(r'dovodi-transmitter-reliabilities', DovodiTransmitterReliabilityViewSet, basename='dovodi-transmitter-reliabilities') |
||||
|
|
||||
admin_router.register(r'dovodi-opinion-statuses', DovodiOpinionStatusViewSet, basename='dovodi-opinion-statuses') |
admin_router.register(r'dovodi-opinion-statuses', DovodiOpinionStatusViewSet, basename='dovodi-opinion-statuses') |
||||
|
|
||||
admin_router.register(r'dovodi-opinions', DovodiScholarlyOpinionViewSet, basename='dovodi-opinions') |
admin_router.register(r'dovodi-opinions', DovodiScholarlyOpinionViewSet, basename='dovodi-opinions') |
||||
|
|
||||
admin_router.register(r'dovodi-year-values', DovodiYearValueViewSet, basename='dovodi-year-values') |
admin_router.register(r'dovodi-year-values', DovodiYearValueViewSet, basename='dovodi-year-values') |
||||
|
|
||||
admin_router.register(r'hadis-transmitters', AdminHadisTransmitterViewSet, basename='admin-hadis-transmitters') |
admin_router.register(r'hadis-transmitters', AdminHadisTransmitterViewSet, basename='admin-hadis-transmitters') |
||||
|
|
||||
admin_router.register(r'transmitter-opinions', AdminTransmitterOpinionViewSet, basename='admin-transmitter-opinions') |
admin_router.register(r'transmitter-opinions', AdminTransmitterOpinionViewSet, basename='admin-transmitter-opinions') |
||||
|
|
||||
admin_router.register(r'transmitter-original-texts', AdminTransmitterOriginalTextViewSet, basename='admin-transmitter-original-texts') |
admin_router.register(r'transmitter-original-texts', AdminTransmitterOriginalTextViewSet, basename='admin-transmitter-original-texts') |
||||
|
|
||||
admin_router.register(r'book-editions', AdminBookEditionViewSet, basename='admin-book-editions') |
admin_router.register(r'book-editions', AdminBookEditionViewSet, basename='admin-book-editions') |
||||
|
|
||||
admin_router.register(r'book-volumes', AdminBookVolumeViewSet, basename='admin-book-volumes') |
admin_router.register(r'book-volumes', AdminBookVolumeViewSet, basename='admin-book-volumes') |
||||
|
|
||||
admin_router.register(r'content-version', AdminContentReleaseViewSet, basename='admin-content-version') |
admin_router.register(r'content-version', AdminContentReleaseViewSet, basename='admin-content-version') |
||||
|
|
||||
|
admin_router.register(r'dovodi-cities', DovodiCityViewSet, basename='dovodi-cities') |
||||
|
|
||||
|
admin_router.register(r'cities', DovodiCityViewSet, basename='admin-cities') |
||||
|
admin_router.register(r'dovodi-kunyas', DovodiKunyaViewSet, basename='dovodi-kunyas') |
||||
|
admin_router.register(r'kunyas', DovodiKunyaViewSet, basename='admin-kunyas') |
||||
|
|
||||
|
|
||||
|
|
||||
# Hide admin viewsets from swagger |
# Hide admin viewsets from swagger |
||||
|
|
||||
for prefix, viewset, basename in admin_router.registry: |
for prefix, viewset, basename in admin_router.registry: |
||||
|
|
||||
viewset.swagger_schema = None |
viewset.swagger_schema = None |
||||
|
|
||||
|
|
||||
|
|
||||
urlpatterns = [ |
urlpatterns = [ |
||||
|
|
||||
|
|
||||
|
|
||||
path('sync/authors/', cached_view(BookAuthorSyncView.as_view()), name='sync-author-list'), |
path('sync/authors/', cached_view(BookAuthorSyncView.as_view()), name='sync-author-list'), |
||||
|
|
||||
path('authors/<str:author_slug>/references/', cached_view(AuthorReferencesListView.as_view()), name='author-references'), |
path('authors/<str:author_slug>/references/', cached_view(AuthorReferencesListView.as_view()), name='author-references'), |
||||
|
|
||||
path('authors/<str:author_slug>/', cached_view(BookAuthorDetailView.as_view()), name='author-detail'), |
path('authors/<str:author_slug>/', cached_view(BookAuthorDetailView.as_view()), name='author-detail'), |
||||
|
|
||||
path('authors/', cached_view(BookAuthorListView.as_view()), name='author-list'), |
path('authors/', cached_view(BookAuthorListView.as_view()), name='author-list'), |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
# Admin endpoints |
# Admin endpoints |
||||
|
|
||||
path('admin/', include(admin_router.urls)), |
path('admin/', include(admin_router.urls)), |
||||
|
|
||||
|
|
||||
|
|
||||
# Most specific first (with parameters) |
# Most specific first (with parameters) |
||||
|
|
||||
path('pinned-collections/', cached_view(PinnedHadisCollectionListView.as_view()), name='pinned-hadis-collection-list'), |
path('pinned-collections/', cached_view(PinnedHadisCollectionListView.as_view()), name='pinned-hadis-collection-list'), |
||||
|
|
||||
path('collections/', cached_view(HadisCollectionListView.as_view()), name='hadis-collection-list'), |
path('collections/', cached_view(HadisCollectionListView.as_view()), name='hadis-collection-list'), |
||||
|
|
||||
path('sync/sects/', cached_view(HadisCategorySectListView.as_view()), name='hadis-sect-list'), |
path('sync/sects/', cached_view(HadisCategorySectListView.as_view()), name='hadis-sect-list'), |
||||
|
|
||||
path('sync/categories/tree/', HadisCategoryTreeView.as_view(), name='hadis-category-tree'), |
path('sync/categories/tree/', HadisCategoryTreeView.as_view(), name='hadis-category-tree'), |
||||
|
|
||||
path('sync/hadis/', cached_view(HadisSyncView.as_view()), name='hadis-sync'), |
path('sync/hadis/', cached_view(HadisSyncView.as_view()), name='hadis-sync'), |
||||
|
|
||||
path('sync/narrators/', cached_view(TransmitterSyncView.as_view()), name='transmitter-sync'), |
path('sync/narrators/', cached_view(TransmitterSyncView.as_view()), name='transmitter-sync'), |
||||
|
|
||||
path('sync/references/', cached_view(BookReferenceSyncView.as_view()), name='reference-sync'), |
path('sync/references/', cached_view(BookReferenceSyncView.as_view()), name='reference-sync'), |
||||
|
|
||||
path('v2/sync/references/', cached_view(BookReferenceV2SyncView.as_view()), name='reference-sync-v2'), |
path('v2/sync/references/', cached_view(BookReferenceV2SyncView.as_view()), name='reference-sync-v2'), |
||||
|
|
||||
path('sync/version/', ContentReleaseSyncView.as_view(), name='content-release-sync'), |
path('sync/version/', ContentReleaseSyncView.as_view(), name='content-release-sync'), |
||||
|
|
||||
path('sync/database/', HadisDatabaseVersionView.as_view(), name='hadis-database-sync'), |
path('sync/database/', HadisDatabaseVersionView.as_view(), name='hadis-database-sync'), |
||||
|
|
||||
path('sync/filters/', cached_view(HadisFiltersSyncAPIView.as_view()), name='hadis-filters-sync'), |
path('sync/filters/', cached_view(HadisFiltersSyncAPIView.as_view()), name='hadis-filters-sync'), |
||||
|
|
||||
path('info/', cached_view(HadisInfoView.as_view()), name='hadis-info'), |
path('info/', cached_view(HadisInfoView.as_view()), name='hadis-info'), |
||||
|
|
||||
path('tags/', cached_view(HadisTagListView.as_view()), name='hadis-tag-list'), |
path('tags/', cached_view(HadisTagListView.as_view()), name='hadis-tag-list'), |
||||
|
|
||||
|
|
||||
|
|
||||
# Category paths (more specific first) |
# Category paths (more specific first) |
||||
|
|
||||
path('categories/tree/', HadisCategoryTreeNormalView.as_view(), name='hadis-category-tree-normal'), |
path('categories/tree/', HadisCategoryTreeNormalView.as_view(), name='hadis-category-tree-normal'), |
||||
|
|
||||
path('categories/<str:sect_type>/<str:slug>/<str:source_type>/', HadisCategorySelectBySectSourceView.as_view(), name='categories-tree-by-sect-source'), |
path('categories/<str:sect_type>/<str:slug>/<str:source_type>/', HadisCategorySelectBySectSourceView.as_view(), name='categories-tree-by-sect-source'), |
||||
|
|
||||
path('categories/<str:sect_type>/<str:slug>/', HadisCategorySelectBySectView.as_view(), name='categories-tree-by-sect'), |
path('categories/<str:sect_type>/<str:slug>/', HadisCategorySelectBySectView.as_view(), name='categories-tree-by-sect'), |
||||
|
|
||||
path('categories/<str:sect_type>/', CategoriesBySectView.as_view(), name='categories-by-sect'), |
path('categories/<str:sect_type>/', CategoriesBySectView.as_view(), name='categories-by-sect'), |
||||
|
|
||||
path('categories/', CategoriesView.as_view(), name='categories'), # ← Least specific LAST |
path('categories/', CategoriesView.as_view(), name='categories'), # ← Least specific LAST |
||||
|
|
||||
|
|
||||
|
|
||||
# Hadis paths |
# Hadis paths |
||||
|
|
||||
path('category/<str:category_slug>/xmind/', HadisCategoryXMindView.as_view(), name='hadis-category-xmind'), # ← Must be before other category paths |
path('category/<str:category_slug>/xmind/', HadisCategoryXMindView.as_view(), name='hadis-category-xmind'), # ← Must be before other category paths |
||||
|
|
||||
path('category/<str:category_slug>/corrections/', CategoryHadisCorrectionsView.as_view(), name='category-hadis-corrections'), |
path('category/<str:category_slug>/corrections/', CategoryHadisCorrectionsView.as_view(), name='category-hadis-corrections'), |
||||
|
|
||||
path('category/<str:category_slug>/', HadisListView.as_view(), name='hadis-list'), |
path('category/<str:category_slug>/', HadisListView.as_view(), name='hadis-list'), |
||||
|
|
||||
path('arguments/filters/', cached_view(HadisFiltersView.as_view()), name='hadis-filters'), |
path('arguments/filters/', cached_view(HadisFiltersView.as_view()), name='hadis-filters'), |
||||
|
|
||||
path('arguments/', HadisMainListView.as_view(), name='hadis-main-list'), |
path('arguments/', HadisMainListView.as_view(), name='hadis-main-list'), |
||||
|
|
||||
|
|
||||
|
|
||||
# Narrator paths |
# Narrator paths |
||||
|
|
||||
path('narrators/filters/', cached_view(TransmitterFiltersView.as_view()), name='narrator-filters'), |
path('narrators/filters/', cached_view(TransmitterFiltersView.as_view()), name='narrator-filters'), |
||||
|
|
||||
path('narrators/<str:narrator_slug>/teachers/', cached_view(NarratorTeachersView.as_view()), name='narrator-teachers'), |
path('narrators/<str:narrator_slug>/teachers/', cached_view(NarratorTeachersView.as_view()), name='narrator-teachers'), |
||||
|
|
||||
path('narrators/<str:narrator_slug>/students/', cached_view(NarratorStudentsView.as_view()), name='narrator-students'), |
path('narrators/<str:narrator_slug>/students/', cached_view(NarratorStudentsView.as_view()), name='narrator-students'), |
||||
|
|
||||
path('narrators/<str:narrator_slug>/arguments/', cached_view(NarratorArgumentsListView.as_view()), name='narrator-arguments'), |
path('narrators/<str:narrator_slug>/arguments/', cached_view(NarratorArgumentsListView.as_view()), name='narrator-arguments'), |
||||
|
|
||||
path('narrators/<str:narrator_slug>/opinions/', cached_view(TransmitterOpinionView.as_view()), name='narrator-opinions'), |
path('narrators/<str:narrator_slug>/opinions/', cached_view(TransmitterOpinionView.as_view()), name='narrator-opinions'), |
||||
|
|
||||
path('narrators/<str:narrator_slug>/original_texts/', TransmitterOriginalTextView.as_view(), name='narrator-original-texts'), |
path('narrators/<str:narrator_slug>/original_texts/', TransmitterOriginalTextView.as_view(), name='narrator-original-texts'), |
||||
|
|
||||
path('narrators/<str:narrator_slug>/', cached_view(TransmitterDetailView.as_view()), name='narrator-detail'), |
path('narrators/<str:narrator_slug>/', cached_view(TransmitterDetailView.as_view()), name='narrator-detail'), |
||||
|
|
||||
path('narrators/', cached_view(TransmitterView.as_view()), name='narrators'), |
path('narrators/', cached_view(TransmitterView.as_view()), name='narrators'), |
||||
|
|
||||
|
|
||||
|
|
||||
# Reference paths |
# Reference paths |
||||
|
|
||||
path('v2/references/<str:reference_slug>/excerpts/', cached_view(ReferenceExcerptsListView.as_view()), name='reference-excerpts-v2'), |
path('v2/references/<str:reference_slug>/excerpts/', cached_view(ReferenceExcerptsListView.as_view()), name='reference-excerpts-v2'), |
||||
|
|
||||
path('v2/references/<str:slug>/', cached_view(BookReferenceV2DetailView.as_view()), name='reference-detail-v2'), |
path('v2/references/<str:slug>/', cached_view(BookReferenceV2DetailView.as_view()), name='reference-detail-v2'), |
||||
|
|
||||
path('v2/references/', cached_view(BookReferenceV2ListView.as_view()), name='reference-list-v2'), |
path('v2/references/', cached_view(BookReferenceV2ListView.as_view()), name='reference-list-v2'), |
||||
|
|
||||
path('references/<str:reference_slug>/excerpts/', cached_view(ReferenceExcerptsListView.as_view()), name='reference-excerpts'), |
path('references/<str:reference_slug>/excerpts/', cached_view(ReferenceExcerptsListView.as_view()), name='reference-excerpts'), |
||||
|
|
||||
path('references/<str:reference_slug>/', cached_view(BookDetailView.as_view()), name='reference-detail'), |
path('references/<str:reference_slug>/', cached_view(BookDetailView.as_view()), name='reference-detail'), |
||||
|
|
||||
path('references/<str:reference_slug>', cached_view(BookDetailView.as_view()), name='reference-detail-no-slash'), |
path('references/<str:reference_slug>', cached_view(BookDetailView.as_view()), name='reference-detail-no-slash'), |
||||
|
|
||||
path('references/', cached_view(BookReferencesView.as_view()), name='references'), |
path('references/', cached_view(BookReferencesView.as_view()), name='references'), |
||||
|
|
||||
|
|
||||
|
|
||||
# Hadis detail paths (with slug, more specific) |
# Hadis detail paths (with slug, more specific) |
||||
|
|
||||
path('<str:hadis_slug>/source-details/', cached_view(HadisSourceDetailsView.as_view()), name='hadis-source-details'), |
path('<str:hadis_slug>/source-details/', cached_view(HadisSourceDetailsView.as_view()), name='hadis-source-details'), |
||||
|
|
||||
path('<str:hadis_slug>/sources/', cached_view(HadisSourceDetailsView.as_view()), name='hadis-sources'), |
path('<str:hadis_slug>/sources/', cached_view(HadisSourceDetailsView.as_view()), name='hadis-sources'), |
||||
|
|
||||
path('<str:hadis_slug>/detail/', HadisDetailView.as_view(), name='hadis-detail'), |
path('<str:hadis_slug>/detail/', HadisDetailView.as_view(), name='hadis-detail'), |
||||
|
|
||||
path('<str:hadis_slug>/transmitters/', cached_view(HadisTransmittersView.as_view()), name='hadis-transmitters'), |
path('<str:hadis_slug>/transmitters/', cached_view(HadisTransmittersView.as_view()), name='hadis-transmitters'), |
||||
|
|
||||
path('<str:hadis_slug>/transmitters/layers/', cached_view(HadisLayersView.as_view()), name='hadis-layers'), |
path('<str:hadis_slug>/transmitters/layers/', cached_view(HadisLayersView.as_view()), name='hadis-layers'), |
||||
|
|
||||
path('<str:hadis_slug>/corrections/', HadisCorrectionsView.as_view(), name='hadis-corrections'), |
path('<str:hadis_slug>/corrections/', HadisCorrectionsView.as_view(), name='hadis-corrections'), |
||||
|
|
||||
path('<str:hadis_slug>/interprets/', HadisInterpretsListView.as_view(), name='hadis-interprets'), |
path('<str:hadis_slug>/interprets/', HadisInterpretsListView.as_view(), name='hadis-interprets'), |
||||
|
|
||||
path('<str:hadis_slug>/', HadisBasicView.as_view(), name='hadis-basic'), # ← Least specific LAST |
path('<str:hadis_slug>/', HadisBasicView.as_view(), name='hadis-basic'), # ← Least specific LAST |
||||
|
|
||||
|
|
||||
|
|
||||
# path('test/test-deploy',test_deploy , name='test'), |
# path('test/test-deploy',test_deploy , name='test'), |
||||
|
|
||||
# path('debug-headers',debug_headers , name='headers'), |
# path('debug-headers',debug_headers , name='headers'), |
||||
|
|
||||
|
|
||||
|
|
||||
# Interpretation Detail Endpoint (تفسیر بر اساس آیدی) |
# Interpretation Detail Endpoint (تفسیر بر اساس آیدی) |
||||
|
|
||||
path('interpretations/<str:interpretation_slug>/', cached_view(HadisInterpretationDetailView.as_view()), name='interpretation-detail'), |
path('interpretations/<str:interpretation_slug>/', cached_view(HadisInterpretationDetailView.as_view()), name='interpretation-detail'), |
||||
|
|
||||
# Correction Detail Endpoint (تصحیح بر اساس اسلاگ) |
# Correction Detail Endpoint (تصحیح بر اساس اسلاگ) |
||||
|
|
||||
path('corrections/<str:correction_slug>/', HadisCorrectionDetailView.as_view(), name='correction-detail'), |
path('corrections/<str:correction_slug>/', HadisCorrectionDetailView.as_view(), name='correction-detail'), |
||||
|
|
||||
|
|
||||
|
|
||||
# Original Text Detail Endpoint (متن اصلی راوی بر اساس اسلاگ) |
# Original Text Detail Endpoint (متن اصلی راوی بر اساس اسلاگ) |
||||
|
|
||||
path('original-texts/<str:original_text_slug>/', cached_view(TransmitterOriginalTextDetailView.as_view()), name='original-text-detail'), |
path('original-texts/<str:original_text_slug>/', cached_view(TransmitterOriginalTextDetailView.as_view()), name='original-text-detail'), |
||||
|
|
||||
|
|
||||
|
|
||||
] |
] |
||||
|
|
||||
1490
apps/hadis/views_admin.py
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
Write
Preview
Loading…
Cancel
Save
Reference in new issue