Browse Source

Add BookSubjectArea and BookType models, enhance HadisStatus and BookReference with new fields

- Introduced BookSubjectArea and BookType models with JSONField for titles.
- Added 'description' field to HadisStatus model.
- Updated BookReference model to include ManyToMany relationship with BookSubjectArea and a ForeignKey to BookType.
- Enhanced admin interfaces for HadisStatus, BookReference, and BookAuthor to accommodate new fields and improve usability.
master
Mohsen Taba 4 months ago
parent
commit
e83b321b70
  1. 7
      apps/hadis/admin/hadis.py
  2. 103
      apps/hadis/admin/reference.py
  3. 85
      apps/hadis/migrations/0003_booksubjectarea_booktype_hadisstatus_description_and_more.py
  4. 1
      apps/hadis/models/hadis.py
  5. 52
      apps/hadis/models/reference.py

7
apps/hadis/admin/hadis.py

@ -186,14 +186,15 @@ class HadisTagAdmin(ModelAdmin):
class HadisStatusAdmin(ModelAdmin):
"""Admin for HadisStatus model"""
list_display = ('title', 'color', 'order')
list_display = ('title', 'color', 'order', 'description')
list_filter = ('color',)
search_fields = ('title',)
search_fields = ('title', 'description')
ordering = ('order',)
readonly_fields = ('slug',)
fieldsets = (
(None, {
'fields': ('title', 'color', 'order')
'fields': ('title', 'slug', 'color', 'order', 'description')
}),
)

103
apps/hadis/admin/reference.py

@ -14,7 +14,9 @@ from ..models import (
BookReference,
BookReferenceImage,
BookAuthor,
BookAttribute
BookAttribute,
BookSubjectArea,
BookType
)
# -----------------------------------------------------------------------------
@ -259,31 +261,34 @@ class BookReferenceAdmin(ModelAdmin):
# Use custom methods for JSON fields to show readable text
list_display = (
'get_title_display',
'slug',
'get_publisher_display',
'year_of_publication',
'rate',
'get_title_display',
'slug',
'get_publisher_display',
'year_of_publication',
'rate',
'created_at'
)
list_filter = ('year_of_publication', 'rate', 'created_at')
# Searching by JSON fields via string is limited in Django,
list_filter = ('year_of_publication', 'rate', 'created_at', 'type')
# Searching by JSON fields via string is limited in Django,
# so we focus on standard fields
search_fields = ('isbn', 'slug', 'volume')
readonly_fields = ('created_at', 'updated_at')
# Add the inlines to manage images and attributes on the same page
inlines = [BookReferenceImageInline, BookAttributeInline]
# Use filter_horizontal for ManyToMany fields to make selection easier
filter_horizontal = ('subject_area',)
fieldsets = (
(_('Basic Info'), {
'fields': ('title', 'description', 'slug', 'language')
'fields': ('title', 'description', 'slug', 'language', 'subject_area')
}),
(_('Publication Info'), {
'fields': ('publisher', 'isbn', 'year_of_publication', 'number_page', 'volume')
'fields': ('publisher', 'isbn', 'year_of_publication', 'number_page', 'volume', 'type')
}),
(_('Rating & Stats'), {
'fields': ('rate', 'created_at', 'updated_at')
@ -311,14 +316,34 @@ class BookReferenceAdmin(ModelAdmin):
class BookAuthorAdmin(ModelAdmin):
"""Admin for BookAuthor model"""
list_display = ('get_name_display', 'created_at', 'updated_at')
list_display = ('get_name_display', 'birth_year_hijri', 'death_year_hijri', 'birth_year_miladi', 'death_year_miladi', 'created_at', 'updated_at')
search_fields = ('name',) # Note: Search works best on exact text matches
readonly_fields = ('created_at', 'updated_at')
list_filter = ('birth_year_hijri', 'death_year_hijri', 'birth_year_miladi', 'death_year_miladi', 'created_at')
# Use filter_horizontal for ManyToMany fields to make selection easier
filter_horizontal = ('book_references',)
fieldsets = (
(_('Basic Info'), {
'fields': ('name',)
}),
(_('Dates (Hijri)'), {
'fields': ('birth_year_hijri', 'death_year_hijri')
}),
(_('Dates (Miladi)'), {
'fields': ('birth_year_miladi', 'death_year_miladi')
}),
(_('References'), {
'fields': ('book_references',)
}),
(_('Timestamps'), {
'fields': ('created_at', 'updated_at'),
'classes': ('collapse',)
}),
)
@display(description=_('Name'), ordering='name')
def get_name_display(self, obj):
if obj.name and isinstance(obj.name, list) and len(obj.name) > 0:
@ -379,6 +404,46 @@ class BookAttributeAdmin(ModelAdmin):
return '-'
class BookSubjectAreaAdmin(ModelAdmin):
"""Admin for BookSubjectArea model"""
list_display = ('get_title_display', 'created_at', 'updated_at')
search_fields = ('title',)
readonly_fields = ('created_at', 'updated_at')
@display(description=_('Title'), ordering='title')
def get_title_display(self, obj):
return self._extract_first_text(obj.title)
def _extract_first_text(self, json_data):
"""Helper to safely extract the first 'text' from a JSON list"""
if json_data and isinstance(json_data, list) and len(json_data) > 0:
first_item = json_data[0]
if isinstance(first_item, dict):
return first_item.get('text', '-')
return '-'
class BookTypeAdmin(ModelAdmin):
"""Admin for BookType model"""
list_display = ('get_title_display', 'created_at', 'updated_at')
search_fields = ('title',)
readonly_fields = ('created_at', 'updated_at')
@display(description=_('Title'), ordering='title')
def get_title_display(self, obj):
return self._extract_first_text(obj.title)
def _extract_first_text(self, json_data):
"""Helper to safely extract the first 'text' from a JSON list"""
if json_data and isinstance(json_data, list) and len(json_data) > 0:
first_item = json_data[0]
if isinstance(first_item, dict):
return first_item.get('text', '-')
return '-'
# -----------------------------------------------------------------------------
# 3. Registration
# -----------------------------------------------------------------------------
@ -386,4 +451,6 @@ class BookAttributeAdmin(ModelAdmin):
dovoodi_admin_site.register(BookReference, BookReferenceAdmin)
dovoodi_admin_site.register(BookAuthor, BookAuthorAdmin)
dovoodi_admin_site.register(BookAttribute, BookAttributeAdmin)
dovoodi_admin_site.register(BookReferenceImage, BookReferenceImageAdmin)
dovoodi_admin_site.register(BookReferenceImage, BookReferenceImageAdmin)
dovoodi_admin_site.register(BookSubjectArea, BookSubjectAreaAdmin)
dovoodi_admin_site.register(BookType, BookTypeAdmin)

85
apps/hadis/migrations/0003_booksubjectarea_booktype_hadisstatus_description_and_more.py

@ -0,0 +1,85 @@
# Generated by Django 4.2.27 on 2026-01-22 11:07
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
("hadis", "0002_bookauthor_birth_year_hijri_and_more"),
]
operations = [
migrations.CreateModel(
name="BookSubjectArea",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("title", models.JSONField(default=list, verbose_name="Title")),
(
"created_at",
models.DateTimeField(auto_now_add=True, verbose_name="created at"),
),
(
"updated_at",
models.DateTimeField(auto_now=True, verbose_name="updated at"),
),
],
),
migrations.CreateModel(
name="BookType",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("title", models.JSONField(default=list, verbose_name="Title")),
(
"created_at",
models.DateTimeField(auto_now_add=True, verbose_name="created at"),
),
(
"updated_at",
models.DateTimeField(auto_now=True, verbose_name="updated at"),
),
],
),
migrations.AddField(
model_name="hadisstatus",
name="description",
field=models.JSONField(default=list, verbose_name="Description"),
),
migrations.AddField(
model_name="bookreference",
name="subject_area",
field=models.ManyToManyField(
blank=True,
related_name="book_subjects",
to="hadis.booksubjectarea",
verbose_name="subject area",
),
),
migrations.AddField(
model_name="bookreference",
name="type",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to="hadis.booktype",
verbose_name="type",
),
),
]

