Browse Source
feat(transmitters): add TransmitterFeature model, relational features, and admin viewsets
feat(transmitters): add TransmitterFeature model, relational features, and admin viewsets
- Introduce TransmitterFeature model with title, slug, and timestamps - Link features relation to Transmitters and BookAuthors - Add DovodiTransmitterFeatureViewSet and register routes in admin router - Add migration 0060 to create table and populate from existing features data - Update RELATIONAL_MIGRATION_ROADMAP.md with completed features migrationmaster
7 changed files with 1399 additions and 1135 deletions
-
36RELATIONAL_MIGRATION_ROADMAP.md
-
112apps/hadis/migrations/0060_transmitter_feature_and_relations.py
-
6apps/hadis/models/reference.py
-
39apps/hadis/models/transmitter.py
-
92apps/hadis/serializers/serializers_admin.py
-
5apps/hadis/urls.py
-
20apps/hadis/views_admin.py
@ -0,0 +1,112 @@ |
|||||
|
# Generated by Django 4.2.30 on 2026-10-20 14:18 |
||||
|
|
||||
|
from django.db import migrations, models |
||||
|
from django.utils.text import slugify |
||||
|
|
||||
|
|
||||
|
def populate_features_from_existing_records(apps, schema_editor): |
||||
|
TransmitterFeature = apps.get_model('hadis', 'TransmitterFeature') |
||||
|
Transmitters = apps.get_model('hadis', 'Transmitters') |
||||
|
BookAuthor = apps.get_model('hadis', 'BookAuthor') |
||||
|
|
||||
|
feature_by_text = {} |
||||
|
for f in TransmitterFeature.objects.all(): |
||||
|
if isinstance(f.title, list): |
||||
|
for entry in f.title: |
||||
|
if isinstance(entry, dict): |
||||
|
txt = (entry.get('text') or entry.get('title') or '').strip().lower() |
||||
|
if txt: |
||||
|
feature_by_text[txt] = f |
||||
|
|
||||
|
def get_or_create_feature(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 feature_by_text: |
||||
|
existing = feature_by_text[key] |
||||
|
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: |
||||
|
feature_by_text[k] = existing |
||||
|
return existing |
||||
|
|
||||
|
first_text = cleaned[0]['text'] |
||||
|
slug_val = slugify(first_text, allow_unicode=True)[:240] or 'feature' |
||||
|
feat_obj = TransmitterFeature.objects.create(title=cleaned, slug=slug_val) |
||||
|
for k in lookup_keys: |
||||
|
feature_by_text[k] = feat_obj |
||||
|
return feat_obj |
||||
|
|
||||
|
for tr in Transmitters.objects.all().iterator(): |
||||
|
if tr.features and isinstance(tr.features, list): |
||||
|
f_obj = get_or_create_feature(tr.features) |
||||
|
if f_obj: |
||||
|
tr.features_list.add(f_obj) |
||||
|
|
||||
|
for author in BookAuthor.objects.all().iterator(): |
||||
|
if author.features and isinstance(author.features, list): |
||||
|
f_obj = get_or_create_feature(author.features) |
||||
|
if f_obj: |
||||
|
author.features_list.add(f_obj) |
||||
|
|
||||
|
|
||||
|
class Migration(migrations.Migration): |
||||
|
|
||||
|
dependencies = [ |
||||
|
('hadis', '0059_kunya_and_relations'), |
||||
|
] |
||||
|
|
||||
|
operations = [ |
||||
|
migrations.CreateModel( |
||||
|
name='TransmitterFeature', |
||||
|
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': 'Transmitter Feature', |
||||
|
'verbose_name_plural': 'Transmitter Features', |
||||
|
'ordering': ('-created_at', '-id'), |
||||
|
}, |
||||
|
), |
||||
|
migrations.AddField( |
||||
|
model_name='bookauthor', |
||||
|
name='features_list', |
||||
|
field=models.ManyToManyField(blank=True, related_name='authors', to='hadis.transmitterfeature', verbose_name='Features (M2M)'), |
||||
|
), |
||||
|
migrations.AddField( |
||||
|
model_name='transmitters', |
||||
|
name='features_list', |
||||
|
field=models.ManyToManyField(blank=True, related_name='transmitters', to='hadis.transmitterfeature', verbose_name='Features (M2M)'), |
||||
|
), |
||||
|
migrations.RunPython( |
||||
|
populate_features_from_existing_records, |
||||
|
reverse_code=migrations.RunPython.noop, |
||||
|
), |
||||
|
] |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue