[PATCH 07/10] REST: De-duplicate handling of nested resource URLs
Stephen Finucane
stephen at that.guru
Sat Oct 1 02:19:18 AEST 2022
These were all doing the same thing. Make things more generic.
We also speed up test (inadvertently) by using the 'patch_id' attribute
of the 'Check' model rather than 'patch.id', thus avoiding the JOIN.
Signed-off-by: Stephen Finucane <stephen at that.guru>
---
patchwork/api/base.py | 52 +++++++++----------------------
patchwork/api/check.py | 10 ++++--
patchwork/api/embedded.py | 28 +++++++++++++----
patchwork/tests/api/test_event.py | 2 +-
4 files changed, 45 insertions(+), 47 deletions(-)
diff --git patchwork/api/base.py patchwork/api/base.py
index 3ed4182c..0f5c44a2 100644
--- patchwork/api/base.py
+++ patchwork/api/base.py
@@ -9,8 +9,8 @@ from django.conf import settings
from django.shortcuts import get_object_or_404
from rest_framework import permissions
from rest_framework.pagination import PageNumberPagination
+from rest_framework.relations import HyperlinkedIdentityField
from rest_framework.response import Response
-from rest_framework.serializers import HyperlinkedIdentityField
from rest_framework.serializers import HyperlinkedModelSerializer
from rest_framework.utils.urls import replace_query_param
@@ -122,52 +122,28 @@ class MultipleFieldLookupMixin(object):
return get_object_or_404(queryset, **filter_kwargs)
-class CheckHyperlinkedIdentityField(HyperlinkedIdentityField):
- def get_url(self, obj, view_name, request, format):
- # Unsaved objects will not yet have a valid URL.
- if obj.pk is None:
- return None
-
- return self.reverse(
- view_name,
- kwargs={
- 'patch_id': obj.patch.id,
- 'check_id': obj.id,
- },
- request=request,
- format=format,
- )
+class NestedHyperlinkedIdentityField(HyperlinkedIdentityField):
+ """A variant of HyperlinkedIdentityField that supports nested resources."""
+ def __init__(self, view_name, lookup_field_mapping, **kwargs):
+ self.lookup_field_mapping = lookup_field_mapping
+ super().__init__(view_name, **kwargs)
-class CoverCommentHyperlinkedIdentityField(HyperlinkedIdentityField):
def get_url(self, obj, view_name, request, format):
# Unsaved objects will not yet have a valid URL.
- if obj.pk is None:
+ if hasattr(obj, 'pk') and obj.pk in (None, ''):
return None
- return self.reverse(
- view_name,
- kwargs={
- 'cover_id': obj.cover.id,
- 'comment_id': obj.id,
- },
- request=request,
- format=format,
- )
-
-
-class PatchCommentHyperlinkedIdentityField(HyperlinkedIdentityField):
- def get_url(self, obj, view_name, request, format):
- # Unsaved objects will not yet have a valid URL.
- if obj.pk is None:
- return None
+ kwargs = {}
+ for (
+ lookup_url_kwarg,
+ lookup_field,
+ ) in self.lookup_field_mapping.items():
+ kwargs[lookup_url_kwarg] = getattr(obj, lookup_field)
return self.reverse(
view_name,
- kwargs={
- 'patch_id': obj.patch.id,
- 'comment_id': obj.id,
- },
+ kwargs=kwargs,
request=request,
format=format,
)
diff --git patchwork/api/check.py patchwork/api/check.py
index c28d89f7..f5461fc6 100644
--- patchwork/api/check.py
+++ patchwork/api/check.py
@@ -14,8 +14,8 @@ from rest_framework.serializers import HiddenField
from rest_framework.serializers import HyperlinkedModelSerializer
from rest_framework.serializers import ValidationError
-from patchwork.api.base import CheckHyperlinkedIdentityField
from patchwork.api.base import MultipleFieldLookupMixin
+from patchwork.api.base import NestedHyperlinkedIdentityField
from patchwork.api.base import CurrentPatchDefault
from patchwork.api.embedded import UserSerializer
from patchwork.api.filters import CheckFilterSet
@@ -25,7 +25,13 @@ from patchwork.models import Patch
class CheckSerializer(HyperlinkedModelSerializer):
- url = CheckHyperlinkedIdentityField('api-check-detail')
+ url = NestedHyperlinkedIdentityField(
+ 'api-check-detail',
+ lookup_field_mapping={
+ 'patch_id': 'patch_id',
+ 'check_id': 'id',
+ },
+ )
patch = HiddenField(default=CurrentPatchDefault())
user = UserSerializer(default=CurrentUserDefault())
diff --git patchwork/api/embedded.py patchwork/api/embedded.py
index 485ed6f7..7105da08 100644
--- patchwork/api/embedded.py
+++ patchwork/api/embedded.py
@@ -16,9 +16,7 @@ from rest_framework.serializers import PrimaryKeyRelatedField
from rest_framework.serializers import SerializerMethodField
from patchwork.api.base import BaseHyperlinkedModelSerializer
-from patchwork.api.base import CheckHyperlinkedIdentityField
-from patchwork.api.base import CoverCommentHyperlinkedIdentityField
-from patchwork.api.base import PatchCommentHyperlinkedIdentityField
+from patchwork.api.base import NestedHyperlinkedIdentityField
from patchwork import models
@@ -82,7 +80,13 @@ class WebURLMixin(BaseHyperlinkedModelSerializer):
class CheckSerializer(SerializedRelatedField):
class _Serializer(BaseHyperlinkedModelSerializer):
- url = CheckHyperlinkedIdentityField('api-check-detail')
+ url = NestedHyperlinkedIdentityField(
+ 'api-check-detail',
+ lookup_field_mapping={
+ 'patch_id': 'patch_id',
+ 'check_id': 'id',
+ },
+ )
def to_representation(self, instance):
data = super(CheckSerializer._Serializer, self).to_representation(
@@ -130,7 +134,13 @@ class CoverSerializer(SerializedRelatedField):
class CoverCommentSerializer(SerializedRelatedField):
class _Serializer(MboxMixin, WebURLMixin, BaseHyperlinkedModelSerializer):
- url = CoverCommentHyperlinkedIdentityField('api-cover-comment-detail')
+ url = NestedHyperlinkedIdentityField(
+ 'api-cover-comment-detail',
+ lookup_field_mapping={
+ 'cover_id': 'cover_id',
+ 'comment_id': 'id',
+ },
+ )
class Meta:
model = models.CoverComment
@@ -182,7 +192,13 @@ class PatchSerializer(SerializedRelatedField):
class PatchCommentSerializer(SerializedRelatedField):
class _Serializer(MboxMixin, WebURLMixin, BaseHyperlinkedModelSerializer):
- url = PatchCommentHyperlinkedIdentityField('api-patch-comment-detail')
+ url = NestedHyperlinkedIdentityField(
+ 'api-patch-comment-detail',
+ lookup_field_mapping={
+ 'patch_id': 'patch_id',
+ 'comment_id': 'id',
+ },
+ )
class Meta:
model = models.PatchComment
diff --git patchwork/tests/api/test_event.py patchwork/tests/api/test_event.py
index 7ca09c2e..1a0d811d 100644
--- patchwork/tests/api/test_event.py
+++ patchwork/tests/api/test_event.py
@@ -200,7 +200,7 @@ class TestEventAPI(APITestCase):
for _ in range(3):
self._create_events()
- with self.assertNumQueries(33):
+ with self.assertNumQueries(30):
self.client.get(self.api_url())
def test_order_by_date_default(self):
--
2.37.3
More information about the Patchwork
mailing list