From aedf1d87abe8cda4ab5753fad21f34d99a0f1484 Mon Sep 17 00:00:00 2001 From: Chris Church Date: Mon, 28 Mar 2016 18:00:53 -0400 Subject: [PATCH 1/3] Fix help text in OPTIONS for common, read-only fields. Also fix display of None for foreign key fields in browsable API help. --- awx/api/metadata.py | 23 +++++++++++++++++-- awx/api/serializers.py | 22 ------------------ .../templates/api/_result_fields_common.md | 2 +- 3 files changed, 22 insertions(+), 25 deletions(-) diff --git a/awx/api/metadata.py b/awx/api/metadata.py index 6fccdb887d..f5c72fed97 100644 --- a/awx/api/metadata.py +++ b/awx/api/metadata.py @@ -6,7 +6,7 @@ from collections import OrderedDict # Django from django.core.exceptions import PermissionDenied from django.http import Http404 -from django.utils.encoding import force_text +from django.utils.encoding import force_text, smart_text # Django REST Framework from rest_framework import exceptions @@ -37,6 +37,25 @@ class Metadata(metadata.SimpleMetadata): if value is not None and value != '': field_info[attr] = force_text(value, strings_only=True) + # Update help text for common fields. + serializer = getattr(field, 'parent', None) + if serializer: + field_help_text = { + 'id': 'Database ID for this {}.', + 'name': 'Name of this {}.', + 'description': 'Optional description of this {}.', + 'type': 'Data type for this {}.', + 'url': 'URL for this {}.', + 'related': 'Data structure with URLs of related resources.', + 'summary_fields': 'Data structure with name/description for related resources.', + 'created': 'Timestamp when this {} was created.', + 'modified': 'Timestamp when this {} was last modified.', + } + if field.field_name in field_help_text: + opts = serializer.Meta.model._meta.concrete_model._meta + verbose_name = smart_text(opts.verbose_name) + field_info['help_text'] = field_help_text[field.field_name].format(verbose_name) + # Indicate if a field has a default value. # FIXME: Still isn't showing all default values? try: @@ -77,7 +96,7 @@ class Metadata(metadata.SimpleMetadata): # Update type of fields returned... if field.field_name == 'type': - field_info['type'] = 'multiple choice' + field_info['type'] = 'choice' elif field.field_name == 'url': field_info['type'] = 'string' elif field.field_name in ('related', 'summary_fields'): diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 0cca493965..9397b93b2e 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -326,7 +326,6 @@ class BaseSerializer(serializers.ModelSerializer): return obj.active def build_standard_field(self, field_name, model_field): - # DRF 3.3 serializers.py::build_standard_field() -> utils/field_mapping.py::get_field_kwargs() short circuits # when a Model's editable field is set to False. The short circuit skips choice rendering. # @@ -343,27 +342,6 @@ class BaseSerializer(serializers.ModelSerializer): if was_editable is False: field_kwargs['read_only'] = True - # Update help text for common fields. - opts = self.Meta.model._meta.concrete_model._meta - if field_name == 'id': - field_kwargs.setdefault('help_text', 'Database ID for this %s.' % smart_text(opts.verbose_name)) - elif field_name == 'name': - field_kwargs['help_text'] = 'Name of this %s.' % smart_text(opts.verbose_name) - elif field_name == 'description': - field_kwargs['help_text'] = 'Optional description of this %s.' % smart_text(opts.verbose_name) - elif field_name == 'type': - field_kwargs['help_text'] = 'Data type for this %s.' % smart_text(opts.verbose_name) - elif field_name == 'url': - field_kwargs['help_text'] = 'URL for this %s.' % smart_text(opts.verbose_name) - elif field_name == 'related': - field_kwargs['help_text'] = 'Data structure with URLs of related resources.' - elif field_name == 'summary_fields': - field_kwargs['help_text'] = 'Data structure with name/description for related resources.' - elif field_name == 'created': - field_kwargs['help_text'] = 'Timestamp when this %s was created.' % smart_text(opts.verbose_name) - elif field_name == 'modified': - field_kwargs['help_text'] = 'Timestamp when this %s was last modified.' % smart_text(opts.verbose_name) - # Pass model field default onto the serializer field if field is not read-only. if model_field.has_default() and not field_kwargs.get('read_only', False): field_kwargs['default'] = field_kwargs['initial'] = model_field.get_default() diff --git a/awx/api/templates/api/_result_fields_common.md b/awx/api/templates/api/_result_fields_common.md index 35fc3b55d1..43abefc534 100644 --- a/awx/api/templates/api/_result_fields_common.md +++ b/awx/api/templates/api/_result_fields_common.md @@ -1,6 +1,6 @@ {% for fn, fm in serializer_fields.items %}{% spaceless %} {% if not write_only or not fm.read_only %} -* `{{ fn }}`: {{ fm.help_text|capfirst }} ({{ fm.type }}{% if write_only and fm.required %}, required{% endif %}{% if write_only and fm.read_only %}, read-only{% endif %}{% if write_only and not fm.choices and not fm.required %}, default=`{% if fm.type == "string" or fm.type == "email" %}"{% firstof fm.default "" %}"{% else %}{{ fm.default }}{% endif %}`{% endif %}){% if fm.choices %}{% for c in fm.choices %} +* `{{ fn }}`: {{ fm.help_text|capfirst }} ({{ fm.type }}{% if write_only and fm.required %}, required{% endif %}{% if write_only and fm.read_only %}, read-only{% endif %}{% if write_only and not fm.choices and not fm.required %}, default=`{% if fm.type == "string" or fm.type == "email" %}"{% firstof fm.default "" %}"{% else %}{% if fm.type == "field" and not fm.default %}None{% else %}{{ fm.default }}{% endif %}{% endif %}`{% endif %}){% if fm.choices %}{% for c in fm.choices %} - `{% if c.0 == "" %}""{% else %}{{ c.0 }}{% endif %}`{% if c.1 != c.0 %}: {{ c.1 }}{% endif %}{% if write_only and c.0 == fm.default %} (default){% endif %}{% endfor %}{% endif %}{% endif %} {% endspaceless %} {% endfor %} From 895e082e08808bc86bfbf3545f74587f35f41976 Mon Sep 17 00:00:00 2001 From: Chris Church Date: Mon, 28 Mar 2016 18:19:20 -0400 Subject: [PATCH 2/3] Damn you, flake8. --- awx/api/serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 9397b93b2e..fb0b4525c1 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -20,7 +20,7 @@ from django.core.urlresolvers import reverse from django.core.exceptions import ObjectDoesNotExist, ValidationError as DjangoValidationError from django.db import models # from django.utils.translation import ugettext_lazy as _ -from django.utils.encoding import force_text, smart_text +from django.utils.encoding import force_text from django.utils.text import capfirst # Django REST Framework From 77f064d728b3c8f637107344264f8aa5146ec4b5 Mon Sep 17 00:00:00 2001 From: Chris Church Date: Tue, 29 Mar 2016 13:27:49 -0400 Subject: [PATCH 3/3] Keep model meta around in base serializer. --- awx/api/serializers.py | 1 + 1 file changed, 1 insertion(+) diff --git a/awx/api/serializers.py b/awx/api/serializers.py index fb0b4525c1..75c00a7db6 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -367,6 +367,7 @@ class BaseSerializer(serializers.ModelSerializer): # Update the message used for the unique validator to use capitalized # verbose name; keeps unique message the same as with DRF 2.x. + opts = self.Meta.model._meta.concrete_model._meta for validator in field_kwargs.get('validators', []): if isinstance(validator, validators.UniqueValidator): unique_error_message = model_field.error_messages.get('unique', None)