diff options
Diffstat (limited to 'rest_framework/relations.py')
| -rw-r--r-- | rest_framework/relations.py | 67 |
1 files changed, 44 insertions, 23 deletions
diff --git a/rest_framework/relations.py b/rest_framework/relations.py index c4271e33..e3675b51 100644 --- a/rest_framework/relations.py +++ b/rest_framework/relations.py @@ -72,7 +72,6 @@ class RelatedField(WritableField): else: # Reverse self.queryset = manager.field.rel.to._default_manager.all() except Exception: - raise msg = ('Serializer related fields must include a `queryset`' + ' argument or set `read_only=True') raise Exception(msg) @@ -488,13 +487,15 @@ class HyperlinkedIdentityField(Field): slug_url_kwarg = None # Defaults to same as `slug_field` unless overridden def __init__(self, *args, **kwargs): - # TODO: Make view_name mandatory, and have the - # HyperlinkedModelSerializer set it on-the-fly - self.view_name = kwargs.pop('view_name', None) - # Optionally the format of the target hyperlink may be specified - self.format = kwargs.pop('format', None) + try: + self.view_name = kwargs.pop('view_name') + except KeyError: + msg = "HyperlinkedIdentityField requires 'view_name' argument" + raise ValueError(msg) - self.lookup_field = kwargs.pop('lookup_field', self.lookup_field) + self.format = kwargs.pop('format', None) + lookup_field = kwargs.pop('lookup_field', None) + self.lookup_field = lookup_field or self.lookup_field # These are pending deprecation if 'pk_url_kwarg' in kwargs: @@ -517,9 +518,7 @@ class HyperlinkedIdentityField(Field): def field_to_native(self, obj, field_name): request = self.context.get('request', None) format = self.context.get('format', None) - view_name = self.view_name or self.parent.opts.view_name - lookup_field = getattr(obj, self.lookup_field) - kwargs = {self.lookup_field: lookup_field} + view_name = self.view_name if request is None: warnings.warn("Using `HyperlinkedIdentityField` without including the " @@ -539,29 +538,51 @@ class HyperlinkedIdentityField(Field): if format and self.format and self.format != format: format = self.format + # Return the hyperlink, or error if incorrectly configured. try: - return reverse(view_name, kwargs=kwargs, request=request, format=format) + return self.get_url(obj, view_name, request, format) except NoReverseMatch: - pass - - slug = getattr(obj, self.slug_field, None) + msg = ( + 'Could not resolve URL for hyperlinked relationship using ' + 'view name "%s". You may have failed to include the related ' + 'model in your API, or incorrectly configured the ' + '`lookup_field` attribute on this field.' + ) + raise Exception(msg % view_name) - if not slug: - raise Exception('Could not resolve URL for field using view name "%s"' % view_name) + def get_url(self, obj, view_name, request, format): + """ + Given an object, return the URL that hyperlinks to the object. - kwargs = {self.slug_url_kwarg: slug} + May raise a `NoReverseMatch` if the `view_name` and `lookup_field` + attributes are not configured to correctly match the URL conf. + """ + lookup_field = getattr(obj, self.lookup_field) + kwargs = {self.lookup_field: lookup_field} try: return reverse(view_name, kwargs=kwargs, request=request, format=format) except NoReverseMatch: pass - kwargs = {self.pk_url_kwarg: obj.pk, self.slug_url_kwarg: slug} - try: - return reverse(view_name, kwargs=kwargs, request=request, format=format) - except NoReverseMatch: - pass + if self.pk_url_kwarg != 'pk': + # Only try pk lookup if it has been explicitly set. + # Otherwise, the default `lookup_field = 'pk'` has us covered. + kwargs = {self.pk_url_kwarg: obj.pk} + try: + return reverse(view_name, kwargs=kwargs, request=request, format=format) + except NoReverseMatch: + pass + + slug = getattr(obj, self.slug_field, None) + if slug: + # Only use slug lookup if a slug field exists on the model + kwargs = {self.slug_url_kwarg: slug} + try: + return reverse(view_name, kwargs=kwargs, request=request, format=format) + except NoReverseMatch: + pass - raise Exception('Could not resolve URL for field using view name "%s"' % view_name) + raise NoReverseMatch() ### Old-style many classes for backwards compat |
