Browse Source

feat(references): add book priority, edition title, and many-to-many works linking

master
Mohsen Taba 1 week ago
parent
commit
8e0f566ae9
  1. 42
      apps/hadis/migrations/0066_alter_bookreference_options_bookedition_books_and_more.py
  2. 19
      apps/hadis/migrations/0067_populate_book_edition_books.py
  3. 28
      apps/hadis/models/reference.py
  4. 30
      apps/hadis/serializers/serializers_admin.py
  5. 17
      apps/hadis/views_admin.py

42
apps/hadis/migrations/0066_alter_bookreference_options_bookedition_books_and_more.py

@ -0,0 +1,42 @@
# Generated by Django 4.2.30 on 2026-10-21 12:54
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('hadis', '0065_country_and_publisher_models'),
]
operations = [
migrations.AlterModelOptions(
name='bookreference',
options={'ordering': ('priority', 'id'), 'verbose_name': 'Book Reference', 'verbose_name_plural': 'Book References'},
),
migrations.AddField(
model_name='bookedition',
name='books',
field=models.ManyToManyField(blank=True, related_name='editions_list', to='hadis.bookreference', verbose_name='Connected Books/Works'),
),
migrations.AddField(
model_name='bookedition',
name='title',
field=models.JSONField(blank=True, default=list, verbose_name='Edition Title'),
),
migrations.AddField(
model_name='bookreference',
name='priority',
field=models.PositiveIntegerField(db_index=True, default=0, help_text='Display order priority. Lower numbers appear first.', verbose_name='Priority'),
),
migrations.AlterField(
model_name='bookedition',
name='book_reference',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='editions', to='hadis.bookreference', verbose_name='book reference'),
),
migrations.AddIndex(
model_name='bookreference',
index=models.Index(fields=['priority'], name='hadis_bookr_priorit_74ae96_idx'),
),
]

19
apps/hadis/migrations/0067_populate_book_edition_books.py

@ -0,0 +1,19 @@
from django.db import migrations
def populate_books(apps, schema_editor):
BookEdition = apps.get_model('hadis', 'BookEdition')
for edition in BookEdition.objects.filter(book_reference__isnull=False):
edition.books.add(edition.book_reference)
def reverse_populate(apps, schema_editor):
pass
class Migration(migrations.Migration):
dependencies = [
('hadis', '0066_alter_bookreference_options_bookedition_books_and_more'),
]
operations = [
migrations.RunPython(populate_books, reverse_populate),
]

28
apps/hadis/models/reference.py

