From b4b860b45b90769833a598d01d7ccf8950a2753b Mon Sep 17 00:00:00 2001 From: Ludwig Kraatz Date: Thu, 8 Nov 2012 14:54:01 +0100 Subject: [PATCH 1/4] moved field_mapping to be BrowsableAPIRenderer attr from local serializer_to_form_fields var to BrowsableAPIRenderer class attr in order to - allow customization when subclassing --- rest_framework/renderers.py | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py index 22fd6e740..2446f5b59 100644 --- a/rest_framework/renderers.py +++ b/rest_framework/renderers.py @@ -258,6 +258,22 @@ class BrowsableAPIRenderer(BaseRenderer): media_type = 'text/html' format = 'api' template = 'rest_framework/api.html' + field_mapping = { + serializers.FloatField: forms.FloatField, + serializers.IntegerField: forms.IntegerField, + serializers.DateTimeField: forms.DateTimeField, + serializers.DateField: forms.DateField, + serializers.EmailField: forms.EmailField, + serializers.CharField: forms.CharField, + serializers.ChoiceField: forms.ChoiceField, + serializers.BooleanField: forms.BooleanField, + serializers.PrimaryKeyRelatedField: forms.ChoiceField, + serializers.ManyPrimaryKeyRelatedField: forms.MultipleChoiceField, + serializers.SlugRelatedField: forms.ChoiceField, + serializers.ManySlugRelatedField: forms.MultipleChoiceField, + serializers.HyperlinkedRelatedField: forms.ChoiceField, + serializers.ManyHyperlinkedRelatedField: forms.MultipleChoiceField + } def get_default_renderer(self, view): """ @@ -306,22 +322,6 @@ class BrowsableAPIRenderer(BaseRenderer): return True def serializer_to_form_fields(self, serializer): - field_mapping = { - serializers.FloatField: forms.FloatField, - serializers.IntegerField: forms.IntegerField, - serializers.DateTimeField: forms.DateTimeField, - serializers.DateField: forms.DateField, - serializers.EmailField: forms.EmailField, - serializers.CharField: forms.CharField, - serializers.ChoiceField: forms.ChoiceField, - serializers.BooleanField: forms.BooleanField, - serializers.PrimaryKeyRelatedField: forms.ChoiceField, - serializers.ManyPrimaryKeyRelatedField: forms.MultipleChoiceField, - serializers.SlugRelatedField: forms.ChoiceField, - serializers.ManySlugRelatedField: forms.MultipleChoiceField, - serializers.HyperlinkedRelatedField: forms.ChoiceField, - serializers.ManyHyperlinkedRelatedField: forms.MultipleChoiceField - } fields = {} for k, v in serializer.get_fields(True).items(): @@ -347,7 +347,7 @@ class BrowsableAPIRenderer(BaseRenderer): kwargs['label'] = k try: - fields[k] = field_mapping[v.__class__](**kwargs) + fields[k] = self.field_mapping[v.__class__](**kwargs) except KeyError: if getattr(v, 'choices', None) is not None: fields[k] = forms.ChoiceField(**kwargs) From 08fef1ac81cdf3fb76b6cdf2bdd0896eca513c09 Mon Sep 17 00:00:00 2001 From: Ludwig Kraatz Date: Thu, 8 Nov 2012 14:58:53 +0100 Subject: [PATCH 2/4] Allowing custom Serializer Fields to have different BrowsableApiRendered Form Fields than CharField moved field_mapping from local serializer_to_form_fields var to BrowsableAPIRenderer class attr --- rest_framework/renderers.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py index 2446f5b59..748c1512b 100644 --- a/rest_framework/renderers.py +++ b/rest_framework/renderers.py @@ -259,21 +259,21 @@ class BrowsableAPIRenderer(BaseRenderer): format = 'api' template = 'rest_framework/api.html' field_mapping = { - serializers.FloatField: forms.FloatField, - serializers.IntegerField: forms.IntegerField, - serializers.DateTimeField: forms.DateTimeField, - serializers.DateField: forms.DateField, - serializers.EmailField: forms.EmailField, - serializers.CharField: forms.CharField, - serializers.ChoiceField: forms.ChoiceField, - serializers.BooleanField: forms.BooleanField, - serializers.PrimaryKeyRelatedField: forms.ChoiceField, - serializers.ManyPrimaryKeyRelatedField: forms.MultipleChoiceField, - serializers.SlugRelatedField: forms.ChoiceField, - serializers.ManySlugRelatedField: forms.MultipleChoiceField, - serializers.HyperlinkedRelatedField: forms.ChoiceField, - serializers.ManyHyperlinkedRelatedField: forms.MultipleChoiceField - } + serializers.FloatField: forms.FloatField, + serializers.IntegerField: forms.IntegerField, + serializers.DateTimeField: forms.DateTimeField, + serializers.DateField: forms.DateField, + serializers.EmailField: forms.EmailField, + serializers.CharField: forms.CharField, + serializers.ChoiceField: forms.ChoiceField, + serializers.BooleanField: forms.BooleanField, + serializers.PrimaryKeyRelatedField: forms.ChoiceField, + serializers.ManyPrimaryKeyRelatedField: forms.MultipleChoiceField, + serializers.SlugRelatedField: forms.ChoiceField, + serializers.ManySlugRelatedField: forms.MultipleChoiceField, + serializers.HyperlinkedRelatedField: forms.ChoiceField, + serializers.ManyHyperlinkedRelatedField: forms.MultipleChoiceField + } def get_default_renderer(self, view): """ From e9dfebc9c6c44bebc317611b84696582f502d1ac Mon Sep 17 00:00:00 2001 From: Ludwig Kraatz Date: Thu, 8 Nov 2012 15:27:32 +0100 Subject: [PATCH 3/4] clean support for view namespaces in as serializer attribute view name is prepended with namespace if existend --- rest_framework/serializers.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 4f68ada68..522878e47 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -480,6 +480,7 @@ class HyperlinkedModelSerializer(ModelSerializer): """ _options_class = HyperlinkedModelSerializerOptions _default_view_name = '%(model_name)s-detail' + _default_view_namespace = None # default: no namespace is prepend to view_name url = HyperlinkedIdentityField() @@ -497,7 +498,14 @@ class HyperlinkedModelSerializer(ModelSerializer): 'app_label': model_meta.app_label, 'model_name': model_meta.object_name.lower() } - return self._default_view_name % format_kwargs + view_name = self._default_view_name % format_kwargs + if self._default_view_namespace: + return "%(namespace)s:%(view)s" % { + 'view': view_name, + 'namespace': self._default_view_namespace + } + else: + return view_name def get_pk_field(self, model_field): return None From bf77a92c66ca97bda776eeef183c446e48b3c018 Mon Sep 17 00:00:00 2001 From: Ludwig Kraatz Date: Thu, 8 Nov 2012 16:27:38 +0100 Subject: [PATCH 4/4] Namespace for views specified in Serializers attr possible added _default_view_namespace attr of serializers, and view_namespace as hyperlinked fields init attr --- rest_framework/fields.py | 24 +++++++++++++++++++----- rest_framework/serializers.py | 29 ++++++++++++++++++++--------- 2 files changed, 39 insertions(+), 14 deletions(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index a4e29a30a..c4acb00a5 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -504,6 +504,7 @@ class HyperlinkedRelatedField(RelatedField): except: raise ValueError("Hyperlinked field requires 'view_name' kwarg") + self.view_namespace = kwargs.pop('view_namespace',None) self.slug_field = kwargs.pop('slug_field', 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) @@ -519,7 +520,13 @@ class HyperlinkedRelatedField(RelatedField): return self.slug_field def to_native(self, obj): - view_name = self.view_name + if self.view_namespace: + view_name = "%(namespace)s:%(view)s" % { + 'view':self.view_name, + 'namespace':self.view_namespace + } + else: + view_name = self.view_name request = self.context.get('request', None) format = self.format or self.context.get('format', None) kwargs = {self.pk_url_kwarg: obj.pk} @@ -535,13 +542,13 @@ class HyperlinkedRelatedField(RelatedField): kwargs = {self.slug_url_kwarg: slug} try: - return reverse(self.view_name, kwargs=kwargs, request=request, format=format) + return reverse(view_name, kwargs=kwargs, request=request, format=format) except: pass kwargs = {self.pk_url_kwarg: obj.pk, self.slug_url_kwarg: slug} try: - return reverse(self.view_name, kwargs=kwargs, request=request, format=format) + return reverse(view_name, kwargs=kwargs, request=request, format=format) except: pass @@ -608,6 +615,7 @@ class HyperlinkedIdentityField(Field): # TODO: Make view_name mandatory, and have the # HyperlinkedModelSerializer set it on-the-fly self.view_name = kwargs.pop('view_name', None) + self.view_namespace = kwargs.pop('view_namespace', None) self.format = kwargs.pop('format', None) self.slug_field = kwargs.pop('slug_field', self.slug_field) @@ -620,7 +628,13 @@ class HyperlinkedIdentityField(Field): def field_to_native(self, obj, field_name): request = self.context.get('request', None) format = self.format or self.context.get('format', None) + view_namespace = self.view_namespace or self.parent.opts.view_namespace view_name = self.view_name or self.parent.opts.view_name + if view_namespace: + view_name = "%(namespace)s:%(view)s" % { + 'view':view_name, + 'namespace':view_namespace + } kwargs = {self.pk_url_kwarg: obj.pk} try: return reverse(view_name, kwargs=kwargs, request=request, format=format) @@ -634,13 +648,13 @@ class HyperlinkedIdentityField(Field): kwargs = {self.slug_url_kwarg: slug} try: - return reverse(self.view_name, kwargs=kwargs, request=request, format=format) + return reverse(view_name, kwargs=kwargs, request=request, format=format) except: pass kwargs = {self.pk_url_kwarg: obj.pk, self.slug_url_kwarg: slug} try: - return reverse(self.view_name, kwargs=kwargs, request=request, format=format) + return reverse(view_name, kwargs=kwargs, request=request, format=format) except: pass diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 522878e47..23acfe0e0 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -473,6 +473,7 @@ class HyperlinkedModelSerializerOptions(ModelSerializerOptions): def __init__(self, meta): super(HyperlinkedModelSerializerOptions, self).__init__(meta) self.view_name = getattr(meta, 'view_name', None) + self.view_namespace = getattr(meta, 'view_namespace', None) class HyperlinkedModelSerializer(ModelSerializer): @@ -488,6 +489,8 @@ class HyperlinkedModelSerializer(ModelSerializer): super(HyperlinkedModelSerializer, self).__init__(*args, **kwargs) if self.opts.view_name is None: self.opts.view_name = self._get_default_view_name(self.opts.model) + if self.opts.view_namespace is None: + self.opts.view_namespace = self._get_default_view_namespace(self.opts.model) def _get_default_view_name(self, model): """ @@ -498,14 +501,21 @@ class HyperlinkedModelSerializer(ModelSerializer): 'app_label': model_meta.app_label, 'model_name': model_meta.object_name.lower() } - view_name = self._default_view_name % format_kwargs - if self._default_view_namespace: - return "%(namespace)s:%(view)s" % { - 'view': view_name, - 'namespace': self._default_view_namespace - } - else: - return view_name + return self._default_view_name % format_kwargs + + def _get_default_view_namespace(self, model): + """ + Return the view namespace to use if 'view_namespace' is not specified in 'Meta' + """ + if self._default_view_namespace is None: + return self._default_view_namespace + + model_meta = model._meta + format_kwargs = { + 'app_label': model_meta.app_label, + 'model_name': model_meta.object_name.lower() + } + return self._default_view_namespace % format_kwargs def get_pk_field(self, model_field): return None @@ -520,7 +530,8 @@ class HyperlinkedModelSerializer(ModelSerializer): queryset = rel._default_manager kwargs = { 'queryset': queryset, - 'view_name': self._get_default_view_name(rel) + 'view_name': self._get_default_view_name(rel), + 'view_namespace': self._get_default_view_namespace(rel) } if to_many: return ManyHyperlinkedRelatedField(**kwargs)