Skip to content

Commit fe07f30

Browse files
committed
Remove 'PatchRelationSerializer'
This wasn't writeable for reasons I haven't been able to figure out. However, it's not actually needed: the 'PatchSerializer' can do the job just fine, given enough information. This exposes a bug in DRF, which has been reported upstream [1]. While we wait for that fix, or some variant of it, to be merged, we must monkey patch the library. [1] encode/django-rest-framework#7550 [2] encode/django-rest-framework#7574 Signed-off-by: Stephen Finucane <[email protected]> Reported-by: Ralf Ramsauer <[email protected]> Closes: #379 Cc: Daniel Axtens <[email protected]> Cc: Rohit Sarkar <[email protected]>
1 parent 8092f8f commit fe07f30

File tree

4 files changed

+65
-42
lines changed

4 files changed

+65
-42
lines changed

patchwork/api/__init__.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Patchwork - automated patch tracking system
2+
# Copyright (C) 2020, Stephen Finucane <[email protected]>
3+
#
4+
# SPDX-License-Identifier: GPL-2.0-or-later
5+
6+
from rest_framework.fields import empty
7+
from rest_framework.fields import get_attribute
8+
from rest_framework.fields import SkipField
9+
from rest_framework.relations import ManyRelatedField
10+
11+
12+
# monkey patch django-rest-framework to work around issue #7550 [1] until #7574
13+
# [2] or some other variant lands
14+
#
15+
# [1] https://github.com/encode/django-rest-framework/issues/7550
16+
# [2] https://github.com/encode/django-rest-framework/pull/7574
17+
18+
def _get_attribute(self, instance):
19+
# Can't have any relationships if not created
20+
if hasattr(instance, 'pk') and instance.pk is None:
21+
return []
22+
23+
try:
24+
relationship = get_attribute(instance, self.source_attrs)
25+
except (KeyError, AttributeError) as exc:
26+
if self.default is not empty:
27+
return self.get_default()
28+
if self.allow_null:
29+
return None
30+
if not self.required:
31+
raise SkipField()
32+
msg = (
33+
'Got {exc_type} when attempting to get a value for field '
34+
'`{field}` on serializer `{serializer}`.\nThe serializer '
35+
'field might be named incorrectly and not match '
36+
'any attribute or key on the `{instance}` instance.\n'
37+
'Original exception text was: {exc}.'.format(
38+
exc_type=type(exc).__name__,
39+
field=self.field_name,
40+
serializer=self.parent.__class__.__name__,
41+
instance=instance.__class__.__name__,
42+
exc=exc
43+
)
44+
)
45+
raise type(exc)(msg)
46+
47+
return relationship.all() if hasattr(relationship, 'all') else relationship
48+
49+
50+
ManyRelatedField.get_attribute = _get_attribute

patchwork/api/embedded.py

Lines changed: 1 addition & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,8 @@
1212
from collections import OrderedDict
1313

1414
from rest_framework.serializers import CharField
15-
from rest_framework.serializers import SerializerMethodField
1615
from rest_framework.serializers import PrimaryKeyRelatedField
17-
from rest_framework.serializers import ValidationError
16+
from rest_framework.serializers import SerializerMethodField
1817

1918
from patchwork.api.base import BaseHyperlinkedModelSerializer
2019
from patchwork.api.base import CheckHyperlinkedIdentityField
@@ -139,31 +138,6 @@ class Meta:
139138
}
140139

141140

142-
class PatchRelationSerializer(BaseHyperlinkedModelSerializer):
143-
"""Hide the PatchRelation model, just show the list"""
144-
patches = PatchSerializer(many=True,
145-
style={'base_template': 'input.html'})
146-
147-
def to_internal_value(self, data):
148-
if not isinstance(data, type([])):
149-
raise ValidationError(
150-
"Patch relations must be specified as a list of patch IDs"
151-
)
152-
result = super(PatchRelationSerializer, self).to_internal_value(
153-
{'patches': data}
154-
)
155-
return result
156-
157-
def to_representation(self, instance):
158-
data = super(PatchRelationSerializer, self).to_representation(instance)
159-
data = data['patches']
160-
return data
161-
162-
class Meta:
163-
model = models.PatchRelation
164-
fields = ('patches',)
165-
166-
167141
class PersonSerializer(SerializedRelatedField):
168142

