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
-
122apps/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 rest_framework.routers import SimpleRouter |
|||
|
|||
from .views.category import HadisCategorySectListView, HadisCategoryTreeView, CategoriesView, CategoriesBySectView, HadisCategorySelectBySectView, HadisCategorySelectBySectSourceView , HadisCategoryTreeNormalView ,test_deploy,debug_headers,HadisCategoryXMindView |
|||
|
|||
from .views.hadis import ( |
|||
|
|||
HadisCollectionListView, HadisListView, HadisBasicView, HadisDetailView, HadisSyncView, HadisTransmittersView, HadisCorrectionsView, CategoryHadisCorrectionsView, HadisMainListView, HadisFiltersView, HadisLayersView,PinnedHadisCollectionListView, HadisFiltersSyncAPIView, |
|||
|
|||
HadisCorrectionDetailView, HadisInterpretationDetailView, HadisInterpretsListView, HadisSourceDetailsView, HadisTagListView |
|||
|
|||
) |
|||
|
|||
from .views.transmitter import ( |
|||
|
|||
TransmitterView ,TransmitterDetailView, TransmitterSyncView,TransmitterOpinionView, TransmitterOriginalTextView, TransmitterFiltersView, |
|||
|
|||
NarratorArgumentsListView , NarratorTeachersView, NarratorStudentsView, TransmitterOriginalTextDetailView |
|||
|
|||
) |
|||
|
|||
from .views.reference import BookDetailView, BookReferencesView, BookReferenceSyncView, BookAttributeView, ReferenceExcerptsListView |
|||
|
|||
from .views.reference_v2 import BookReferenceV2DetailView, BookReferenceV2SyncView, BookReferenceV2ListView |
|||
|
|||
from .views.version import ContentReleaseSyncView, HadisDatabaseVersionView |
|||
|
|||
from .views.info import HadisInfoView |
|||
|
|||
from .views_admin import ( |
|||
|
|||
AdminHadisCategoryViewSet, AdminHadisViewSet, AdminHadisCorrectionViewSet, AdminCorrectionReferenceViewSet, AdminHadisReferenceViewSet, AdminInterpretationReferenceViewSet, AdminHadisInterpretationViewSet, AdminHadisStatusViewSet, AdminHadisTagViewSet, AdminHadisCollectionViewSet, |
|||
|
|||
AdminBookSubjectAreaViewSet, AdminBookTypeViewSet, AdminBookTagViewSet, AdminBookAuthorViewSet, DovodiBookAuthorRutbaViewSet, AdminBookAttributeViewSet, AdminBookReferenceImageViewSet, AdminHadisReferenceImageViewSet, AdminBookReferenceDocumentViewSet, AdminBookReferenceViewSet, |
|||
|
|||
AdminTransmitterViewSet, AdminTransmitterReliabilityViewSet, DovodiBookSubjectAreaViewSet, DovodiBookTypeViewSet, |
|||
|
|||
DovodiNarratorLayerViewSet, DovodiTransmitterReliabilityViewSet, DovodiOpinionStatusViewSet, DovodiScholarlyOpinionViewSet, DovodiYearValueViewSet, |
|||
|
|||
DovodiHadisSectViewSet, DovodiHadisCategoryViewSet, AdminCategoryQuranVerseViewSet, AdminHadisTransmitterViewSet, |
|||
|
|||
AdminTransmitterOpinionViewSet, AdminTransmitterOriginalTextViewSet, AdminOriginalTextReferenceViewSet, AdminBookEditionViewSet, AdminBookVolumeViewSet, |
|||
AdminContentReleaseViewSet |
|||
|
|||
AdminContentReleaseViewSet, |
|||
|
|||
DovodiCityViewSet, |
|||
DovodiKunyaViewSet |
|||
|
|||
) |
|||
|
|||
from .views.reference_v2 import BookAuthorListView, BookAuthorDetailView, AuthorReferencesListView, BookAuthorSyncView |
|||
|
|||
from utils.cache import smart_cached_view |
|||
|
|||
|
|||
|
|||
# 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') |
|||
|
|||
|
|||
|
|||
admin_router = SimpleRouter() |
|||
|
|||
admin_router.register(r'hadises', AdminHadisViewSet, basename='admin-hadises') |
|||
|
|||
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'dovodi-sects', DovodiHadisSectViewSet, basename='dovodi-hadis-sects') |
|||
|
|||
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'correction-references', AdminCorrectionReferenceViewSet, basename='admin-correction-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'interpretation-references', AdminInterpretationReferenceViewSet, basename='admin-interpretation-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'tags', AdminHadisTagViewSet, basename='admin-hadis-tags') |
|||
|
|||
admin_router.register(r'collections', AdminHadisCollectionViewSet, basename='admin-hadis-collections') |
|||
|
|||
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'book-types', AdminBookTypeViewSet, basename='admin-book-types') |
|||
|
|||
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-book-types', DovodiBookTypeViewSet, basename='dovodi-book-types') |
|||
|
|||
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-attributes', AdminBookAttributeViewSet, basename='admin-book-attributes') |
|||
|
|||
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'book-documents', AdminBookReferenceDocumentViewSet, basename='admin-book-documents') |
|||
|
|||
admin_router.register(r'transmitters', AdminTransmitterViewSet, basename='admin-transmitters') |
|||
|
|||
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-transmitter-reliabilities', DovodiTransmitterReliabilityViewSet, basename='dovodi-transmitter-reliabilities') |
|||
|
|||
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-year-values', DovodiYearValueViewSet, basename='dovodi-year-values') |
|||
|
|||
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-original-texts', AdminTransmitterOriginalTextViewSet, basename='admin-transmitter-original-texts') |
|||
|
|||
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'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 |
|||
|
|||
for prefix, viewset, basename in admin_router.registry: |
|||
|
|||
viewset.swagger_schema = None |
|||
|
|||
|
|||
|
|||
urlpatterns = [ |
|||
|
|||
|
|||
|
|||
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>/', cached_view(BookAuthorDetailView.as_view()), name='author-detail'), |
|||
|
|||
path('authors/', cached_view(BookAuthorListView.as_view()), name='author-list'), |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
# Admin endpoints |
|||
|
|||
path('admin/', include(admin_router.urls)), |
|||
|
|||
|
|||
|
|||
# Most specific first (with parameters) |
|||
|
|||
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('sync/sects/', cached_view(HadisCategorySectListView.as_view()), name='hadis-sect-list'), |
|||
|
|||
path('sync/categories/tree/', HadisCategoryTreeView.as_view(), name='hadis-category-tree'), |
|||
|
|||
path('sync/hadis/', cached_view(HadisSyncView.as_view()), name='hadis-sync'), |
|||
|
|||
path('sync/narrators/', cached_view(TransmitterSyncView.as_view()), name='transmitter-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('sync/version/', ContentReleaseSyncView.as_view(), name='content-release-sync'), |
|||
|
|||
path('sync/database/', HadisDatabaseVersionView.as_view(), name='hadis-database-sync'), |
|||
|
|||
path('sync/filters/', cached_view(HadisFiltersSyncAPIView.as_view()), name='hadis-filters-sync'), |
|||
|
|||
path('info/', cached_view(HadisInfoView.as_view()), name='hadis-info'), |
|||
|
|||
path('tags/', cached_view(HadisTagListView.as_view()), name='hadis-tag-list'), |
|||
|
|||
|
|||
|
|||
# Category paths (more specific first) |
|||
|
|||
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>/', HadisCategorySelectBySectView.as_view(), name='categories-tree-by-sect'), |
|||
|
|||
path('categories/<str:sect_type>/', CategoriesBySectView.as_view(), name='categories-by-sect'), |
|||
|
|||
path('categories/', CategoriesView.as_view(), name='categories'), # ← Least specific LAST |
|||
|
|||
|
|||
|
|||
# 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>/corrections/', CategoryHadisCorrectionsView.as_view(), name='category-hadis-corrections'), |
|||
|
|||
path('category/<str:category_slug>/', HadisListView.as_view(), name='hadis-list'), |
|||
|
|||
path('arguments/filters/', cached_view(HadisFiltersView.as_view()), name='hadis-filters'), |
|||
|
|||
path('arguments/', HadisMainListView.as_view(), name='hadis-main-list'), |
|||
|
|||
|
|||
|
|||
# Narrator paths |
|||
|
|||
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>/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>/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>/', cached_view(TransmitterDetailView.as_view()), name='narrator-detail'), |
|||
|
|||
path('narrators/', cached_view(TransmitterView.as_view()), name='narrators'), |
|||
|
|||
|
|||
|
|||
# Reference paths |
|||
|
|||
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/', 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>/', 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/', cached_view(BookReferencesView.as_view()), name='references'), |
|||
|
|||
|
|||
|
|||
# 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>/sources/', cached_view(HadisSourceDetailsView.as_view()), name='hadis-sources'), |
|||
|
|||
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/layers/', cached_view(HadisLayersView.as_view()), name='hadis-layers'), |
|||
|
|||
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>/', HadisBasicView.as_view(), name='hadis-basic'), # ← Least specific LAST |
|||
|
|||
|
|||
|
|||
# path('test/test-deploy',test_deploy , name='test'), |
|||
|
|||
# path('debug-headers',debug_headers , name='headers'), |
|||
|
|||
|
|||
|
|||
# Interpretation Detail Endpoint (تفسیر بر اساس آیدی) |
|||
|
|||
path('interpretations/<str:interpretation_slug>/', cached_view(HadisInterpretationDetailView.as_view()), name='interpretation-detail'), |
|||
|
|||
# Correction Detail Endpoint (تصحیح بر اساس اسلاگ) |
|||
|
|||
path('corrections/<str:correction_slug>/', HadisCorrectionDetailView.as_view(), name='correction-detail'), |
|||
|
|||
|
|||
|
|||
# Original Text Detail Endpoint (متن اصلی راوی بر اساس اسلاگ) |
|||
|
|||
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