1
apps/hadis/models/hadis.py

@ -137,6 +137,7 @@ class HadisStatus(models.Model):
slug= models.SlugField(max_length=255, verbose_name=_('slug'), blank=True,unique = True)
color = models.CharField(max_length=20, choices=ColorChoices.choices, verbose_name=_('color'))
order = models.IntegerField(default=0, verbose_name=_('order'))
description = models.JSONField(default = list , verbose_name=_('Description'))
def save(self, *args, **kwargs):
if not self.slug:

52
apps/hadis/models/reference.py

@ -3,6 +3,56 @@ from django.utils.translation import gettext_lazy as _
from django.utils.text import slugify
from typing import Optional
class BookSubjectArea(models.Model):
title = models.JSONField(default = list , verbose_name=_('Title'))
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_('created at'))
updated_at = models.DateTimeField(auto_now=True, verbose_name=_('updated at'))
def __str__(self):
return f"{self.title[0]['text']}"
def get_title(self,lang):
"""
Get title for a specific language
"""
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', '')
for tr in self.title:
if isinstance(tr, dict) and tr.get('language_code') == 'en':
return tr.get('text', '')
return None
class BookType(models.Model):
title = models.JSONField(default = list , verbose_name=_('Title'))
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_('created at'))
updated_at = models.DateTimeField(auto_now=True, verbose_name=_('updated at'))
def __str__(self):
return f"{self.title[0]['text']}"
def get_title(self,lang):
"""
Get title for a specific language
"""
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', '')
for tr in self.title:
if isinstance(tr, dict) and tr.get('language_code') == 'en':
return tr.get('text', '')
return None
class BookReference(models.Model):
"""
@ -18,6 +68,8 @@ class BookReference(models.Model):
number_page = models.PositiveIntegerField(verbose_name=_('number of pages'), blank=True, null=True)
slug = models.SlugField(max_length=255, verbose_name=_('slug'), blank=True,unique=True)
publisher = models.JSONField(default = list , verbose_name=_('Publisher'))
subject_area = models.ManyToManyField(BookSubjectArea, related_name="book_subjects", verbose_name=_('subject area'), blank=True)
type = models.ForeignKey(BookType, on_delete=models.SET_NULL, null=True, blank=True, verbose_name=_('type'))
rate = models.DecimalField(
max_digits=3,
decimal_places=2,

Loading…
Cancel
Save