Refactor link header and fix empty page case

This commit is contained in:
Andrew Godwin 2022-12-29 11:46:25 -07:00
parent ea6f272047
commit 9c3806a175
4 changed files with 43 additions and 45 deletions

View File

@ -7,11 +7,19 @@ from django.http import HttpRequest
@dataclasses.dataclass
class PaginationResult:
"""
Represents a pagination result for Mastodon (it does Link header stuff)
"""
#: A list of objects that matched the pagination query.
results: list[models.Model]
#: The actual applied limit, which may be different from what was requested.
limit: int
sort_attribute: str
@classmethod
def empty(cls):
return cls(results=[], limit=20)
def next(self, request: HttpRequest, allowed_params: list[str]):
"""
@ -37,6 +45,17 @@ class PaginationResult:
return f"{request.build_absolute_uri(request.path)}?{urllib.parse.urlencode(params)}"
def link_header(self, request: HttpRequest, allowed_params: list[str]):
"""
Creates a link header for the given request
"""
return ", ".join(
(
f'<{self.next(request, allowed_params)}>; rel="next"',
f'<{self.prev(request, allowed_params)}>; rel="prev"',
)
)
@staticmethod
def filter_params(request: HttpRequest, allowed_params: list[str]):
params = {}
@ -71,12 +90,12 @@ class MastodonPaginator:
max_id: str | None,
since_id: str | None,
limit: int | None,
):
) -> PaginationResult:
if max_id:
try:
anchor = self.anchor_model.objects.get(pk=max_id)
except self.anchor_model.DoesNotExist:
return []
return PaginationResult.empty()
queryset = queryset.filter(
**{self.sort_attribute + "__lt": getattr(anchor, self.sort_attribute)}
)
@ -85,7 +104,7 @@ class MastodonPaginator:
try:
anchor = self.anchor_model.objects.get(pk=since_id)
except self.anchor_model.DoesNotExist:
return []
return PaginationResult.empty()
queryset = queryset.filter(
**{self.sort_attribute + "__gt": getattr(anchor, self.sort_attribute)}
)
@ -96,7 +115,7 @@ class MastodonPaginator:
try:
anchor = self.anchor_model.objects.get(pk=min_id)
except self.anchor_model.DoesNotExist:
return []
return PaginationResult.empty()
queryset = queryset.filter(
**{self.sort_attribute + "__gt": getattr(anchor, self.sort_attribute)}
).order_by(self.sort_attribute)
@ -107,5 +126,4 @@ class MastodonPaginator:
return PaginationResult(
results=list(queryset[:limit]),
limit=limit,
sort_attribute=self.sort_attribute,
)

View File

@ -132,20 +132,17 @@ def account_statuses(
)
if pager.results:
params = [
"limit",
"id",
"exclude_reblogs",
"exclude_replies",
"only_media",
"pinned",
"tagged",
]
response.headers["Link"] = ", ".join(
(
f'<{pager.next(request, params)}>; rel="next"',
f'<{pager.prev(request, params)}>; rel="prev"',
)
response.headers["Link"] = pager.link_header(
request,
[
"limit",
"id",
"exclude_reblogs",
"exclude_replies",
"only_media",
"pinned",
"tagged",
],
)
interactions = PostInteraction.get_post_interactions(

View File

@ -45,13 +45,7 @@ def notifications(
)
if pager.results:
params = ["limit", "account_id"]
response.headers["Link"] = ", ".join(
(
f'<{pager.next(request, params)}>; rel="next"',
f'<{pager.prev(request, params)}>; rel="prev"',
)
)
response.headers["Link"] = pager.link_header(request, ["limit", "account_id"])
interactions = PostInteraction.get_event_interactions(
pager.results, request.identity

View File

@ -33,12 +33,7 @@ def home(
)
if pager.results:
response.headers["Link"] = ", ".join(
(
f"<{pager.next(request, ['limit'])}>; rel=\"next\"",
f"<{pager.prev(request, ['limit'])}>; rel=\"prev\"",
)
)
response.headers["Link"] = pager.link_header(request, ["limit"])
return [
event.to_mastodon_status_json(interactions=interactions)
@ -79,12 +74,9 @@ def public(
)
if pager.results:
params = ["limit", "local", "remote", "only_media"]
response.headers["Link"] = ", ".join(
(
f'<{pager.next(request, params)}>; rel="next"',
f'<{pager.prev(request, params)}>; rel="prev"',
)
response.headers["Link"] = pager.link_header(
request,
["limit", "local", "remote", "only_media"],
)
interactions = PostInteraction.get_post_interactions(
@ -123,12 +115,9 @@ def hashtag(
)
if pager.results:
params = ["limit", "local", "hashtag", "only_media"]
response.headers["Link"] = ", ".join(
(
f'<{pager.next(request, params)}>; rel="next"',
f'<{pager.prev(request, params)}>; rel="prev"',
)
response.headers["Link"] = pager.link_header(
request,
["limit", "local", "remote", "only_media"],
)
interactions = PostInteraction.get_post_interactions(