from django.shortcuts import render from rest_framework.views import APIView, Response from rest_framework import status from .models import BlogModel, BlogCategoryModel from .serializers import AllBlogSerilizer, BlogSerilizer, AllBlogCategorySerilizer from django.shortcuts import get_object_or_404 from utils.pagination import StructurePagination from drf_spectacular.utils import extend_schema, OpenApiParameter, OpenApiTypes from django.db.models import Q, F from django.views.decorators.cache import cache_page from django.utils.decorators import method_decorator from django.core.cache import cache class AllBlogCategoryView(APIView): serializer_class = AllBlogCategorySerilizer authentication_classes = [] @method_decorator(cache_page(60 * 60)) # Cache for 1 hour def get(self, request): categories = BlogCategoryModel.objects.all().only('id', 'name') category_ser = self.serializer_class(instance=categories, many=True, context={'request': request}) return Response(category_ser.data, status=status.HTTP_200_OK) class AllBlogView(APIView): serializer_class = AllBlogSerilizer pagination_class = StructurePagination authentication_classes = [] @extend_schema( parameters=[ OpenApiParameter( name="search", description="بگردش در بلاگ", required=False, type=OpenApiTypes.STR, ), OpenApiParameter( name="category_id", description="فیلتر بر اساس دسته بندی", required=False, type=OpenApiTypes.STR, ), OpenApiParameter( name="limit", description="تعداد نتایج", required=False, type=OpenApiTypes.INT, ), OpenApiParameter( name="offset", description="شروع از", required=False, type=OpenApiTypes.INT, ) ], responses={ 200: AllBlogSerilizer(many=True), 404: OpenApiTypes.OBJECT, }, ) def get(self, request): # Use only() to fetch required fields blogs = BlogModel.objects.filter(is_published=True).select_related('category').only( 'id', 'title', 'slug', 'summery', 'cover_image', 'views', 'created_at', 'category' ) search_query = request.query_params.get('search', None) category_id = request.query_params.get('category_id', None) if search_query: blogs = blogs.filter(Q(title__icontains=search_query) | Q(summery__icontains=search_query)) if category_id: blogs = blogs.filter(category_id=category_id) # Use category_id directly paginator = self.pagination_class() paginated_blogs = paginator.paginate_queryset(blogs, request) blog_ser = self.serializer_class(instance=paginated_blogs, many=True, context={'request': request}) return paginator.get_paginated_response(blog_ser.data) class BlogView(APIView): serializer_class = BlogSerilizer authentication_classes = [] def get_client_ip(self, request): """Helper function to get the client IP from request headers.""" x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR') if x_forwarded_for: ip = x_forwarded_for.split(',')[0] else: ip = request.META.get('REMOTE_ADDR') return ip def get(self, request, slug): # Use only() to fetch required fields blog = get_object_or_404(BlogModel.objects.select_related('category', 'author'), slug=slug) if blog.is_published: # Track views using cache instead of session (more efficient) client_ip = self.get_client_ip(request) cache_key = f'viewed_blog_{slug}_{client_ip}' if not cache.get(cache_key): # Use F() to avoid race conditions BlogModel.objects.filter(pk=blog.pk).update(views=F('views') + 1) blog.refresh_from_db(fields=['views']) cache.set(cache_key, True, 3600) # Cache for 1 hour blog_ser = self.serializer_class(instance=blog, context={'request': request}) return Response(blog_ser.data, status=status.HTTP_200_OK) else: return Response( {'detail': 'object with the given id does not exist or is not published yet'}, status=status.HTTP_404_NOT_FOUND )