diff --git a/apps/article/admin.py b/apps/article/admin.py index 8b64dba..57899ce 100755 --- a/apps/article/admin.py +++ b/apps/article/admin.py @@ -81,7 +81,7 @@ class ArticleCollectionAdminBase(ModelAdmin): fieldsets = ( (None, { - 'fields': ('title', 'summary', 'thumbnail', 'status', 'pin_top', 'order') + 'fields': ('title', 'summary', 'thumbnail', 'status', 'order') }), ) @@ -152,7 +152,7 @@ class MiddleArticleCollectionAdmin(ArticleCollectionAdminBase): fieldsets = ( (None, { - 'fields': ('title', 'status', 'pin_top', 'order') + 'fields': ('title', 'status', 'order') }), ) diff --git a/apps/article/management/commands/seed_article_data.py b/apps/article/management/commands/seed_article_data.py index e0229bf..0c0d3a8 100644 --- a/apps/article/management/commands/seed_article_data.py +++ b/apps/article/management/commands/seed_article_data.py @@ -59,14 +59,12 @@ class Command(BaseCommand): { 'title': 'Избранные статьи об имамах', 'summary': 'Лучшие статьи о жизни и учениях имамов', - 'pin_top': True, 'display_position': 'pinned', 'order': 1 }, { 'title': 'Коллекция о пророках', 'summary': 'Статьи о пророках в исламе', - 'pin_top': False, 'display_position': 'middle', 'order': 2 }, @@ -79,7 +77,6 @@ class Command(BaseCommand): defaults={ 'slug': slugify(coll_data['title'], allow_unicode=True), 'summary': coll_data['summary'], - 'pin_top': coll_data['pin_top'], 'display_position': coll_data['display_position'], 'order': coll_data['order'], 'status': True diff --git a/apps/article/migrations/0003_remove_articlecollection_pin_top.py b/apps/article/migrations/0003_remove_articlecollection_pin_top.py new file mode 100644 index 0000000..8b3b4cd --- /dev/null +++ b/apps/article/migrations/0003_remove_articlecollection_pin_top.py @@ -0,0 +1,17 @@ +# Generated by Django 4.2.30 on 2026-07-20 15:49 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('article', '0002_article_embedded_in'), + ] + + operations = [ + migrations.RemoveField( + model_name='articlecollection', + name='pin_top', + ), + ] diff --git a/apps/article/models.py b/apps/article/models.py index be88858..3c5ea15 100755 --- a/apps/article/models.py +++ b/apps/article/models.py @@ -32,7 +32,6 @@ class ArticleCollection(LowercaseSlugMixin, models.Model): title = models.CharField(max_length=255, help_text="This title will not be displayed anywhere") slug = models.SlugField(max_length=255, unique=True) summary = models.CharField(max_length=512, null=True, blank=True, help_text=_('could be null')) - pin_top = models.BooleanField(_('pin top'), default=True) thumbnail = models.ImageField(upload_to='article/collection/', null=True, blank=True, help_text=_('image allowed')) order = models.IntegerField(default=0, verbose_name=_('order')) status = models.BooleanField(default=True, verbose_name=_('status')) diff --git a/apps/article/serializers.py b/apps/article/serializers.py index b5d7540..bc4bfa9 100644 --- a/apps/article/serializers.py +++ b/apps/article/serializers.py @@ -32,7 +32,7 @@ class MiddleArticleCollectionSerializer(serializers.ModelSerializer): class Meta: model = ArticleCollection - fields = ('id', 'title', 'slug', 'summary', 'status', 'order', 'pin_top', 'articles') + fields = ('id', 'title', 'slug', 'summary', 'status', 'order', 'articles') def get_articles(self, obj): articles = obj.articles.filter(status=True).order_by('-created_at') diff --git a/apps/article/serializers_admin.py b/apps/article/serializers_admin.py index 173cd5f..201807e 100644 --- a/apps/article/serializers_admin.py +++ b/apps/article/serializers_admin.py @@ -48,7 +48,7 @@ class AdminArticleCategorySerializer(serializers.ModelSerializer): class AdminArticleCollectionSerializer(serializers.ModelSerializer): class Meta: model = ArticleCollection - fields = ["id", "title", "slug", "summary", "display_position", "status", "order", "pin_top"] + fields = ["id", "title", "slug", "summary", "display_position", "status", "order"] class AdminArticleListSerializer(serializers.ModelSerializer): diff --git a/apps/article/serializers_dovodi.py b/apps/article/serializers_dovodi.py index d34765f..3f47582 100644 --- a/apps/article/serializers_dovodi.py +++ b/apps/article/serializers_dovodi.py @@ -27,7 +27,6 @@ class DovodiArticleCollectionSerializer(serializers.ModelSerializer): "display_position", "status", "order", - "pin_top", "thumbnail", "remove_thumbnail", ] diff --git a/apps/article/views.py b/apps/article/views.py index 2888d1d..472ceb0 100755 --- a/apps/article/views.py +++ b/apps/article/views.py @@ -65,7 +65,7 @@ class PinnedArticleCollectionListView(generics.ListAPIView): return PinnedArticleCollection.objects.filter( status=True, display_position=ArticleCollection.DisplayPosition.PINNED - ).order_by('-order', '-id') + ).order_by('order', '-created_at') def list(self, request, *args, **kwargs): response = super().list(request, *args, **kwargs) @@ -112,7 +112,7 @@ class MiddleArticleCollectionListView(generics.ListAPIView): return ArticleCollection.objects.filter( status=True, display_position=ArticleCollection.DisplayPosition.MIDDLE - ).order_by('order') + ).order_by('order', '-created_at') diff --git a/apps/hadis/admin/hadis.py b/apps/hadis/admin/hadis.py index 1d531da..ff44a7d 100644 --- a/apps/hadis/admin/hadis.py +++ b/apps/hadis/admin/hadis.py @@ -748,8 +748,8 @@ class HadisInCollectionInline(TabularInline): class HadisCollectionAdmin(ModelAdmin): """Admin for HadisCollection model""" form = HadisCollectionAdminForm - list_display = ('display_header','get_title_display', 'slug', 'status', 'order', 'created_at') - list_filter = ('status', 'created_at') + list_display = ('display_header','get_title_display', 'slug', 'display_position', 'status', 'order', 'created_at') + list_filter = ('status', 'display_position', 'created_at') search_fields = ('title', 'slug', 'summary') readonly_fields = ('slug', 'created_at', 'updated_at') ordering = ('order',) @@ -757,7 +757,7 @@ class HadisCollectionAdmin(ModelAdmin): fieldsets = ( (None, { - 'fields': ('title', 'slug', 'summary', 'status','pin_top', 'order','thumbnail') + 'fields': ('title', 'slug', 'summary', 'status', 'display_position', 'order', 'thumbnail') }), (_('Timestamps'), { 'fields': ('created_at', 'updated_at'), diff --git a/apps/hadis/migrations/0031_remove_hadiscollection_pin_top_and_more.py b/apps/hadis/migrations/0031_remove_hadiscollection_pin_top_and_more.py new file mode 100644 index 0000000..618b1af --- /dev/null +++ b/apps/hadis/migrations/0031_remove_hadiscollection_pin_top_and_more.py @@ -0,0 +1,22 @@ +# Generated by Django 4.2.30 on 2026-07-20 15:49 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('hadis', '0030_remove_bookauthor_book_references_and_more'), + ] + + operations = [ + migrations.RemoveField( + model_name='hadiscollection', + name='pin_top', + ), + migrations.AddField( + model_name='hadiscollection', + name='display_position', + field=models.CharField(choices=[('pinned', 'Pinned'), ('middle', 'Middle Section')], default='pinned', max_length=20, verbose_name='Display Position'), + ), + ] diff --git a/apps/hadis/models/hadis.py b/apps/hadis/models/hadis.py index 3b46960..3fac040 100644 --- a/apps/hadis/models/hadis.py +++ b/apps/hadis/models/hadis.py @@ -10,12 +10,21 @@ from utils.slug import generate_smart_slug from utils.mixins import ColorPaletteMixin, LowercaseSlugMixin class HadisCollection(LowercaseSlugMixin, models.Model): + class DisplayPosition(models.TextChoices): + PINNED = 'pinned', _('Pinned') + MIDDLE = 'middle', _('Middle Section') + slug_source_field = 'title' title = models.JSONField(default = list , verbose_name=_('Title')) slug = models.SlugField(max_length=255, unique=True, verbose_name=_('slug'), blank=True) summary = models.JSONField(default = list , verbose_name=_('Summary')) status = models.BooleanField(default=True, verbose_name=_('status')) - pin_top = models.BooleanField(default=False, verbose_name=_('pin top')) + display_position = models.CharField( + max_length=20, + choices=DisplayPosition.choices, + default=DisplayPosition.PINNED, + verbose_name=_('Display Position') + ) order = models.IntegerField(default=0, verbose_name=_('order')) thumbnail = models.ImageField(upload_to='hadis/collection_thumbnails/', null=True, blank=True, help_text=_('image allowed')) created_at = models.DateTimeField(auto_now_add=True, verbose_name=_('created at')) diff --git a/apps/hadis/serializers/serializers_admin.py b/apps/hadis/serializers/serializers_admin.py index 84db34e..7696bee 100644 --- a/apps/hadis/serializers/serializers_admin.py +++ b/apps/hadis/serializers/serializers_admin.py @@ -118,7 +118,7 @@ class AdminHadisCollectionSerializer(serializers.ModelSerializer): "slug", "summary", "status", - "pin_top", + "display_position", "order", "thumbnail", "remove_thumbnail", diff --git a/apps/hadis/views/hadis.py b/apps/hadis/views/hadis.py index 7a78dfa..64831d8 100644 --- a/apps/hadis/views/hadis.py +++ b/apps/hadis/views/hadis.py @@ -33,7 +33,7 @@ class PinnedHadisCollectionListView(ListAPIView): def get_queryset(self): return HadisCollection.objects.filter( status=True, - pin_top=True + display_position=HadisCollection.DisplayPosition.PINNED ).order_by('order', '-created_at') def list(self, request, *args, **kwargs): @@ -89,7 +89,6 @@ class HadisCollectionListView(ListAPIView): """ API view to list all hadis collections """ - queryset = HadisCollection.objects.filter(status=True).order_by('order') serializer_class = HadisCollectionListSerializer pagination_class = NoPagination @@ -97,6 +96,12 @@ class HadisCollectionListView(ListAPIView): def get(self, request, *args, **kwargs): return self.list(request, *args, **kwargs) + def get_queryset(self): + return HadisCollection.objects.filter( + status=True, + display_position=HadisCollection.DisplayPosition.MIDDLE + ).order_by('order', '-created_at') + class HadisSyncView(ListAPIView): """ diff --git a/apps/library/admin.py b/apps/library/admin.py index 2cceaf1..fa51813 100644 --- a/apps/library/admin.py +++ b/apps/library/admin.py @@ -142,7 +142,7 @@ class PinnedBookCollectionAdmin(BookCollectionAdminBase): fieldsets = ( (None, { - 'fields': ('title', 'summary', 'status', 'order', 'pin_top') + 'fields': ('title', 'summary', 'status', 'order') }), (_('Books'), { 'fields': ('books',) diff --git a/apps/library/management/commands/seed_category_collection.py b/apps/library/management/commands/seed_category_collection.py index c3b91a5..2325161 100644 --- a/apps/library/management/commands/seed_category_collection.py +++ b/apps/library/management/commands/seed_category_collection.py @@ -38,7 +38,7 @@ class Command(BaseCommand): for item in collections_data: col, created = BookCollection.objects.get_or_create( title=item['title'], - defaults={'display_position': item['pos'], 'pin_top': True} + defaults={'display_position': item['pos']} ) collection_objs.append(col) if created: diff --git a/apps/library/migrations/0007_remove_bookcollection_pin_top_and_more.py b/apps/library/migrations/0007_remove_bookcollection_pin_top_and_more.py new file mode 100644 index 0000000..7f66c00 --- /dev/null +++ b/apps/library/migrations/0007_remove_bookcollection_pin_top_and_more.py @@ -0,0 +1,27 @@ +# Generated by Django 4.2.30 on 2026-07-20 15:49 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('library', '0006_alter_author_number_of_volumes'), + ] + + operations = [ + migrations.RemoveField( + model_name='bookcollection', + name='pin_top', + ), + migrations.AddField( + model_name='bookcollection', + name='created_at', + field=models.DateTimeField(auto_now_add=True, null=True, verbose_name='created at'), + ), + migrations.AddField( + model_name='bookcollection', + name='updated_at', + field=models.DateTimeField(auto_now=True, null=True, verbose_name='updated at'), + ), + ] diff --git a/apps/library/models.py b/apps/library/models.py index 2acc6ab..a81aabe 100644 --- a/apps/library/models.py +++ b/apps/library/models.py @@ -20,7 +20,6 @@ class BookCollection(LowercaseSlugMixin, models.Model): title = models.CharField(max_length=255) slug = models.SlugField(max_length=255, unique=True, blank=True, null=True) summary = models.CharField(max_length=512, null=True, blank=True, help_text=_('could be null')) - pin_top = models.BooleanField(_('pin top'), default=True) display_position = models.CharField( max_length=20, choices=DisplayPosition.choices, @@ -29,6 +28,8 @@ class BookCollection(LowercaseSlugMixin, models.Model): ) status = models.BooleanField(_('status'), default=True) order = models.IntegerField(default=0, verbose_name=_('order')) + created_at = models.DateTimeField(auto_now_add=True, verbose_name=_('created at'), null=True, blank=True) + updated_at = models.DateTimeField(auto_now=True, verbose_name=_('updated at'), null=True, blank=True) books = models.ManyToManyField('library.Book', related_name='related_collections_books',through="library.Book_collections" ,verbose_name=_('Books'), blank=True) def __str__(self): diff --git a/apps/library/serializers_admin.py b/apps/library/serializers_admin.py index 8e2b96b..a98a036 100644 --- a/apps/library/serializers_admin.py +++ b/apps/library/serializers_admin.py @@ -58,7 +58,8 @@ class AdminLibraryCollectionSerializer(serializers.ModelSerializer): class Meta: model = BookCollection - fields = ["id", "title", "slug", "summary", "display_position", "status", "order", "pin_top"] + fields = ["id", "title", "slug", "summary", "display_position", "status", "order", "created_at", "updated_at"] + read_only_fields = ["id", "created_at", "updated_at"] class AdminAuthorSerializer(serializers.ModelSerializer): diff --git a/apps/library/views.py b/apps/library/views.py index 3bec746..7d1e23a 100644 --- a/apps/library/views.py +++ b/apps/library/views.py @@ -68,7 +68,7 @@ class PinnedBookCollectionListView(ListAPIView): return BookCollection.objects.filter( status=True, display_position=BookCollection.DisplayPosition.PINNED - ).order_by('-order', '-id') + ).order_by('order', '-created_at') def list(self, request, *args, **kwargs): @@ -249,7 +249,7 @@ class MiddleBookCollectionListView(ListAPIView): return BookCollection.objects.filter( status=True, display_position=BookCollection.DisplayPosition.MIDDLE - ).order_by('order') + ).order_by('order', '-created_at') class AuthorListView(ListAPIView): diff --git a/apps/podcast/admin.py b/apps/podcast/admin.py index 669e2dc..b8111c1 100644 --- a/apps/podcast/admin.py +++ b/apps/podcast/admin.py @@ -40,7 +40,7 @@ class PodcastCollectionAdminBase(ModelAdmin): fieldsets = ( (None, { - 'fields': ('title', 'summary', 'thumbnail', 'status', 'pin_top', 'order') + 'fields': ('title', 'summary', 'thumbnail', 'status', 'order') }), ) @@ -101,7 +101,7 @@ class MiddlePodcastCollectionAdmin(PodcastCollectionAdminBase): fieldsets = ( (None, { - 'fields': ('title', 'status', 'pin_top', 'order') + 'fields': ('title', 'status', 'order') }), ) diff --git a/apps/podcast/migrations/0003_remove_podcastcollection_pin_top.py b/apps/podcast/migrations/0003_remove_podcastcollection_pin_top.py new file mode 100644 index 0000000..fa70ae5 --- /dev/null +++ b/apps/podcast/migrations/0003_remove_podcastcollection_pin_top.py @@ -0,0 +1,17 @@ +# Generated by Django 4.2.30 on 2026-07-20 15:49 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('podcast', '0002_podcast_collections'), + ] + + operations = [ + migrations.RemoveField( + model_name='podcastcollection', + name='pin_top', + ), + ] diff --git a/apps/podcast/models.py b/apps/podcast/models.py index 4273cbc..c3e38e4 100644 --- a/apps/podcast/models.py +++ b/apps/podcast/models.py @@ -33,7 +33,6 @@ class PodcastCollection(LowercaseSlugMixin, models.Model): title = models.CharField(max_length=255, help_text="This title will not be displayed anywhere") slug = models.SlugField(max_length=255, unique=True) summary = models.CharField(max_length=512, null=True, blank=True, help_text=_('could be null')) - pin_top = models.BooleanField(_('pin top'), default=True) thumbnail = models.ImageField(upload_to='podcast/collection/', null=True, blank=True, help_text=_('image allowed')) order = models.IntegerField(default=0, verbose_name=_('order')) status = models.BooleanField(default=True, verbose_name=_('status')) diff --git a/apps/podcast/serializers.py b/apps/podcast/serializers.py index 9797873..f718175 100755 --- a/apps/podcast/serializers.py +++ b/apps/podcast/serializers.py @@ -295,7 +295,7 @@ class MiddlePodcastCollectionSerializer(serializers.ModelSerializer): class Meta: model = PodcastCollection - fields = ('id', 'title', 'slug', 'summary', 'status', 'order', 'pin_top', 'playlists') + fields = ('id', 'title', 'slug', 'summary', 'status', 'order', 'playlists') def get_playlists(self, obj): playlists = obj.related_playlists.filter(status=True).order_by('order', '-created_at') diff --git a/apps/podcast/serializers_admin.py b/apps/podcast/serializers_admin.py index 801ed3f..043e757 100644 --- a/apps/podcast/serializers_admin.py +++ b/apps/podcast/serializers_admin.py @@ -48,7 +48,7 @@ class AdminPodcastCategorySerializer(serializers.ModelSerializer): class AdminPodcastCollectionSerializer(serializers.ModelSerializer): class Meta: model = PodcastCollection - fields = ["id", "title", "slug", "summary", "display_position", "status", "order", "pin_top"] + fields = ["id", "title", "slug", "summary", "display_position", "status", "order"] class AdminPodcastListSerializer(serializers.ModelSerializer): diff --git a/apps/podcast/serializers_dovodi.py b/apps/podcast/serializers_dovodi.py index e657b6e..43e34c9 100644 --- a/apps/podcast/serializers_dovodi.py +++ b/apps/podcast/serializers_dovodi.py @@ -27,7 +27,6 @@ class DovodiPodcastCollectionSerializer(serializers.ModelSerializer): "display_position", "status", "order", - "pin_top", "thumbnail", "remove_thumbnail", ] diff --git a/apps/podcast/views.py b/apps/podcast/views.py index 030dd2b..3a53e24 100644 --- a/apps/podcast/views.py +++ b/apps/podcast/views.py @@ -71,7 +71,7 @@ class PinnedPodcastCollectionListView(generics.ListAPIView): return PinnedPodcastCollection.objects.filter( status=True, display_position=PodcastCollection.DisplayPosition.PINNED - ).order_by('-order', '-id') + ).order_by('order', '-created_at') def list(self, request, *args, **kwargs): response = super().list(request, *args, **kwargs) @@ -121,7 +121,7 @@ class MiddlePodcastCollectionListView(generics.ListAPIView): return PodcastCollection.objects.filter( status=True, display_position=PodcastCollection.DisplayPosition.MIDDLE - ).order_by('order') + ).order_by('order', '-created_at') class PodcastListAPIView(generics.ListAPIView): diff --git a/apps/video/admin.py b/apps/video/admin.py index 518505a..6961a76 100644 --- a/apps/video/admin.py +++ b/apps/video/admin.py @@ -40,7 +40,7 @@ class VideoCollectionAdminBase(ModelAdmin): fieldsets = ( (None, { - 'fields': ('title', 'summary', 'thumbnail' , 'status', 'pin_top', 'order') + 'fields': ('title', 'summary', 'thumbnail' , 'status', 'order') }), ) @@ -103,7 +103,7 @@ class MiddleVideoCollectionAdmin(VideoCollectionAdminBase): fieldsets = ( (None, { - 'fields': ('title', 'status', 'pin_top', 'order') + 'fields': ('title', 'status', 'order') }), ) diff --git a/apps/video/migrations/0003_remove_videocollection_pin_top.py b/apps/video/migrations/0003_remove_videocollection_pin_top.py new file mode 100644 index 0000000..de762c8 --- /dev/null +++ b/apps/video/migrations/0003_remove_videocollection_pin_top.py @@ -0,0 +1,17 @@ +# Generated by Django 4.2.30 on 2026-07-20 15:49 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('video', '0002_alter_video_video_file'), + ] + + operations = [ + migrations.RemoveField( + model_name='videocollection', + name='pin_top', + ), + ] diff --git a/apps/video/models.py b/apps/video/models.py index b384d5f..410ddd4 100644 --- a/apps/video/models.py +++ b/apps/video/models.py @@ -35,7 +35,6 @@ class VideoCollection(LowercaseSlugMixin, models.Model): title = models.CharField(max_length=255) slug = models.SlugField(max_length=255, unique=True) summary = models.CharField(max_length=512, null=True, blank=True, help_text=_('could be null')) - pin_top = models.BooleanField(_('pin top'), default=True) thumbnail = models.ImageField(upload_to='video/collection/', null=True, blank=True, help_text=_('image allowed')) order = models.IntegerField(default=0, verbose_name=_('order')) diff --git a/apps/video/serializers.py b/apps/video/serializers.py index cf1ac97..16b305f 100644 --- a/apps/video/serializers.py +++ b/apps/video/serializers.py @@ -283,7 +283,7 @@ class MiddleVideoCollectionSerializer(serializers.ModelSerializer): class Meta: model = VideoCollection - fields = ('id', 'title', 'slug', 'summary', 'status', 'order', 'pin_top','playlists') + fields = ('id', 'title', 'slug', 'summary', 'status', 'order', 'playlists') def get_playlists(self, obj): playlists = obj.related_playlists.filter(status=True).order_by('order', '-created_at') diff --git a/apps/video/serializers_admin.py b/apps/video/serializers_admin.py index 8bab2ea..3758426 100644 --- a/apps/video/serializers_admin.py +++ b/apps/video/serializers_admin.py @@ -48,7 +48,7 @@ class AdminVideoCategorySerializer(serializers.ModelSerializer): class AdminVideoCollectionSerializer(serializers.ModelSerializer): class Meta: model = VideoCollection - fields = ["id", "title", "slug", "summary", "display_position", "status", "order", "pin_top"] + fields = ["id", "title", "slug", "summary", "display_position", "status", "order"] class AdminVideoListSerializer(serializers.ModelSerializer): diff --git a/apps/video/serializers_dovodi.py b/apps/video/serializers_dovodi.py index 6b23f78..f4f137d 100644 --- a/apps/video/serializers_dovodi.py +++ b/apps/video/serializers_dovodi.py @@ -27,7 +27,6 @@ class DovodiVideoCollectionSerializer(serializers.ModelSerializer): "display_position", "status", "order", - "pin_top", "thumbnail", "remove_thumbnail", ] diff --git a/apps/video/views.py b/apps/video/views.py index 94b92ae..4cefa60 100644 --- a/apps/video/views.py +++ b/apps/video/views.py @@ -84,7 +84,7 @@ class PinnedVideoCollectionListView(generics.ListAPIView): queryset = PinnedVideoCollection.objects.filter( status=True, display_position=VideoCollection.DisplayPosition.PINNED - ).order_by('-order', '-id') + ).order_by('order', '-created_at') # Filter by bookmarks if requested is_bookmark = self.request.query_params.get('is_bookmark', '').lower() @@ -154,7 +154,7 @@ class MiddleVideoCollectionListView(generics.ListAPIView): return VideoCollection.objects.filter( status=True, display_position=VideoCollection.DisplayPosition.MIDDLE - ).order_by('order') + ).order_by('order', '-created_at') def list(self, request, *args, **kwargs):