You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

838 lines
30 KiB

from django.db import models
from django.utils.translation import gettext_lazy as _
from django.utils.text import slugify
from django.conf import settings
from typing import Optional
from utils.mixins import ColorPaletteMixin, LowercaseSlugMixin
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'))
class Meta:
ordering = ('-created_at', '-id')
verbose_name = _('Subject Area')
verbose_name_plural = _('Subject Areas')
def __str__(self):
if self.title and isinstance(self.title, list) and len(self.title) > 0 and isinstance(self.title[0], dict):
return self.title[0].get('text') or self.title[0].get('title') or str(self.id)
return str(self.id)
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', '') or tr.get('title', '')
for tr in self.title:
if isinstance(tr, dict) and tr.get('language_code') == 'en':
return tr.get('text', '') or tr.get('title', '')
return None
class BookTag(models.Model):
"""
Model for book reference tags
"""
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'))
class Meta:
verbose_name = _('Book Tag')
verbose_name_plural = _('Book Tags')
def __str__(self):
if self.title and isinstance(self.title, list) and len(self.title) > 0 and isinstance(self.title[0], dict):
return self.title[0].get('text') or self.title[0].get('title') or f"Tag {self.id}"
return f"Tag {self.id}"
class BookType(models.Model):
class Sect(models.TextChoices):
SHIA = 'shia', _('Shia')
SUNNI = 'sunni', _('Sunni')
title = models.JSONField(default = list , verbose_name=_('Title'))
sect = models.CharField(
max_length=10,
choices=Sect.choices,
default=Sect.SHIA,
verbose_name=_('Sect')
)
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):
if self.title and isinstance(self.title, list) and len(self.title) > 0 and isinstance(self.title[0], dict):
return self.title[0].get('text') or self.title[0].get('title') or str(self.id)
return str(self.id)
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', '') or tr.get('title', '')
for tr in self.title:
if isinstance(tr, dict) and tr.get('language_code') == 'en':
return tr.get('text', '') or tr.get('title', '')
return None
class BookReference(LowercaseSlugMixin, models.Model):
slug_source_field = 'title'
"""
Model for hadis book references with detailed information
This is different from library books - these are reference books for hadis
"""
legacy_id = models.CharField(max_length=255, unique=True, null=True, blank=True, db_index=True)
title = models.JSONField(default = list , verbose_name=_('Title'))
description = models.JSONField(default = list , verbose_name=_('Description'))
language = models.JSONField(default = list , verbose_name=_('Language'))
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, allow_unicode=True)
subject_area = models.ManyToManyField(
'BookSubjectArea',
verbose_name=_('Subject Area'),
blank=True
)
tag = models.ForeignKey(
'BookTag',
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='book_references',
verbose_name=_('Tag')
)
type = models.ForeignKey('BookType', on_delete=models.SET_NULL, null=True, blank=True, verbose_name=_('Type'))
author = models.ForeignKey(
'BookAuthor',
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='book_references',
verbose_name=_('Author')
)
order = models.IntegerField(default=0, verbose_name=_('Order'))
rate = models.DecimalField(
max_digits=3,
decimal_places=2,
verbose_name=_('rate'),
blank=True,
null=True,
help_text=_('Rating from 0 to 5')
)
has_editions = models.BooleanField(
default=False,
verbose_name=_('Has Editions Structure'),
help_text=_('True if this reference has one or more editions, False for direct volumes without edition structure')
)
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_('created at'))
updated_at = models.DateTimeField(auto_now=True, verbose_name=_('updated at'))
normalized_text = models.TextField(blank=True, null=True, verbose_name=_('normalized text for search'))
class Meta:
indexes = [
models.Index(fields=['id']), # Already indexed (PK)
]
verbose_name = _('Book Reference')
verbose_name_plural = _('Book References')
ordering = ('-created_at',)
def __str__(self):
if self.title and isinstance(self.title, list) and len(self.title) > 0 and isinstance(self.title[0], dict):
return self.title[0].get('text') or self.title[0].get('title') or f"Reference {self.id}"
return f"Reference {self.id}"
def save(self, *args, **kwargs):
from utils.text_normalizer import build_search_blob
author_name = self.author.name if self.author else ""
self.normalized_text = build_search_blob(
self.title,
self.description,
author_name
)
super().save(*args, **kwargs)
def _get_json_field(self, field_name: str, lang: Optional[str]=None , fallback: str = "en"):
"""
Generic getter for JSONField in our [{text, language_code}] format.
Usage: self._get_json_field('title', 'fa')
"""
if lang is None:
lang = fallback
value = getattr(self, field_name, None)
if not value or not isinstance(value, list):
return None
# 1) exact language
for item in value:
if isinstance(item, dict) and item.get("language_code") == lang:
return item.get("text", "")
# 2) fallback language
if fallback and fallback != lang:
for item in value:
if isinstance(item, dict) and item.get("language_code") == fallback:
return item.get("text", "")
# 3) first available
item = value[0]
print(item)
return item.get("text", "") if isinstance(item, dict) else None
@property
def share_link(self):
if self.slug:
return f"{settings.DOVODI_DOMAIN}/arguments/sources/{self.slug}"
return None
@property
def authors(self):
"""
Backward compatibility helper returning a queryset/list of authors.
Allows serializers calling obj.authors.all() to function correctly without hitting DB if author is already cached.
"""
if hasattr(self, '_state') and hasattr(self._state, 'fields_cache') and 'author' in self._state.fields_cache:
author_obj = self._state.fields_cache['author']
class InMemAuthorList(list):
def all(self):
return self
def first(self):
return self[0] if self else None
def exists(self):
return bool(self)
def count(self):
return len(self)
def filter(self, *args, **kwargs):
return self
return InMemAuthorList([author_obj] if author_obj else [])
if self.author_id:
return BookAuthor.objects.filter(id=self.author_id)
return BookAuthor.objects.none()
def get_title(self,lang):
return self._get_json_field("title" , lang)
def get_description(self, lang):
return self._get_json_field("description" , lang)
def get_language(self, lang):
return self._get_json_field("language" , lang)
class BookEdition(models.Model):
"""
Model representing a specific edition or print of a book reference.
"""
book_reference = models.ForeignKey(BookReference, on_delete=models.CASCADE, related_name='editions', verbose_name=_('book reference'))
publisher = models.JSONField(default=list, blank=True, verbose_name=_('Publisher'))
city_of_publication = models.JSONField(default=list, blank=True, verbose_name=_('City of Publication'))
city_of_publication_city = models.ForeignKey(
'hadis.City',
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='publication_editions',
verbose_name=_('City of Publication (City)'),
)
country_of_publication = models.JSONField(default=list, blank=True, verbose_name=_('Country of Publication'))
edition_number = models.CharField(max_length=100, blank=True, null=True, verbose_name=_('Edition Number'))
year_of_publication = models.CharField(max_length=50, blank=True, null=True, verbose_name=_('year of publication'))
number_of_volumes = models.PositiveIntegerField(null=True, blank=True, verbose_name=_('Total Number of Volumes'))
source_url = models.URLField(max_length=1000, null=True, blank=True, verbose_name=_('Online Source URL'))
isbn = models.CharField(max_length=100, verbose_name=_('ISBN'), blank=True, null=True)
notes = models.JSONField(default=list, blank=True, verbose_name=_('Notes'))
tag = models.ForeignKey(
'BookTag',
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='book_editions',
verbose_name=_('Tag')
)
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 = _('Book Edition')
verbose_name_plural = _('Book Editions')
ordering = ['-created_at']
def __str__(self):
return f"{self.book_reference} - {self.edition_number or 'Edition ' + str(self.id)}"
def save(self, *args, **kwargs):
if self.city_of_publication_city_id and self.city_of_publication_city:
self.city_of_publication = self.city_of_publication_city.title or []
super().save(*args, **kwargs)
class BookVolume(models.Model):
"""
Model representing a single volume, which can belong to an edition or directly to the book.
"""
book_reference = models.ForeignKey(BookReference, on_delete=models.CASCADE, related_name='volumes', verbose_name=_('book reference'))
edition = models.ForeignKey(BookEdition, on_delete=models.CASCADE, related_name='volumes', verbose_name=_('edition'), null=True, blank=True)
title = models.CharField(max_length=255, verbose_name=_('Title'), help_text=_('e.g., Volume 1'), blank=True, null=True)
image = models.ImageField(
upload_to='hadis/volume_images/',
null=True,
blank=True,
verbose_name=_('Image')
)
file = models.FileField(
upload_to='hadis/volume_files/',
null=True,
blank=True,
verbose_name=_('File')
)
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 = _('Book Volume')
verbose_name_plural = _('Book Volumes')
ordering = ['title']
def __str__(self):
return f"{self.book_reference} - {self.title or 'Vol ' + str(self.id)}"
class BookReferenceImage(models.Model):
"""
Model for book reference images - multiple images per book reference
"""
book_reference = models.ForeignKey(
BookReference,
on_delete=models.CASCADE,
related_name='images',
verbose_name=_('book reference')
)
book_volume = models.ForeignKey(
BookVolume,
on_delete=models.CASCADE,
related_name='images',
verbose_name=_('book volume'),
null=True,
blank=True
)
image = models.ImageField(upload_to='hadis/book_reference_images/', verbose_name=_('image'))
order = models.PositiveIntegerField(default=0, verbose_name=_('order'))
description = models.JSONField(default = list , verbose_name=_('Description'))
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_('created at'))
class Meta:
verbose_name = _('Book Reference Image')
verbose_name_plural = _('Book Reference Images')
ordering = ['order', '-created_at']
def save(self, *args, **kwargs):
from utils.watermark import apply_watermark_to_field_file
apply_watermark_to_field_file(self.image, position="bottom_left")
super().save(*args, **kwargs)
def get_description(self,lang):
"""
Get title for a specific language
"""
if not self.description or not isinstance(self.description, list):
return None
for tr in self.description:
if isinstance(tr, dict) and tr.get('language_code') == lang:
return tr.get('text', '')
for tr in self.description:
if isinstance(tr, dict) and tr.get('language_code') == 'en':
return tr.get('text', '')
return None
def __str__(self):
return f"{self.book_reference} - Image {self.order}"
class BookReferenceDocument(models.Model):
"""
Model for handling multiple PDF files per book (e.g., different volumes).
"""
book_reference = models.ForeignKey(
BookReference,
on_delete=models.CASCADE,
related_name='documents',
verbose_name=_('book reference')
)
book_volume = models.ForeignKey(
BookVolume,
on_delete=models.CASCADE,
related_name='documents',
verbose_name=_('book volume'),
null=True,
blank=True
)
file = models.FileField(upload_to='hadis/book_reference_documents/', verbose_name=_('Document File'))
title = models.CharField(max_length=255, blank=True, null=True, help_text=_('e.g., Volume 1, 01.pdf'))
order = models.PositiveIntegerField(default=0, verbose_name=_('order'))
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
verbose_name = _('Book Reference Document')
verbose_name_plural = _('Book Reference Documents')
ordering = ['order', '-created_at']
def __str__(self):
return f"{self.book_reference} - {self.title or 'Doc ' + str(self.id)}"
class BookAuthorRutba(ColorPaletteMixin, LowercaseSlugMixin, models.Model):
slug_source_field = 'title'
title = models.JSONField(default=list, verbose_name=_('Title'))
slug = models.SlugField(max_length=255, verbose_name=_('slug'), blank=True, null=True, allow_unicode=True)
description = models.JSONField(default=list, verbose_name=_('Description'))
def __str__(self):
if self.title and isinstance(self.title, list) and len(self.title) > 0 and isinstance(self.title[0], dict):
return self.title[0].get('text') or self.title[0].get('title') or str(self.id)
return str(self.id)
def get_title(self, lang):
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', '') or tr.get('title', '')
for tr in self.title:
if isinstance(tr, dict) and tr.get('language_code') == 'en':
return tr.get('text', '') or tr.get('title', '')
return None
def get_description(self, lang):
if not self.description or not isinstance(self.description, list):
return None
for tr in self.description:
if isinstance(tr, dict) and tr.get('language_code') == lang:
return tr.get('text', '') or tr.get('title', '')
for tr in self.description:
if isinstance(tr, dict) and tr.get('language_code') == 'en':
return tr.get('text', '') or tr.get('title', '')
return None
class Meta:
verbose_name = _('Author Rutba')
verbose_name_plural = _('Author Rutbas')
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
self.authors.all().update(rutba=self.title or [])
class BookAuthor(LowercaseSlugMixin, models.Model):
slug_source_field = 'name'
"""
Model for book reference authors
"""
name = models.JSONField(default=list, verbose_name=_('Name'))
slug = models.SlugField(max_length=255, verbose_name=_('slug'), null=True, blank=True, unique=True, allow_unicode=True)
related_narrator = models.OneToOneField(
'hadis.Transmitters',
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='book_author_profile',
verbose_name=_('Related Narrator Profile')
)
# 👇 فیلدهای جدید اضافه شده
known_as = models.JSONField(default=list, blank=True, verbose_name=_('Known as'))
scholarity = models.JSONField(default=list, blank=True, verbose_name=_('Scholarity'))
tabaqa = models.JSONField(default=list, blank=True, verbose_name=_('Tabaqa'))
rutba = models.JSONField(default=list, blank=True, verbose_name=_('Rutba'))
rutba_status = models.ForeignKey(
'BookAuthorRutba',
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='authors',
verbose_name=_('Rutba Status')
)
features = models.JSONField(default=list, blank=True, verbose_name=_('Features'))
# Geographic Information
origin = models.JSONField(default=list, blank=True, verbose_name=_('Origin'))
lived_in = models.JSONField(default=list, blank=True, verbose_name=_('Lived in'))
died_in = models.JSONField(default=list, blank=True, verbose_name=_('Died in'))
origin_city = models.ForeignKey(
'hadis.City',
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='origin_authors',
verbose_name=_('Origin City'),
)
lived_in_city = models.ForeignKey(
'hadis.City',
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='lived_in_authors',
verbose_name=_('Lived in City'),
)
died_in_city = models.ForeignKey(
'hadis.City',
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='died_in_authors',
verbose_name=_('Died in City'),
)
birth_year_hijri = models.CharField(max_length=100, verbose_name=_("Birth Year (Hijri)"), null=True, blank=True)
death_year_hijri = models.CharField(max_length=100, verbose_name=_("Death Year (Hijri)"), null=True, blank=True)
age_at_death = models.CharField(max_length=100, verbose_name=_("Age at Death"), null=True, blank=True)
birth_year_miladi = models.IntegerField(verbose_name=_("Birth Year (Miladi)"), null=True, blank=True)
death_year_miladi = models.IntegerField(verbose_name=_("Death Year (Miladi)"), null=True, blank=True)
image = models.ImageField(
upload_to='hadis/author_images/',
null=True,
blank=True,
help_text=_('image allowed'),
verbose_name=_('Image')
)
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 = _('Book Author')
verbose_name_plural = _('Book Authors')
ordering = ['name']
def __str__(self):
return self.name[0]['text'] if self.name else "Unknown Author"
def get_name(self,lang):
"""
Get title for a specific language
"""
if not self.name or not isinstance(self.name, list):
return None
for tr in self.name:
if isinstance(tr, dict) and tr.get('language_code') == lang:
return tr.get('text', '')
for tr in self.name:
if isinstance(tr, dict) and tr.get('language_code') == 'en':
return tr.get('text', '')
return None
def save(self, *args, **kwargs):
if self.rutba_status_id and self.rutba_status:
self.rutba = self.rutba_status.title or []
if self.origin_city_id and self.origin_city:
self.origin = self.origin_city.title or []
if self.lived_in_city_id and self.lived_in_city:
self.lived_in = self.lived_in_city.title or []
if self.died_in_city_id and self.died_in_city:
self.died_in = self.died_in_city.title or []
for date_field in ('birth_year_hijri', 'death_year_hijri', 'age_at_death'):
val = getattr(self, date_field, None)
if val is not None:
val_str = str(val).strip()
setattr(self, date_field, val_str if val_str else None)
super().save(*args, **kwargs)
self.researcher_profiles.all().update(name=self.name, slug=self.slug)
try:
self.sync_author_dates()
except Exception:
pass
def sync_author_dates(self):
"""
Synchronizes birth_year_hijri, death_year_hijri, and age_at_death
with YearValue and BookAuthorDate tables.
"""
if not self.pk:
return
from .transmitter import YearValue
date_mapping = {
'birth': self.birth_year_hijri,
'death': self.death_year_hijri,
'age': self.age_at_death,
}
for date_type, raw_val in date_mapping.items():
if not raw_val or not str(raw_val).strip():
BookAuthorDate.objects.filter(author=self, date_type=date_type).delete()
continue
raw_str = str(raw_val).strip()
yv = YearValue.get_or_create_from_canonical(category=date_type, raw_value=raw_str)
BookAuthorDate.objects.update_or_create(
author=self,
date_type=date_type,
defaults={
'year_value': yv,
'raw_value': yv.raw_value,
}
)
def get_formatted_date(self, date_type: str, lang: str = 'ru') -> Optional[str]:
field_map = {
'birth': 'birth_year_hijri',
'death': 'death_year_hijri',
'age': 'age_at_death',
}
field_name = field_map.get(date_type)
if not field_name:
return None
raw_val = getattr(self, field_name, None)
if raw_val is None or str(raw_val).strip() == '':
return None
raw_str = str(raw_val).strip()
target_lang = (lang or 'ru').split('-')[0].lower()
# Check prefetched author_dates first if available
if hasattr(self, '_prefetched_objects_cache') and 'author_dates' in self._prefetched_objects_cache:
for ad in self._prefetched_objects_cache['author_dates']:
if ad.date_type == date_type and ad.year_value_id and ad.year_value:
txt = ad.year_value.get_translation(target_lang)
if txt:
return txt
# If context tag (#...), check YearValue in DB for custom translations
if raw_str.startswith('#'):
from .transmitter import YearValue
yv = YearValue.objects.filter(raw_value=raw_str).first()
if yv:
txt = yv.get_translation(target_lang)
if txt:
return txt
from ..utils.date_engine import format_canonical_date
engine_lang = target_lang if target_lang in ('ru', 'en', 'ar') else 'en'
return format_canonical_date(raw_str, category=date_type, lang=engine_lang)
def get_formatted_birth_year(self, lang: str = 'ru') -> Optional[str]:
return self.get_formatted_date('birth', lang=lang)
def get_formatted_death_year(self, lang: str = 'ru') -> Optional[str]:
return self.get_formatted_date('death', lang=lang)
def get_formatted_age(self, lang: str = 'ru') -> Optional[str]:
return self.get_formatted_date('age', lang=lang)
class BookAuthorDate(models.Model):
"""
Links a BookAuthor to a parsed YearValue for birth, death, or age.
"""
class DateTypeChoices(models.TextChoices):
BIRTH = 'birth', _('Birth')
DEATH = 'death', _('Death')
AGE = 'age', _('Age')
author = models.ForeignKey(
BookAuthor,
on_delete=models.CASCADE,
related_name='author_dates',
verbose_name=_('Book Author')
)
date_type = models.CharField(
max_length=20,
choices=DateTypeChoices.choices,
db_index=True,
verbose_name=_('Date Type')
)
year_value = models.ForeignKey(
'hadis.YearValue',
on_delete=models.CASCADE,
related_name='author_dates',
verbose_name=_('Year Value')
)
raw_value = models.CharField(
max_length=100,
verbose_name=_('Canonical Raw Value')
)
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 = _('Book Author Date')
verbose_name_plural = _('Book Author Dates')
unique_together = ('author', 'date_type')
indexes = [
models.Index(fields=['date_type', 'year_value']),
models.Index(fields=['author', 'date_type']),
]
ordering = ('author_id', 'date_type')
def __str__(self):
return f"{self.author_id} - {self.date_type}: {self.raw_value}"
class BookAttribute(models.Model):
"""
Model for book reference attributes - custom key-value pairs
"""
title = models.JSONField(default = list , verbose_name=_('Title'))
value = models.JSONField(default = list , verbose_name=_('Value'))
book_reference = models.ForeignKey(
BookReference,
on_delete=models.CASCADE,
related_name='attributes',
verbose_name=_('book attribute'),
default = None,
null = True
)
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 = _('Book Attribute')
verbose_name_plural = _('Book Attributes')
ordering = ['title']
def __str__(self):
title = self.title[0]['text'] if self.title else "No Title"
value = self.value[0]['text'] if self.value else "No Value"
return f"{title}: {value}"
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
def get_value(self,lang):
"""
Get title for a specific language
"""
if not self.value or not isinstance(self.value, list):
return None
for tr in self.value:
if isinstance(tr, dict) and tr.get('language_code') == lang:
return tr.get('text', '')
for tr in self.value:
if isinstance(tr, dict) and tr.get('language_code') == 'en':
return tr.get('text', '')
return None
class BookResearcher(LowercaseSlugMixin, models.Model):
slug_source_field = 'name'
book_edition = models.ForeignKey(
'BookEdition',
on_delete=models.CASCADE,
related_name='researchers',
verbose_name=_('Book Edition')
)
author = models.ForeignKey(
'BookAuthor',
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='researcher_profiles',
verbose_name=_('Author')
)
name = models.JSONField(default=list, blank=True, verbose_name=_('Name'))
slug = models.SlugField(max_length=255, verbose_name=_('slug'), null=True, blank=True, allow_unicode=True)
class Meta:
verbose_name = _('Book Researcher')
verbose_name_plural = _('Book Researchers')
def save(self, *args, **kwargs):
if self.author:
self.name = self.author.name
self.slug = self.author.slug
super().save(*args, **kwargs)
def __str__(self):
try:
return self.name[0]['text']
except:
return self.slug or f"Researcher {self.id}"
class BookEditor(LowercaseSlugMixin, models.Model):
slug_source_field = 'name'
book_edition = models.ForeignKey(
'BookEdition',
on_delete=models.CASCADE,
related_name='editors',
verbose_name=_('Book Edition')
)
author = models.ForeignKey(
'BookAuthor',
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='editor_profiles',
verbose_name=_('Author')
)
name = models.JSONField(default=list, blank=True, verbose_name=_('Name'))
slug = models.SlugField(max_length=255, verbose_name=_('slug'), null=True, blank=True, allow_unicode=True)
class Meta:
verbose_name = _('Book Editor')
verbose_name_plural = _('Book Editors')
def save(self, *args, **kwargs):
if self.author:
self.name = self.author.name
self.slug = self.author.slug
super().save(*args, **kwargs)
def __str__(self):
try:
return self.name[0]['text']
except:
return self.slug or f"Editor {self.id}"