@ -127,6 +127,12 @@ class BookReference(LowercaseSlugMixin, models.Model):
verbose_name=_('Author') verbose_name=_('Author')
) )
order = models.IntegerField(default=0, verbose_name=_('Order')) order = models.IntegerField(default=0, verbose_name=_('Order'))
priority = models.PositiveIntegerField(
default=0,
db_index=True,
verbose_name=_('Priority'),
help_text=_('Display order priority. Lower numbers appear first.')
)
rate = models.DecimalField( rate = models.DecimalField(
max_digits=3, max_digits=3,
decimal_places=2, decimal_places=2,
@ -148,10 +154,11 @@ class BookReference(LowercaseSlugMixin, models.Model):
class Meta: class Meta:
indexes = [ indexes = [
models.Index(fields=['id']), # Already indexed (PK) models.Index(fields=['id']), # Already indexed (PK)
models.Index(fields=['priority']),
] ]
verbose_name = _('Book Reference') verbose_name = _('Book Reference')
verbose_name_plural = _('Book References') verbose_name_plural = _('Book References')
ordering = ('-created_at',)
ordering = ('priority', 'id')
def __str__(self): def __str__(self):
if self.title and isinstance(self.title, list) and len(self.title) > 0 and isinstance(self.title[0], dict): if self.title and isinstance(self.title, list) and len(self.title) > 0 and isinstance(self.title[0], dict):
@ -292,7 +299,14 @@ class BookEdition(models.Model):
""" """
Model representing a specific edition or print of a book reference. 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'))
title = models.JSONField(default=list, blank=True, verbose_name=_('Edition Title'))
book_reference = models.ForeignKey(BookReference, on_delete=models.CASCADE, related_name='editions', verbose_name=_('book reference'), null=True, blank=True)
books = models.ManyToManyField(
BookReference,
related_name='editions_list',
blank=True,
verbose_name=_('Connected Books/Works')
)
publisher = models.JSONField(default=list, blank=True, verbose_name=_('Publisher')) publisher = models.JSONField(default=list, blank=True, verbose_name=_('Publisher'))
publisher_item = models.ForeignKey( publisher_item = models.ForeignKey(
'hadis.BookPublisher', 'hadis.BookPublisher',
@ -344,12 +358,22 @@ class BookEdition(models.Model):
ordering = ['-created_at'] ordering = ['-created_at']
def __str__(self): def __str__(self):
t_str = ""
if self.title and isinstance(self.title, list):
for item in self.title:
if isinstance(item, dict) and (item.get('text') or item.get('title')):
t_str = (item.get('text') or item.get('title')).strip()
break
if t_str:
return f"{t_str} ({self.edition_number or 'Ed. ' + str(self.id)})"
return f"{self.book_reference} - {self.edition_number or 'Edition ' + str(self.id)}" return f"{self.book_reference} - {self.edition_number or 'Edition ' + str(self.id)}"
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
if self.city_of_publication_city_id and self.city_of_publication_city: if self.city_of_publication_city_id and self.city_of_publication_city:
self.city_of_publication = self.city_of_publication_city.title or [] self.city_of_publication = self.city_of_publication_city.title or []
super().save(*args, **kwargs) super().save(*args, **kwargs)
if self.book_reference and not self.books.filter(id=self.book_reference_id).exists():
self.books.add(self.book_reference)
class BookVolume(models.Model): class BookVolume(models.Model):
""" """

30
apps/hadis/serializers/serializers_admin.py

@ -2902,6 +2902,7 @@ class AdminBookReferenceListSerializer(serializers.ModelSerializer):
"title", "title",
"slug", "slug",
"has_editions", "has_editions",
"priority",
"author_id", "author_id",
"author_name", "author_name",
"tag_id", "tag_id",
@ -3123,6 +3124,7 @@ class AdminBookReferenceDetailSerializer(serializers.ModelSerializer):
"tag", "tag",
"tag_detail", "tag_detail",
"order", "order",
"priority",
"city_of_publication", "city_of_publication",
"city_of_publication_city", "city_of_publication_city",
"city_of_publication_city_detail", "city_of_publication_city_detail",
@ -3718,6 +3720,12 @@ class AdminBookEditorSerializer(serializers.ModelSerializer):
class AdminBookEditionSerializer(serializers.ModelSerializer): class AdminBookEditionSerializer(serializers.ModelSerializer):
title = serializers.JSONField(required=False, default=list)
books = serializers.PrimaryKeyRelatedField(
queryset=BookReference.objects.all(),
many=True,
required=False,
)
publisher = serializers.JSONField(required=False, default=list) publisher = serializers.JSONField(required=False, default=list)
city_of_publication = serializers.JSONField(required=False, default=list) city_of_publication = serializers.JSONField(required=False, default=list)
publisher_item = serializers.PrimaryKeyRelatedField( publisher_item = serializers.PrimaryKeyRelatedField(
@ -3757,7 +3765,10 @@ class AdminBookEditionSerializer(serializers.ModelSerializer):
model = BookEdition model = BookEdition
fields = [ fields = [
"id", "id",
"title",
"book_reference", "book_reference",
"books",
"books_detail",
"publisher", "publisher",
"publisher_item", "publisher_item",
"publisher_item_detail", "publisher_item_detail",
@ -3791,6 +3802,15 @@ class AdminBookEditionSerializer(serializers.ModelSerializer):
ed_obj = instance.editors.first() ed_obj = instance.editors.first()
ret["editor_author_id"] = ed_obj.author_id if ed_obj and ed_obj.author_id else None ret["editor_author_id"] = ed_obj.author_id if ed_obj and ed_obj.author_id else None
ret["books"] = list(instance.books.values_list('id', flat=True))
ret["books_detail"] = [
{
"id": b.id,
"title": b.title,
"slug": b.slug,
}
for b in instance.books.all()
]
return ret return ret
def get_number_of_volumes(self, obj): def get_number_of_volumes(self, obj):
@ -3835,6 +3855,7 @@ class AdminBookEditionSerializer(serializers.ModelSerializer):
pass pass
def create(self, validated_data): def create(self, validated_data):
books_data = validated_data.pop("books", None)
researchers_data = validated_data.pop("researchers", []) researchers_data = validated_data.pop("researchers", [])
editors_data = validated_data.pop("editors", []) editors_data = validated_data.pop("editors", [])
has_res_id = "researcher_author_id" in validated_data has_res_id = "researcher_author_id" in validated_data
@ -3852,9 +3873,14 @@ class AdminBookEditionSerializer(serializers.ModelSerializer):
if has_ed_id: if has_ed_id:
author_links_dict["editor_author_id"] = ed_author_id author_links_dict["editor_author_id"] = ed_author_id
self._handle_author_links(edition, author_links_dict) self._handle_author_links(edition, author_links_dict)
if books_data is not None:
edition.books.set(books_data)
if edition.book_reference and not edition.books.filter(id=edition.book_reference_id).exists():
edition.books.add(edition.book_reference)
return edition return edition
def update(self, instance, validated_data): def update(self, instance, validated_data):
books_data = validated_data.pop("books", None)
researchers_data = validated_data.pop("researchers", None) researchers_data = validated_data.pop("researchers", None)
editors_data = validated_data.pop("editors", None) editors_data = validated_data.pop("editors", None)
has_res_id = "researcher_author_id" in validated_data has_res_id = "researcher_author_id" in validated_data
@ -3883,6 +3909,10 @@ class AdminBookEditionSerializer(serializers.ModelSerializer):
# Delete editors no longer in the request payload # Delete editors no longer in the request payload
keep_ids = [e.get("id") for e in editors_data if e.get("id")] keep_ids = [e.get("id") for e in editors_data if e.get("id")]
instance.editors.exclude(id__in=keep_ids).delete() instance.editors.exclude(id__in=keep_ids).delete()
if books_data is not None:
edition.books.set(books_data)
if edition.book_reference and not edition.books.filter(id=edition.book_reference_id).exists():
edition.books.add(edition.book_reference)
for ed_data in editors_data: for ed_data in editors_data:
ed_id = ed_data.get("id") ed_id = ed_data.get("id")
if ed_id: if ed_id:

17
apps/hadis/views_admin.py

@ -2262,7 +2262,10 @@ class AdminBookReferenceViewSet(ModelViewSet):
return queryset.distinct().order_by("-created_at")
ordering = self.request.query_params.get("ordering")
if ordering:
return queryset.distinct().order_by(ordering)
return queryset.distinct().order_by("priority", "-created_at")
@ -2568,13 +2571,21 @@ class AdminBookEditionViewSet(ModelViewSet):
def get_queryset(self): def get_queryset(self):
queryset = BookEdition.objects.all()
queryset = BookEdition.objects.all().prefetch_related("books", "volumes")
book_reference_id = self.request.query_params.get("book_reference_id") book_reference_id = self.request.query_params.get("book_reference_id")
if book_reference_id: if book_reference_id:
from django.db.models import Q
queryset = queryset.filter(Q(book_reference_id=book_reference_id) | Q(books__id=book_reference_id)).distinct()
queryset = queryset.filter(book_reference_id=book_reference_id)
search_query = self.request.query_params.get("search")
if search_query:
from django.db.models import Q
queryset = queryset.filter(
Q(edition_number__icontains=search_query) |
Q(isbn__icontains=search_query)
)
return queryset.order_by("-created_at") return queryset.order_by("-created_at")

Loading…
Cancel
Save