Skip to content

Commit 481bb4a

Browse files
committed
fix(translate): improved comments loading
Avoid repeated fetching of comment attributes while translating. Fixes WEBLATE-36WD
1 parent 2aefe90 commit 481bb4a

File tree

4 files changed

+66
-7
lines changed

4 files changed

+66
-7
lines changed

docs/changes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ Weblate 5.17
8484
* :doc:`/formats/android` now preserves template-defined escaped markup formatting when saving translations.
8585
* REST API component creation now handles temporary uploaded files for ``docfile`` and ``zipfile`` uploads.
8686
* SSH repository errors now distinguish changed host keys from missing host keys and avoid automatically trusting host key replacements.
87+
* Improved loading speed for comments on the translate page.
8788

8889
.. rubric:: Compatibility
8990

weblate/trans/models/comment.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@
2121
if TYPE_CHECKING:
2222
from datetime import datetime
2323

24-
from weblate.auth.models import AuthenticatedHttpRequest, Unit, User
24+
from weblate.auth.models import AuthenticatedHttpRequest, User
25+
from weblate.trans.models.unit import Unit
2526

2627

2728
class CommentManager(models.Manager):
@@ -94,6 +95,9 @@ def add(
9495

9596

9697
class CommentQuerySet(models.QuerySet):
98+
def prefetch(self):
99+
return self.select_related("user")
100+
97101
def order(self):
98102
return self.order_by("timestamp")
99103

weblate/trans/models/unit.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1834,12 +1834,20 @@ def active_checks(self) -> list[Check]:
18341834
def all_comments(self) -> models.QuerySet[Comment]:
18351835
"""Return list of target comments."""
18361836
if self.is_source:
1837-
# Add all comments on translation on source string comment
1838-
query = Q(unit__source_unit=self)
1839-
else:
1840-
# Add source string comments for translation unit
1841-
query = Q(unit__in=(self, self.source_unit))
1842-
return Comment.objects.filter(query).prefetch_related("unit", "user").order()
1837+
return (
1838+
Comment.objects.filter(unit__source_unit=self)
1839+
.prefetch()
1840+
.prefetch_related(
1841+
"unit__translation__language",
1842+
"unit__translation__component__project",
1843+
)
1844+
.order()
1845+
)
1846+
return (
1847+
(self.comment_set.all() | self.source_unit.comment_set.all())
1848+
.prefetch()
1849+
.order()
1850+
)
18431851

18441852
@cached_property
18451853
def unresolved_comments(self) -> list[Comment]:

weblate/trans/tests/test_comment.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
"""Tests for comment views."""
66

7+
from django.db import connection
8+
from django.test.utils import CaptureQueriesContext
79
from django.urls import reverse
810

911
from weblate.trans.models import Comment
@@ -155,3 +157,47 @@ def test_resolve_comment(self) -> None:
155157
comment.refresh_from_db()
156158
self.assertTrue(comment.resolved)
157159
self.assertFalse(comment.unit.has_comment)
160+
161+
def test_translate_comment_queries_do_not_scale_with_comment_count(self) -> None:
162+
unit = self.get_unit()
163+
self.make_manager()
164+
165+
def render_queries() -> int:
166+
for _unused in range(2):
167+
session = self.client.session
168+
session.clear()
169+
session.save()
170+
response = self.client.get(
171+
unit.translation.get_translate_url(),
172+
{"checksum": unit.checksum},
173+
)
174+
self.assertEqual(response.status_code, 200)
175+
176+
session = self.client.session
177+
session.clear()
178+
session.save()
179+
with CaptureQueriesContext(connection) as queries:
180+
response = self.client.get(
181+
unit.translation.get_translate_url(),
182+
{"checksum": unit.checksum},
183+
)
184+
self.assertEqual(response.status_code, 200)
185+
return len(queries)
186+
187+
Comment.objects.filter(unit__in=(unit, unit.source_unit)).delete()
188+
Comment.objects.create(
189+
unit=unit,
190+
comment="Comment 0",
191+
user=self.anotheruser,
192+
)
193+
baseline_queries = render_queries()
194+
195+
Comment.objects.filter(unit__in=(unit, unit.source_unit)).delete()
196+
for index in range(5):
197+
Comment.objects.create(
198+
unit=unit,
199+
comment=f"Comment {index}",
200+
user=self.anotheruser,
201+
)
202+
203+
self.assertEqual(render_queries(), baseline_queries)

0 commit comments

Comments
 (0)