Browse Source

articles views and serializers added for dashboard

master
Mohsen Taba 1 month ago
parent
commit
541c42bc2d
  1. 186
      apps/article/serializers_admin.py
  2. 12
      apps/article/urls.py
  3. 87
      apps/article/views_admin.py

186
apps/article/serializers_admin.py

@ -0,0 +1,186 @@
from rest_framework import serializers
from .models import Article, ArticleCategory, ArticleContent, ContentPart, TextSection
class AbsoluteImageField(serializers.ImageField):
def to_representation(self, value):
if not value:
return None
request = self.context.get("request")
url = value.url if hasattr(value, "url") else str(value)
if request:
return request.build_absolute_uri(url)
return url
class AbsoluteFileField(serializers.FileField):
def to_representation(self, value):
if not value:
return None
request = self.context.get("request")
url = value.url if hasattr(value, "url") else str(value)
if request:
return request.build_absolute_uri(url)
return url
class AdminArticleCategorySerializer(serializers.ModelSerializer):
class Meta:
model = ArticleCategory
fields = ["id", "title", "slug", "status", "order"]
class AdminArticleListSerializer(serializers.ModelSerializer):
thumbnail = AbsoluteImageField(required=False, allow_null=True)
article_file = AbsoluteFileField(required=False, allow_null=True)
categories = AdminArticleCategorySerializer(many=True, read_only=True)
class Meta:
model = Article
fields = [
"id",
"title",
"slug",
"thumbnail",
"description",
"article_file",
"view_count",
"download_count",
"status",
"created_at",
"updated_at",
"categories",
]
read_only_fields = ["id", "view_count", "download_count", "created_at", "updated_at"]
class AdminArticleDetailSerializer(serializers.ModelSerializer):
thumbnail = AbsoluteImageField(required=False, allow_null=True)
article_file = AbsoluteFileField(required=False, allow_null=True)
categories = serializers.PrimaryKeyRelatedField(
queryset=ArticleCategory.objects.all(),
many=True,
required=False,
)
categories_detail = AdminArticleCategorySerializer(source="categories", many=True, read_only=True)
remove_thumbnail = serializers.BooleanField(write_only=True, required=False, default=False)
remove_article_file = serializers.BooleanField(write_only=True, required=False, default=False)
class Meta:
model = Article
fields = [
"id",
"title",
"slug",
"thumbnail",
"description",
"content",
"article_file",
"view_count",
"download_count",
"status",
"created_at",
"updated_at",
"categories",
"categories_detail",
"embedded_in",
"remove_thumbnail",
"remove_article_file",
]
read_only_fields = ["id", "view_count", "download_count", "created_at", "updated_at"]
def create(self, validated_data):
validated_data.pop("remove_thumbnail", False)
validated_data.pop("remove_article_file", False)
categories = validated_data.pop("categories", [])
article = super().create(validated_data)
if categories:
article.categories.set(categories)
return article
def update(self, instance, validated_data):
remove_thumbnail = validated_data.pop("remove_thumbnail", False)
remove_article_file = validated_data.pop("remove_article_file", False)
categories = validated_data.pop("categories", None)
if remove_thumbnail and instance.thumbnail:
instance.thumbnail.delete(save=False)
instance.thumbnail = None
if remove_article_file and instance.article_file:
instance.article_file.delete(save=False)
instance.article_file = None
article = super().update(instance, validated_data)
if categories is not None:
article.categories.set(categories)
return article
class AdminTextSectionSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(required=False)
class Meta:
model = TextSection
fields = ["id", "arabic_text", "translation", "order"]
class AdminContentPartSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(required=False)
text_sections = AdminTextSectionSerializer(many=True, required=False)
class Meta:
model = ContentPart
fields = ["id", "order", "text_sections"]
class AdminArticleContentSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(required=False)
parts = AdminContentPartSerializer(many=True, required=False)
class Meta:
model = ArticleContent
fields = [
"id",
"article",
"title",
"content",
"priority",
"status",
"created_at",
"updated_at",
"parts",
]
read_only_fields = ["created_at", "updated_at"]
def _sync_parts(self, article_content, parts_data):
article_content.parts.all().delete()
for part_index, part_data in enumerate(parts_data, start=1):
text_sections_data = part_data.pop("text_sections", [])
part = ContentPart.objects.create(
article_content=article_content,
order=part_data.get("order") or part_index,
)
for section_index, section_data in enumerate(text_sections_data, start=1):
TextSection.objects.create(
content_part=part,
arabic_text=section_data.get("arabic_text", ""),
translation=section_data.get("translation", ""),
order=section_data.get("order") or section_index,
)
def create(self, validated_data):
parts_data = validated_data.pop("parts", [])
article_content = super().create(validated_data)
self._sync_parts(article_content, parts_data)
return article_content
def update(self, instance, validated_data):
parts_data = validated_data.pop("parts", None)
article_content = super().update(instance, validated_data)
if parts_data is not None:
self._sync_parts(article_content, parts_data)
return article_content

12
apps/article/urls.py

@ -1,8 +1,15 @@
from django.urls import path, re_path
from django.urls import include, path, re_path
from rest_framework.routers import SimpleRouter
from .views import *
from .views_admin import AdminArticleCategoryViewSet, AdminArticleContentViewSet, AdminArticleViewSet
app_name = 'article'
admin_router = SimpleRouter()
admin_router.register(r'articles', AdminArticleViewSet, basename='admin-articles')
admin_router.register(r'categories', AdminArticleCategoryViewSet, basename='admin-article-categories')
admin_router.register(r'contents', AdminArticleContentViewSet, basename='admin-article-contents')
urlpatterns = [
path('categories/', ArticleCategoryListAPIView.as_view(), name='category-list'),
path('pinned-collections/', PinnedArticleCollectionListView.as_view(), name='pinned-collection-list'),
@ -10,8 +17,9 @@ urlpatterns = [
path('list/', ArticleListAPIView.as_view(), name='podcast-list'),
re_path(r'detail/(?P<slug>[\w-]+)/$', ArticleDetailAPIView.as_view(), name='podcast-detail'),
path('admin/', include(admin_router.urls)),
# # User playlist endpoints
# path('user-playlist/', UserPlaylistCreateAPIView.as_view(), name='user-playlist-create'),
# path('user-playlist/list/', UserPlaylistListAPIView.as_view(), name='user-playlist-list'),
]
]

87
apps/article/views_admin.py

@ -0,0 +1,87 @@
from django.db.models import Q
from rest_framework.authentication import TokenAuthentication
from rest_framework.decorators import action
from rest_framework.parsers import FormParser, JSONParser, MultiPartParser
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet
from apps.account.permissions import IsSuperAdmin
from utils.pagination import StandardResultsSetPagination
from .models import Article, ArticleCategory, ArticleContent
from .serializers_admin import (
AdminArticleCategorySerializer,
AdminArticleContentSerializer,
AdminArticleDetailSerializer,
AdminArticleListSerializer,
)
class AdminArticleCategoryViewSet(ReadOnlyModelViewSet):
serializer_class = AdminArticleCategorySerializer
permission_classes = [IsAuthenticated, IsSuperAdmin]
authentication_classes = [TokenAuthentication]
pagination_class = None
def get_queryset(self):
return ArticleCategory.objects.all().order_by("order", "title")
class AdminArticleViewSet(ModelViewSet):
permission_classes = [IsAuthenticated, IsSuperAdmin]
authentication_classes = [TokenAuthentication]
pagination_class = StandardResultsSetPagination
parser_classes = (MultiPartParser, FormParser, JSONParser)
def get_serializer_class(self):
if self.action == "list":
return AdminArticleListSerializer
return AdminArticleDetailSerializer
def get_queryset(self):
queryset = Article.objects.all().prefetch_related("categories")
search_query = self.request.query_params.get("search")
if search_query:
queryset = queryset.filter(
Q(title__icontains=search_query)
| Q(slug__icontains=search_query)
| Q(description__icontains=search_query)
| Q(content__icontains=search_query)
)
category_id = self.request.query_params.get("category")
if category_id and category_id != "all":
queryset = queryset.filter(categories__id=category_id)
status_filter = self.request.query_params.get("status")
if status_filter in {"true", "false"}:
queryset = queryset.filter(status=status_filter == "true")
return queryset.distinct().order_by("-created_at")
class AdminArticleContentViewSet(ModelViewSet):
serializer_class = AdminArticleContentSerializer
permission_classes = [IsAuthenticated, IsSuperAdmin]
authentication_classes = [TokenAuthentication]
parser_classes = (JSONParser, MultiPartParser, FormParser)
def get_queryset(self):
queryset = ArticleContent.objects.all().prefetch_related("parts__text_sections")
article_id = self.request.query_params.get("article_id")
if article_id:
queryset = queryset.filter(article_id=article_id)
return queryset.order_by("priority", "created_at")
@action(detail=False, methods=["post"])
def reorder(self, request):
content_ids = request.data.get("content_ids", [])
if not content_ids:
return Response({"error": "content_ids list is required"}, status=400)
for index, content_id in enumerate(content_ids, start=1):
ArticleContent.objects.filter(id=content_id).update(priority=index)
return Response({"status": "success"})
Loading…
Cancel
Save