HyperlinkedIdentityField uses lookup_field kwarg.

According to the [Serializers API Guide][1], `HyperlinkedIdentityField`
takes `lookup_field` as a kwarg like the other related fields and the
generic views. However, this was not actually implemented.

[1]: http://django-rest-framework.org/api-guide/serializers.html#hyperlinkedmodelserializer
This commit is contained in:
Andy Freeland 2013-05-16 11:24:11 -04:00
parent 0e81ffced2
commit abe207b869
3 changed files with 61 additions and 6 deletions

View File

@ -202,9 +202,7 @@ This field is always read-only.
**Arguments**: **Arguments**:
* `view_name` - The view name that should be used as the target of the relationship. **required**. * `view_name` - The view name that should be used as the target of the relationship. **required**.
* `slug_field` - The field on the target that should be used for the lookup. Default is `'slug'`. * `lookup_field` - The field on the target that should be used for the lookup. Should correspond to a URL keyword argument on the referenced view. Default is `'pk'`.
* `pk_url_kwarg` - The named url parameter for the pk field lookup. Default is `pk`.
* `slug_url_kwarg` - The named url parameter for the slug field lookup. Default is to use the same value as given for `slug_field`.
* `format` - If using format suffixes, hyperlinked fields will use the same format suffix for the target unless overridden by using the `format` argument. * `format` - If using format suffixes, hyperlinked fields will use the same format suffix for the target unless overridden by using the `format` argument.
--- ---

View File

@ -465,10 +465,13 @@ class HyperlinkedIdentityField(Field):
""" """
Represents the instance, or a property on the instance, using hyperlinking. Represents the instance, or a property on the instance, using hyperlinking.
""" """
lookup_field = 'pk'
read_only = True
# These are all pending deprecation
pk_url_kwarg = 'pk' pk_url_kwarg = 'pk'
slug_field = 'slug' slug_field = 'slug'
slug_url_kwarg = None # Defaults to same as `slug_field` unless overridden slug_url_kwarg = None # Defaults to same as `slug_field` unless overridden
read_only = True
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
# TODO: Make view_name mandatory, and have the # TODO: Make view_name mandatory, and have the
@ -477,6 +480,19 @@ class HyperlinkedIdentityField(Field):
# Optionally the format of the target hyperlink may be specified # Optionally the format of the target hyperlink may be specified
self.format = kwargs.pop('format', None) self.format = kwargs.pop('format', None)
self.lookup_field = kwargs.pop('lookup_field', self.lookup_field)
# These are pending deprecation
if 'pk_url_kwarg' in kwargs:
msg = 'pk_url_kwarg is pending deprecation. Use lookup_field instead.'
warnings.warn(msg, PendingDeprecationWarning, stacklevel=2)
if 'slug_url_kwarg' in kwargs:
msg = 'slug_url_kwarg is pending deprecation. Use lookup_field instead.'
warnings.warn(msg, PendingDeprecationWarning, stacklevel=2)
if 'slug_field' in kwargs:
msg = 'slug_field is pending deprecation. Use lookup_field instead.'
warnings.warn(msg, PendingDeprecationWarning, stacklevel=2)
self.slug_field = kwargs.pop('slug_field', self.slug_field) self.slug_field = kwargs.pop('slug_field', self.slug_field)
default_slug_kwarg = self.slug_url_kwarg or self.slug_field default_slug_kwarg = self.slug_url_kwarg or self.slug_field
self.pk_url_kwarg = kwargs.pop('pk_url_kwarg', self.pk_url_kwarg) self.pk_url_kwarg = kwargs.pop('pk_url_kwarg', self.pk_url_kwarg)
@ -488,7 +504,8 @@ class HyperlinkedIdentityField(Field):
request = self.context.get('request', None) request = self.context.get('request', None)
format = self.context.get('format', None) format = self.context.get('format', None)
view_name = self.view_name or self.parent.opts.view_name view_name = self.view_name or self.parent.opts.view_name
kwargs = {self.pk_url_kwarg: obj.pk} lookup_field = getattr(obj, self.lookup_field)
kwargs = {self.lookup_field: lookup_field}
if request is None: if request is None:
warnings.warn("Using `HyperlinkedIdentityField` without including the " warnings.warn("Using `HyperlinkedIdentityField` without including the "

View File

@ -27,6 +27,14 @@ class PhotoSerializer(serializers.Serializer):
return Photo(**attrs) return Photo(**attrs)
class AlbumSerializer(serializers.ModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='album-detail', lookup_field='title')
class Meta:
model = Album
fields = ('title', 'url')
class BasicList(generics.ListCreateAPIView): class BasicList(generics.ListCreateAPIView):
model = BasicModel model = BasicModel
model_serializer_class = serializers.HyperlinkedModelSerializer model_serializer_class = serializers.HyperlinkedModelSerializer
@ -73,6 +81,8 @@ class PhotoListCreate(generics.ListCreateAPIView):
class AlbumDetail(generics.RetrieveAPIView): class AlbumDetail(generics.RetrieveAPIView):
model = Album model = Album
serializer_class = AlbumSerializer
lookup_field = 'title'
class OptionalRelationDetail(generics.RetrieveUpdateDestroyAPIView): class OptionalRelationDetail(generics.RetrieveUpdateDestroyAPIView):
@ -180,6 +190,36 @@ class TestManyToManyHyperlinkedView(TestCase):
self.assertEqual(response.data, self.data[0]) self.assertEqual(response.data, self.data[0])
class TestHyperlinkedIdentityFieldLookup(TestCase):
urls = 'rest_framework.tests.hyperlinkedserializers'
def setUp(self):
"""
Create 3 Album instances.
"""
titles = ['foo', 'bar', 'baz']
for title in titles:
album = Album(title=title)
album.save()
self.detail_view = AlbumDetail.as_view()
self.data = {
'foo': {'title': 'foo', 'url': 'http://testserver/albums/foo/'},
'bar': {'title': 'bar', 'url': 'http://testserver/albums/bar/'},
'baz': {'title': 'baz', 'url': 'http://testserver/albums/baz/'}
}
def test_lookup_field(self):
"""
GET requests to AlbumDetail view should return serialized Albums
with a url field keyed by `title`.
"""
for album in Album.objects.all():
request = factory.get('/albums/{0}/'.format(album.title))
response = self.detail_view(request, title=album.title)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data, self.data[album.title])
class TestCreateWithForeignKeys(TestCase): class TestCreateWithForeignKeys(TestCase):
urls = 'rest_framework.tests.hyperlinkedserializers' urls = 'rest_framework.tests.hyperlinkedserializers'