drf pagination duplicate data when applying a sorting

22 Views Asked by At

Why i am having a duplicate data when i tried to apply a sorting with django-rest-framework.

I have this on my settings.py

REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": (
        "rest_framework.authentication.BasicAuthentication",
        "rest_framework.authentication.SessionAuthentication",
        "rest_framework_simplejwt.authentication.JWTAuthentication",
        "dj_rest_auth.jwt_auth.JWTCookieAuthentication",
    ),
    "DEFAULT_PERMISSION_CLASSES": ("rest_framework.permissions.IsAuthenticated",),
    "DEFAULT_FILTER_BACKENDS": (
        "django_filters.rest_framework.DjangoFilterBackend",
        "rest_framework.filters.OrderingFilter",
    ),
    "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.LimitOffsetPagination",
    "PAGE_SIZE": 10
}

then i have this serializer

class PersonListSerializer(serializers.ModelSerializer):
    org = OrgSerializer(many=False)
    # prompts = PromptResponseSerializer(read_only=True, many=True)
    # emails = EmailSerializer(read_only=True, many=True)
    custom_fields = PersonCustomFieldValueSerializer(many=True)

    tags = TagSerializer(many=True, default=[])
    total_click_count = serializers.IntegerField(read_only=True)
    total_opened = serializers.IntegerField(read_only=True)
    total_reply = serializers.IntegerField(read_only=True)
    total_email_sent = serializers.IntegerField(read_only=True)
    scheduled_emails = serializers.SerializerMethodField()

    class Meta:
        model = Person
        fields = [
            "id",
            "first_name",
            "last_name",
            "job_title",
            "company_name",
            "work_email",
            "company_website",
            "sent_emails",
            "last_contacted",
            "next_scheduled_email",
            "custom_fields",
            "org",
            "tags",
            "total_click_count",
            "total_opened",
            "total_reply",
            "total_email_sent",
            "got_data",
            "force_enable",
            "assigned_user",
            "status",
            "person_city",
            "state",
            "industry",
            "phone",
            "linkedin",
            "scheduled_emails",
        ]

    def get_scheduled_emails(self, obj):
        return EmailSerializer(obj.get_scheduled_emails(), many=True).data

    def get_user_created_emails(self, _obj):
        return _obj.emails.filter(created_by=self.context["request"].user)
    
    def to_representation(self, obj):
        representation = super().to_representation(obj)

        # Calculate sent_email_count
        sent_email_count = obj.emails.filter(
            status=0, created_by=self.context["request"].user
        ).count()

        total_click_count = sum(
            click_count or 0
            for email in self.get_user_created_emails(obj)
            for click_count in email.click_tracking.values_list(
                "click_count", flat=True
            )
        )

        opened_emails = [
            email
            for email in self.get_user_created_emails(obj)
            if hasattr(email, "trackings") and email.trackings.opened
        ]

        total_reply_count = (
            self.get_user_created_emails(obj).aggregate(
                total_reply_count=Sum("reply_count__count")
            )["total_reply_count"]
            or 0
        )

        # Update the total_email_sent field in the representation
        representation['total_email_sent'] = sent_email_count
        representation['total_click_count'] = total_click_count
        representation['total_opened'] = len(opened_emails)
        representation['total_reply_count'] = total_reply_count

        return representation  

after that i tried to implement a sorting on my ModelViewSet views, but when i go to pages on my table since i have implemented a pagination of 10 per page, then i keep having duplicate data when i tried to sort. Why ?

Here is my ViewSet:

class PersonViewSet(ModelViewSet):
    search_fields = [
        "first_name",
        "last_name",
        "job_title",
        "company_website",
        "industry",
        "work_email",
        "person_city",
        "state",
        "email_domain",
        "linkedin",
        "got_data",
        "org__industry",
        "org__name",
        "org__domain",
        "assigned_user",
    ]
    filter_backends = (
        filters.SearchFilter,
        filters.OrderingFilter,
        DjangoFilterBackend,
    )
    ordering_fields = [
        "first_name",
        "got_data",
        "assigned_user",
        "job_title",
        "company_name",
        "company_website",
        "industry",
        "work_email",
        "sent_emails",
        "org__name",
        "last_contacted",
        "next_scheduled_email",
        "total_click_count",
        "total_opened",
        "total_reply_count",
        "total_email_sent",
    ]
    filterset_class = PersonFilter
    queryset = Person.objects.all()

    def create(self, request, *args, **kwargs):
        # Check if a user with the same email already exists
        user = request.user
        existing_user = Person.objects.filter(
            work_email=request.data.get("work_email"),
            team_id=user.team,
        ).first()
        if existing_user:
            return Response(
                {
                    "detail": "A person with this email already exists for this user's team."
                },
                status=status.HTTP_400_BAD_REQUEST,
            )

        return super().create(request, *args, **kwargs)

    def get_queryset(self, *args, **kwargs):
        if self.request.user.team:
            queryset = self.request.user.team.leads.all()
        else:
            queryset = super().get_queryset(*args, **kwargs)
        
        ordering = self.request.query_params.get("ordering", "")

        if ordering == "total_email_sent":
            queryset = queryset.annotate(
                total_email_sent=Count(
                    "emails",
                    filter=models.Q(
                        emails__status=0, emails__created_by=self.request.user
                    ),
                )
            ).order_by("total_email_sent")
        elif ordering == "-total_email_sent":
            queryset = queryset.annotate(
                total_email_sent=Count(
                    "emails",
                    filter=models.Q(
                        emails__status=0, emails__created_by=self.request.user
                    ),
                )
            ).order_by("total_email_sent")

        else:
            queryset = queryset.order_by("-id")

        print(str(queryset.query))

        return queryset
0

There are 0 best solutions below