169143
class _Serializer(BaseHyperlinkedModelSerializer):

patchwork/api/event.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
from patchwork.api.embedded import CheckSerializer
1414
from patchwork.api.embedded import CoverSerializer
1515
from patchwork.api.embedded import PatchSerializer
16-
from patchwork.api.embedded import PatchRelationSerializer
1716
from patchwork.api.embedded import ProjectSerializer
1817
from patchwork.api.embedded import SeriesSerializer
1918
from patchwork.api.embedded import UserSerializer
@@ -34,8 +33,10 @@ class EventSerializer(ModelSerializer):
3433
current_delegate = UserSerializer()
3534
created_check = SerializerMethodField()
3635
created_check = CheckSerializer()
37-
previous_relation = PatchRelationSerializer(read_only=True)
38-
current_relation = PatchRelationSerializer(read_only=True)
36+
previous_relation = PatchSerializer(
37+
source='previous_relation.patches', many=True, default=None)
38+
current_relation = PatchSerializer(
39+
source='current_relation.patches', many=True, default=None)
3940

4041
_category_map = {
4142
Event.CATEGORY_COVER_CREATED: ['cover'],

patchwork/api/patch.py

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,23 @@
1010
from django.core.exceptions import ValidationError
1111
from django.utils.text import slugify
1212
from django.utils.translation import gettext_lazy as _
13-
from rest_framework import status
1413
from rest_framework.exceptions import APIException
1514
from rest_framework.exceptions import PermissionDenied
1615
from rest_framework.generics import ListAPIView
1716
from rest_framework.generics import RetrieveUpdateAPIView
1817
from rest_framework.relations import RelatedField
1918
from rest_framework.reverse import reverse
2019
from rest_framework.serializers import SerializerMethodField
20+
from rest_framework import status
2121

2222
from patchwork.api.base import BaseHyperlinkedModelSerializer
2323
from patchwork.api.base import PatchworkPermission
24-
from patchwork.api.filters import PatchFilterSet
25-
from patchwork.api.embedded import PatchRelationSerializer
24+
from patchwork.api.embedded import PatchSerializer
2625
from patchwork.api.embedded import PersonSerializer
2726
from patchwork.api.embedded import ProjectSerializer
2827
from patchwork.api.embedded import SeriesSerializer
2928
from patchwork.api.embedded import UserSerializer
29+
from patchwork.api.filters import PatchFilterSet
3030
from patchwork.models import Patch
3131
from patchwork.models import PatchRelation
3232
from patchwork.models import State
@@ -83,7 +83,8 @@ class PatchListSerializer(BaseHyperlinkedModelSerializer):
8383
check = SerializerMethodField()
8484
checks = SerializerMethodField()
8585
tags = SerializerMethodField()
86-
related = PatchRelationSerializer()
86+
related = PatchSerializer(
87+
source='related.patches', many=True, default=[])
8788

8889
def get_web_url(self, instance):
8990
request = self.context.get('request')
@@ -127,14 +128,11 @@ def to_representation(self, instance):
127128
data = super(PatchListSerializer, self).to_representation(instance)
128129
data['series'] = [data['series']] if data['series'] else []
129130

130-
# stop the related serializer returning this patch in the list of
131-
# related patches. Also make it return an empty list, not null/None
132-
if 'related' in data:
133-
if data['related']:
134-
data['related'] = [p for p in data['related']
135-
if p['id'] != instance.id]
136-
else:
137-
data['related'] = []
131+
# Remove this patch from 'related'
132+
if 'related' in data and data['related']:
133+
data['related'] = [
134+
x for x in data['related'] if x['id'] != data['id']
135+
]
138136

139137
return data
140138

0 commit comments

Comments
 (0)