diff --git a/forum/backends/mysql/models.py b/forum/backends/mysql/models.py index 806b4292..565d1de3 100644 --- a/forum/backends/mysql/models.py +++ b/forum/backends/mysql/models.py @@ -11,6 +11,7 @@ from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation from django.contrib.contenttypes.models import ContentType from django.core.exceptions import ValidationError +from django.db.models import F from django.db import models from django.db.models import QuerySet from django.utils import timezone @@ -437,40 +438,33 @@ def get_sort_key(self) -> str: def get_list(**kwargs: Any) -> list[dict[str, Any]]: """ Retrieves a list of all comments in the database based on provided filters. - - Args: - kwargs: The filter arguments. - - Returns: - A list of comments. + Optimized with select_related and DB-level sorting/pagination. """ + sort = kwargs.pop("sort", None) resp_skip = kwargs.pop("resp_skip", 0) resp_limit = kwargs.pop("resp_limit", None) - comments = Comment.objects.filter(**kwargs) - result = [] + + # select_related eliminates FK queries in to_dict() + comments = Comment.objects.filter(**kwargs).select_related( + "author", "parent", "comment_thread", "deleted_by" + ) + + # DB-level sorting instead of Python sorted() if sort: if sort == 1: - result = sorted( - comments, key=lambda x: (x.sort_key is None, x.sort_key or "") - ) + comments = comments.order_by(F("sort_key").asc(nulls_last=True)) elif sort == -1: - result = sorted( - comments, - key=lambda x: (x.sort_key is None, x.sort_key or ""), - reverse=True, - ) - - paginated_comments = result or list(comments) + comments = comments.order_by(F("sort_key").desc(nulls_last=True)) - # Apply pagination if resp_limit is provided + # DB-level pagination instead of loading all then slicing if resp_limit is not None: resp_end = resp_skip + resp_limit - paginated_comments = result[resp_skip:resp_end] - elif resp_skip: # If resp_limit is None but resp_skip is provided - paginated_comments = result[resp_skip:] + comments = comments[resp_skip:resp_end] + elif resp_skip: + comments = comments[resp_skip:] - return [content.to_dict() for content in paginated_comments] + return [content.to_dict() for content in comments] @staticmethod def get_list_total_count(**kwargs: Any) -> int: diff --git a/forum/serializers/comment.py b/forum/serializers/comment.py index 4c68b0f1..3fb4c8d4 100644 --- a/forum/serializers/comment.py +++ b/forum/serializers/comment.py @@ -86,21 +86,11 @@ def get_children(self, obj: Any) -> list[dict[str, Any]]: return list(serializer.data) def to_representation(self, instance: Any) -> dict[str, Any]: + """Return comment representation.""" comment = super().to_representation(instance) comment.pop("historical_abuse_flaggers") if comment["parent_id"] == "None": comment["parent_id"] = None - - thread = self.backend.get_thread(comment["thread_id"]) - comment_from_db = self.backend.get_comment(comment["id"]) - if ( - not comment["endorsed"] - and comment_from_db - and "endorsement" not in comment_from_db - and thread - and thread["thread_type"] == "question" - ): - comment.pop("endorsement", None) return comment def create(self, validated_data: dict[str, Any]) -> Any: