diff options
| author | Tom Christie | 2012-10-04 16:58:18 +0100 | 
|---|---|---|
| committer | Tom Christie | 2012-10-04 16:58:18 +0100 | 
| commit | c91d926b0664981de0fd239a4398dd71367a5911 (patch) | |
| tree | bd259ce7df6b584e96467d6b22790b2074d308b7 /rest_framework/fields.py | |
| parent | 55e9cbecac1456f0e1521a4bcceb1ef4f44e5e0b (diff) | |
| download | django-rest-framework-c91d926b0664981de0fd239a4398dd71367a5911.tar.bz2 | |
Initial tests for hyperlinked relationships
Diffstat (limited to 'rest_framework/fields.py')
| -rw-r--r-- | rest_framework/fields.py | 93 | 
1 files changed, 90 insertions, 3 deletions
| diff --git a/rest_framework/fields.py b/rest_framework/fields.py index ad2ca589..9dbc1194 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -4,7 +4,8 @@ import inspect  import warnings  from django.core import validators -from django.core.exceptions import ValidationError +from django.core.exceptions import ObjectDoesNotExist, ValidationError +from django.core.urlresolvers import resolve  from django.conf import settings  from django.utils.encoding import is_protected_type, smart_unicode  from django.utils.translation import ugettext_lazy as _ @@ -223,9 +224,9 @@ class RelatedField(WritableField):          into[(self.source or field_name) + '_id'] = self.from_native(value) -class ManyRelatedField(RelatedField): +class ManyRelatedMixin(object):      """ -    Base class for related model managers. +    Mixin to convert a related field to a many related field.      """      def field_to_native(self, obj, field_name):          value = getattr(obj, self.source or field_name) @@ -244,6 +245,15 @@ class ManyRelatedField(RelatedField):          into[field_name] = [self.from_native(item) for item in value] +class ManyRelatedField(ManyRelatedMixin, RelatedField): +    """ +    Base class for related model managers. +    """ +    pass + + +### PrimaryKey relationships +  class PrimaryKeyRelatedField(RelatedField):      """      Serializes a related field or related object to a pk value. @@ -283,6 +293,83 @@ class ManyPrimaryKeyRelatedField(ManyRelatedField):          return [self.to_native(item.pk) for item in queryset.all()] +### Hyperlinked relationships + +class HyperlinkedRelatedField(RelatedField): +    pk_url_kwarg = 'pk' +    slug_url_kwarg = 'slug' +    slug_field = 'slug' + +    def __init__(self, *args, **kwargs): +        try: +            self.view_name = kwargs.pop('view_name') +        except: +            raise ValueError("Hyperlinked field requires 'view_name' kwarg") +        super(HyperlinkedRelatedField, self).__init__(*args, **kwargs) + +    def to_native(self, obj): +        view_name = self.view_name +        request = self.context.get('request', None) +        kwargs = {self.pk_url_kwarg: obj.pk} +        try: +            return reverse(view_name, kwargs=kwargs, request=request) +        except: +            pass + +        slug = getattr(obj, self.slug_field, None) + +        if not slug: +            raise ValidationError('Could not resolve URL for field using view name "%s"', view_name) + +        kwargs = {self.slug_url_kwarg: slug} +        try: +            return reverse(self.view_name, kwargs=kwargs, request=request) +        except: +            pass + +        kwargs = {self.pk_url_kwarg: obj.pk, self.slug_url_kwarg: slug} +        try: +            return reverse(self.view_name, kwargs=kwargs, request=request) +        except: +            pass + +        raise ValidationError('Could not resolve URL for field using view name "%s"', view_name) + +    def from_native(self, value): +        # Convert URL -> model instance pk +        try: +            match = resolve(value) +        except: +            raise ValidationError('Invalid hyperlink - No URL match') + +        if match.url_name != self.view_name: +            raise ValidationError('Invalid hyperlink - Incorrect URL match') + +        pk = match.kwargs.get(self.pk_url_kwarg, None) +        slug = match.kwargs.get(self.slug_url_kwarg, None) + +        # Try explicit primary key. +        if pk is not None: +            return pk +        # Next, try looking up by slug. +        elif slug is not None: +            slug_field = self.get_slug_field() +            queryset = self.queryset.filter(**{slug_field: slug}) +        # If none of those are defined, it's an error. +        else: +            raise ValidationError('Invalid hyperlink') + +        try: +            obj = queryset.get() +        except ObjectDoesNotExist: +            raise ValidationError('Invalid hyperlink - object does not exist.') +        return obj.pk + + +class ManyHyperlinkedRelatedField(ManyRelatedMixin, HyperlinkedRelatedField): +    pass + +  class HyperlinkedIdentityField(Field):      """      A field that represents the model's identity using a hyperlink. | 
