Beispiel #1
0
class FobiMetaData(SimpleMetadata):
    """Meta data for better representation of the form elements."""

    __mapping = copy.copy(SimpleMetadata.label_lookup.mapping)
    __mapping.update({
        MultipleChoiceWithMaxField: 'multiple choice',
        ContentImageField: 'content',
        ContentTextField: 'content',
        ContentVideoField: 'content',
    })

    label_lookup = ClassLookupDict(__mapping)

    def get_field_info(self, field):
        """Get field info.

        Given an instance of a serializer field, return a dictionary
        of metadata about it.
        """
        field_info = super(FobiMetaData, self).get_field_info(field)

        for __key in ['initial', 'max_value', 'min_value']:
            __val = getattr(field, __key, None)
            if __val not in (None, ''):
                field_info[__key] = __val

        field_metadata = field.root.get_fields_metadata().get(
            field.field_name, {})
        if field_metadata:
            for __k, __val in field_metadata.items():
                if __val not in (None, ''):
                    field_info[__k] = __val

        return field_info
    def get_polymorphic_fields(self):
        """
        returns a dictionary of {field_name: field_instance} for polymorphic
        fields.
        """
        if not self.polymorphic_class_mapping:
            self._build_polymorphic_field_mapping()

        polymorphic_fields = OrderedDict()
        try:
            field_mapping = self._field_mapping
        except AttributeError:
            field_mapping = ClassLookupDict(self.serializer_field_mapping)

        for _, subclass in six.iteritems(self.polymorphic_class_mapping):
            for field in subclass.fields:
                rest_field = field_mapping[field]
                kwargs = get_field_kwargs(field.name, field)
                if 'choices' in kwargs:
                    rest_field = serializers.ChoiceField
                if not issubclass(rest_field, serializers.ModelField):
                    kwargs.pop('model_field', None)
                if (not issubclass(rest_field, serializers.CharField) and
                        not issubclass(rest_field, serializers.ChoiceField)):
                    kwargs.pop('allow_blank', None)

                polymorphic_fields[field.name] = rest_field(**kwargs)

        return polymorphic_fields
Beispiel #3
0
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        fields_we_do_not_want = [
            'id', 'email', 'password', 'is_staff', 'is_superuser', 'is_active',
            'date_joined', 'last_login'
        ]
        important_signup_fields = get_user_signup_fields()
        model = get_user_model()
        # Using ModelSerializers model field to serializer field mapper
        # to build missing fields for signup serializer
        original_mapping = serializers.ModelSerializer.serializer_field_mapping

        try:
            from phonenumber_field.modelfields import PhoneNumberField
            original_mapping.update({PhoneNumberField: serializers.CharField})
        except ImportError:
            pass

        mapping = ClassLookupDict(original_mapping)
        for model_field in model._meta.fields:
            if model_field.name in important_signup_fields and model_field.name not in fields_we_do_not_want:
                field_kwargs = {'required': True}
                if 'phone' in model_field.name:
                    field_kwargs['validators'] = [phonenumber_validation]
                self._declared_fields[model_field.name] = mapping[model_field](
                    **field_kwargs)
class GeoModelSerializer(ModelSerializer):
    """
    A subclass of DFR ModelSerializer that adds support
    for GeoDjango fields to be serialized as GeoJSON
    compatible data
    """
    _field_mapping = ClassLookupDict(_geo_field_mapping)
class FormMetadata(SimpleMetadata):
    """
    Sirve para mostrar el schema de un form al hacer una petición
    con el método OPTIONS.
    """
    label_lookup = ClassLookupDict({
        serializers.Field: 'field',
        serializers.BooleanField: 'checkbox',
        serializers.NullBooleanField: 'boolean',
        serializers.CharField: 'text',
        serializers.UUIDField: 'text',
        serializers.URLField: 'text',
        serializers.EmailField: 'email',
        serializers.RegexField: 'text',
        serializers.SlugField: 'text',
        serializers.IntegerField: 'number',
        serializers.FloatField: 'number',
        serializers.DecimalField: 'number',
        serializers.DateField: 'date',
        serializers.DateTimeField: 'datetime',
        serializers.TimeField: 'time',
        serializers.ChoiceField: 'choice',
        serializers.MultipleChoiceField: 'multiple choice',
        serializers.FileField: 'file',
        serializers.ImageField: 'image',
        serializers.ListField: 'list',
        serializers.DictField: 'nested object',
        serializers.Serializer: 'nested object',
    })

    def get_field_info(self, field):
        """
        Retorna la metadata de un field de serializer, en caso de que el campo
        sea de tipo serializer.Field, significa que es una foreign key
        por lo que se agregan las opciones de dicho campo
        """
        field_info = super().get_field_info(field)
        field_info['name'] = field.field_name
        if field_info['type'] == 'field':
            try:
                options = field.queryset.filter(is_active=True)
            except AttributeError:
                options = []
            choices = []
            for choice in options:
                choices.append({'value': choice.id, 'text': choice.name})
            field_info['choices'] = choices
        elif field_info['type'] == 'choice':
            # Se cambia el nombre de las keys para que se use en
            # el frontend
            choices = []
            for choice in field_info['choices']:
                choices.append({
                    'value': choice['value'],
                    'text': choice['display_name']
                })
            field_info['choices'] = choices
        return field_info
Beispiel #6
0
 def label_lookup(self):
     mapping = SimpleMetadata.label_lookup.mapping
     mapping.update({
         AuthorField: "string",
         CopySerializer: "array",
         EntrySerializer: "array",
         ImageFieldSerializer: "image",
         RichTextField: "richtext"
     })
     return ClassLookupDict(mapping)
 def __init__(self, model, field_names=None, package=None):
     self.model = model
     self.field_names = field_names
     if not package:
         package = model.__name__.lower()
     self.package = package
     self.type_mapping = ClassLookupDict(self.type_mapping)
     # Retrieve metadata about fields & relationships on the model class.
     self.field_info = model_meta.get_field_info(model)
     self._writer = _CodeWriter()
Beispiel #8
0
def get_serializer_class(field):
    bsm_custom = {
        **ModelSerializer.serializer_field_mapping,
        JSONField: drf_field.JSONField,
        OriginJSONField: DrfJSONField,
        models.BooleanField: fields.BooleanField,
        models.DateTimeField: fields.DateTimeField,
    }
    try:
        return ClassLookupDict(bsm_custom)[field]
    except:
        return fields.CharField
Beispiel #9
0
    def get_fields(self):
        """Build and return a list with all the serializer fields"""
        fields = OrderedDict()
        declared_fields = self.get_declared_fields()
        field_classes = ClassLookupDict(self.serializer_field_mapping)
        fields_extra_kwargs = getattr(self.Meta, 'extra_kwargs', {})

        for opt_name, opt_class in iter(
                self.instance.declared_options.items()):
            # Add or exclude any fields declared in Meta.fields or .exclude
            if self.meta_fields and self.meta_fields != ALL_FIELDS and opt_name not in self.meta_fields:
                continue
            if self.meta_exclude and opt_name in self.meta_exclude:
                continue
            # If the field is already declared on the serializer, use it
            if opt_name in declared_fields:
                fields[opt_name] = declared_fields[opt_name]
                continue
            # Create other new fields based on conf option definition
            extra_kwargs = fields_extra_kwargs.get(opt_name, {})
            if getattr(opt_class, 'choices', None) is not None:
                # Every field with choices defined, will be represented as a ChoiceField
                fields[opt_name] = serializers.ChoiceField(
                    choices=opt_class.choices, **extra_kwargs)
            else:
                self.get_field_kwargs(option_instance=opt_class,
                                      extra_kwargs=extra_kwargs)
                # List or Dict field with child
                if isinstance(opt_class, (
                        options.ListOpt,
                        options.DictOpt,
                )) and 'child' not in extra_kwargs:
                    child_kwargs = {}
                    if getattr(opt_class.item_type, 'choices',
                               None) is not None:
                        child = serializers.ChoiceField(
                            choices=opt_class.item_type.choices)
                    else:
                        child = field_classes[opt_class.item_type]
                        self.get_field_kwargs(
                            option_instance=opt_class.item_type,
                            extra_kwargs=child_kwargs)
                    extra_kwargs['child'] = child(**child_kwargs)

                fields[opt_name] = field_classes[opt_class](**extra_kwargs)

        for field_name, field_instance in iter(declared_fields.items()):
            # NOTE(tomo): Add any serializer declared fields that are not part of the config class
            if field_name not in fields and (self.meta_fields == ALL_FIELDS or
                                             field_name in self.meta_fields):
                fields[field_name] = field_instance
        return fields
Beispiel #10
0
    def build_standard_field(self, field_name, model_field):
        """
        Create regular model fields.
        """
        field_mapping = ClassLookupDict(self.serializer_field_mapping)

        field_class = field_mapping[model_field]
        field_kwargs = self.get_field_kwargs(field_name, model_field)
        if 'choices' in field_kwargs:
            # Fields with choices get coerced into `ChoiceField`
            # instead of using their regular typed field.
            field_class = self.serializer_choice_field
            # Some model fields may introduce kwargs that would not be valid
            # for the choice field. We need to strip these out.
            # Eg. models.DecimalField(max_digits=3, decimal_places=1,
            #                         choices=DECIMAL_CHOICES)

            valid_kwargs = {
                'read_only', 'write_only', 'required', 'default', 'initial',
                'source', 'label', 'help_text', 'style', 'error_messages',
                'validators', 'allow_null', 'allow_blank', 'choices'
            }
            for key in list(field_kwargs.keys()):
                if key not in valid_kwargs:
                    field_kwargs.pop(key)

        if not issubclass(field_class, ModelField):
            # `model_field` is only valid for the fallback case of
            # `ModelField`, which is used when no other typed field
            # matched to the model field.
            field_kwargs.pop('model_field', None)

        if not issubclass(field_class, CharField) and not \
                issubclass(field_class, ChoiceField):
            # `allow_blank` is only valid for textual fields.
            field_kwargs.pop('allow_blank', None)

        if postgres_fields and isinstance(model_field,
                                          postgres_fields.ArrayField):
            # Populate the `child` argument on `ListField` instances generated
            # for the PostgrSQL specfic `ArrayField`.
            child_model_field = model_field.base_field
            child_field_class, child_field_kwargs = self.build_standard_field(
                'child', child_model_field)
            field_kwargs['child'] = child_field_class(**child_field_kwargs)

        return field_class, field_kwargs
    def build_standard_field(self, field_name, model_field):
        field_mapping = ClassLookupDict(self.serializer_field_mapping)

        field_class = field_mapping[model_field]
        field_kwargs = get_field_kwargs(field_name, model_field)

        if 'choices' in field_kwargs:
            # Fields with choices get coerced into `ChoiceField`
            # instead of using their regular typed field.
            field_class = self.serializer_choice_field
            # Some model fields may introduce kwargs that would not be valid
            # for the choice field. We need to strip these out.
            # Eg. models.DecimalField(max_digits=3, decimal_places=1, choices=DECIMAL_CHOICES)
            valid_kwargs = set(
                ('read_only', 'write_only', 'required', 'default', 'initial',
                 'source', 'label', 'help_text', 'style', 'error_messages',
                 'validators', 'allow_null', 'allow_blank', 'choices'))
            for key in list(field_kwargs.keys()):
                if key not in valid_kwargs:
                    field_kwargs.pop(key)

        if 'regex' in field_kwargs:
            field_class = drf_fields.RegexField

        if not issubclass(field_class, drfm_fields.DocumentField):
            # `model_field` is only valid for the fallback case of
            # `ModelField`, which is used when no other typed field
            # matched to the model field.
            field_kwargs.pop('model_field', None)

        if not issubclass(field_class,
                          drf_fields.CharField) and not issubclass(
                              field_class, drf_fields.ChoiceField):
            # `allow_blank` is only valid for textual fields.
            field_kwargs.pop('allow_blank', None)

        if field_class is drf_fields.BooleanField and field_kwargs.get(
                'allow_null', False):
            field_kwargs.pop('allow_null', None)
            field_kwargs.pop('default', None)
            field_class = drf_fields.NullBooleanField

        return field_class, field_kwargs
Beispiel #12
0
class NoPermissionMetadata(SimpleMetadata):

    label_lookup = ClassLookupDict({
        serializers.Field: 'field',
        serializers.BooleanField: 'boolean',
        serializers.NullBooleanField: 'boolean',
        serializers.CharField: 'string',
        serializers.URLField: 'url',
        serializers.EmailField: 'email',
        serializers.RegexField: 'regex',
        serializers.SlugField: 'slug',
        serializers.IntegerField: 'integer',
        serializers.FloatField: 'float',
        serializers.DecimalField: 'decimal',
        serializers.DateField: 'date',
        serializers.DateTimeField: 'datetime',
        serializers.TimeField: 'time',
        serializers.ChoiceField: 'choice',
        serializers.MultipleChoiceField: 'multiple choice',
        serializers.FileField: 'file upload',
        serializers.ImageField: 'image upload',
        serializers.ListField: 'list',
        serializers.DictField: 'nested object',
        serializers.Serializer: 'nested object',
        c_fields.BooleanField: 'boolean',
    })

    def determine_actions(self, request, view):
        """
        For generic class based views we return information about
        the fields that are accepted for 'PUT' and 'POST' methods.
        """
        actions = {}
        for method in {'PUT', 'POST'} & set(view.allowed_methods):
            view.request = clone_request(request, method)
            serializer = view.get_serializer()
            actions[method] = self.get_serializer_info(serializer)
            view.request = request
        return actions
Beispiel #13
0
class FobiMetaData(SimpleMetadata):
    """Meta data for better representation of the form elements."""

    __mapping = copy.copy(SimpleMetadata.label_lookup.mapping)
    __mapping.update({
        MultipleChoiceWithMaxField: 'multiple choice',
        ContentImageField: 'content',
        ContentTextField: 'content',
        ContentVideoField: 'content',
    })

    label_lookup = ClassLookupDict(__mapping)

    def get_field_info(self, field):
        """Get field info.

        Given an instance of a serializer field, return a dictionary
        of metadata about it.
        """
        field_info = super(FobiMetaData, self).get_field_info(field)
        if isinstance(
                field,
            (ContentTextField, ContentImageField, ContentVideoField)):
            field_info['type'] = 'content'

            if isinstance(field, ContentTextField):
                field_info['contenttype'] = 'text'
                field_info['content'] = field.initial
                field_info['raw'] = field.raw_data
            elif isinstance(field, ContentImageField):
                field_info['contenttype'] = 'image'
                field_info['content'] = field.initial
                field_info['raw'] = field.raw_data
            else:
                field_info['contenttype'] = 'video'
                field_info['content'] = field.initial
                field_info['raw'] = field.raw_data

        return field_info
Beispiel #14
0
from rest_framework.request import clone_request
from rest_framework.response import Response
from rest_framework.settings import api_settings
from rest_framework.utils import formatting
from rest_framework.utils.field_mapping import ClassLookupDict
from rest_framework.utils.model_meta import _get_pk
from rest_framework.views import APIView

header_regex = re.compile('^[a-zA-Z][0-9A-Za-z_]*:')

types_lookup = ClassLookupDict({
    serializers.Field: 'string',
    serializers.IntegerField: 'integer',
    serializers.FloatField: 'number',
    serializers.DecimalField: 'number',
    serializers.BooleanField: 'boolean',
    serializers.FileField: 'file',
    serializers.MultipleChoiceField: 'array',
    serializers.ManyRelatedField: 'array',
    serializers.Serializer: 'object',
    serializers.ListSerializer: 'array'
})


def common_path(paths):
    split_paths = [path.strip('/').split('/') for path in paths]
    s1 = min(split_paths)
    s2 = max(split_paths)
    common = s1
    for i, c in enumerate(s1):
        if c != s2[i]:
            common = s1[:i]
class ModelSerializer(Serializer):
    """
    A `ModelSerializer` is just a regular `Serializer`, except that:

    * A set of default fields are automatically populated.
    * A set of default validators are automatically populated.
    * Default `.create()` and `.update()` implementations are provided.
    """
    _field_mapping = ClassLookupDict({
        models.AutoField: IntegerField,
        models.BigIntegerField: IntegerField,
        models.BooleanField: BooleanField,
        models.CharField: CharField,
        models.CommaSeparatedIntegerField: CharField,
        models.DateField: DateField,
        models.DateTimeField: DateTimeField,
        models.DecimalField: DecimalField,
        models.EmailField: EmailField,
        models.Field: ModelField,
        models.FileField: FileField,
        models.FloatField: FloatField,
        models.ImageField: ImageField,
        models.IntegerField: IntegerField,
        models.NullBooleanField: NullBooleanField,
        models.PositiveIntegerField: IntegerField,
        models.PositiveSmallIntegerField: IntegerField,
        models.SlugField: SlugField,
        models.SmallIntegerField: IntegerField,
        models.TextField: CharField,
        models.TimeField: TimeField,
        models.URLField: URLField,
    })
    _related_class = PrimaryKeyRelatedField

    def create(self, validated_attrs):
        # Check that the user isn't trying to handle a writable nested field.
        # If we don't do this explicitly they'd likely get a confusing
        # error at the point of calling `Model.objects.create()`.
        assert not any(
            isinstance(field, BaseSerializer) and not field.read_only
            for field in self.fields.values()
        ), (
            'The `.create()` method does not suport nested writable fields '
            'by default. Write an explicit `.create()` method for serializer '
            '`%s.%s`, or set `read_only=True` on nested serializer fields.' %
            (self.__class__.__module__, self.__class__.__name__)
        )

        ModelClass = self.Meta.model

        # Remove many-to-many relationships from validated_attrs.
        # They are not valid arguments to the default `.create()` method,
        # as they require that the instance has already been saved.
        info = model_meta.get_field_info(ModelClass)
        many_to_many = {}
        for field_name, relation_info in info.relations.items():
            if relation_info.to_many and (field_name in validated_attrs):
                many_to_many[field_name] = validated_attrs.pop(field_name)

        instance = ModelClass.objects.create(**validated_attrs)

        # Save many-to-many relationships after the instance is created.
        if many_to_many:
            for field_name, value in many_to_many.items():
                setattr(instance, field_name, value)

        return instance

    def update(self, instance, validated_attrs):
        assert not any(
            isinstance(field, BaseSerializer) and not field.read_only
            for field in self.fields.values()
        ), (
            'The `.update()` method does not suport nested writable fields '
            'by default. Write an explicit `.update()` method for serializer '
            '`%s.%s`, or set `read_only=True` on nested serializer fields.' %
            (self.__class__.__module__, self.__class__.__name__)
        )

        for attr, value in validated_attrs.items():
            setattr(instance, attr, value)
        instance.save()
        return instance

    def get_validators(self):
        field_names = set([
            field.source for field in self.fields.values()
            if (field.source != '*') and ('.' not in field.source)
        ])

        validators = getattr(getattr(self, 'Meta', None), 'validators', [])
        model_class = self.Meta.model

        # Note that we make sure to check `unique_together` both on the
        # base model class, but also on any parent classes.
        for parent_class in [model_class] + list(model_class._meta.parents.keys()):
            for unique_together in parent_class._meta.unique_together:
                if field_names.issuperset(set(unique_together)):
                    validator = UniqueTogetherValidator(
                        queryset=parent_class._default_manager,
                        fields=unique_together
                    )
                    validators.append(validator)

        # Add any unique_for_date/unique_for_month/unique_for_year constraints.
        info = model_meta.get_field_info(model_class)
        for field_name, field in info.fields_and_pk.items():
            if field.unique_for_date and field_name in field_names:
                validator = UniqueForDateValidator(
                    queryset=model_class._default_manager,
                    field=field_name,
                    date_field=field.unique_for_date
                )
                validators.append(validator)

            if field.unique_for_month and field_name in field_names:
                validator = UniqueForMonthValidator(
                    queryset=model_class._default_manager,
                    field=field_name,
                    date_field=field.unique_for_month
                )
                validators.append(validator)

            if field.unique_for_year and field_name in field_names:
                validator = UniqueForYearValidator(
                    queryset=model_class._default_manager,
                    field=field_name,
                    date_field=field.unique_for_year
                )
                validators.append(validator)

        return validators

    def get_fields(self):
        declared_fields = copy.deepcopy(self._declared_fields)

        ret = OrderedDict()
        model = getattr(self.Meta, 'model')
        fields = getattr(self.Meta, 'fields', None)
        exclude = getattr(self.Meta, 'exclude', None)
        depth = getattr(self.Meta, 'depth', 0)
        extra_kwargs = getattr(self.Meta, 'extra_kwargs', {})

        assert not (fields and exclude), "Cannot set both 'fields' and 'exclude'."

        extra_kwargs = self._include_additional_options(extra_kwargs)

        # Retrieve metadata about fields & relationships on the model class.
        info = model_meta.get_field_info(model)

        # Use the default set of field names if none is supplied explicitly.
        if fields is None:
            fields = self._get_default_field_names(declared_fields, info)
            exclude = getattr(self.Meta, 'exclude', None)
            if exclude is not None:
                for field_name in exclude:
                    fields.remove(field_name)

        # Determine the set of model fields, and the fields that they map to.
        # We actually only need this to deal with the slightly awkward case
        # of supporting `unique_for_date`/`unique_for_month`/`unique_for_year`.
        model_field_mapping = {}
        for field_name in fields:
            if field_name in declared_fields:
                field = declared_fields[field_name]
                source = field.source or field_name
            else:
                try:
                    source = extra_kwargs[field_name]['source']
                except KeyError:
                    source = field_name
            # Model fields will always have a simple source mapping,
            # they can't be nested attribute lookups.
            if '.' not in source and source != '*':
                model_field_mapping[source] = field_name

        # Determine if we need any additional `HiddenField` or extra keyword
        # arguments to deal with `unique_for` dates that are required to
        # be in the input data in order to validate it.
        hidden_fields = {}
        unique_constraint_names = set()

        for model_field_name, field_name in model_field_mapping.items():
            try:
                model_field = model._meta.get_field(model_field_name)
            except FieldDoesNotExist:
                continue

            # Include each of the `unique_for_*` field names.
            unique_constraint_names |= set([
                model_field.unique_for_date,
                model_field.unique_for_month,
                model_field.unique_for_year
            ])

        unique_constraint_names -= set([None])

        # Include each of the `unique_together` field names,
        # so long as all the field names are included on the serializer.
        for parent_class in [model] + list(model._meta.parents.keys()):
            for unique_together_list in parent_class._meta.unique_together:
                if set(fields).issuperset(set(unique_together_list)):
                    unique_constraint_names |= set(unique_together_list)

        # Now we have all the field names that have uniqueness constraints
        # applied, we can add the extra 'required=...' or 'default=...'
        # arguments that are appropriate to these fields, or add a `HiddenField` for it.
        for unique_constraint_name in unique_constraint_names:
            # Get the model field that is refered too.
            unique_constraint_field = model._meta.get_field(unique_constraint_name)

            if getattr(unique_constraint_field, 'auto_now_add', None):
                default = CreateOnlyDefault(timezone.now)
            elif getattr(unique_constraint_field, 'auto_now', None):
                default = timezone.now
            elif unique_constraint_field.has_default():
                default = unique_constraint_field.default
            else:
                default = empty

            if unique_constraint_name in model_field_mapping:
                # The corresponding field is present in the serializer
                if unique_constraint_name not in extra_kwargs:
                    extra_kwargs[unique_constraint_name] = {}
                if default is empty:
                    if 'required' not in extra_kwargs[unique_constraint_name]:
                        extra_kwargs[unique_constraint_name]['required'] = True
                else:
                    if 'default' not in extra_kwargs[unique_constraint_name]:
                        extra_kwargs[unique_constraint_name]['default'] = default
            elif default is not empty:
                # The corresponding field is not present in the,
                # serializer. We have a default to use for it, so
                # add in a hidden field that populates it.
                hidden_fields[unique_constraint_name] = HiddenField(default=default)

        # Now determine the fields that should be included on the serializer.
        for field_name in fields:
            if field_name in declared_fields:
                # Field is explicitly declared on the class, use that.
                ret[field_name] = declared_fields[field_name]
                continue

            elif field_name in info.fields_and_pk:
                # Create regular model fields.
                model_field = info.fields_and_pk[field_name]
                field_cls = self._field_mapping[model_field]
                kwargs = get_field_kwargs(field_name, model_field)
                if 'choices' in kwargs:
                    # Fields with choices get coerced into `ChoiceField`
                    # instead of using their regular typed field.
                    field_cls = ChoiceField
                if not issubclass(field_cls, ModelField):
                    # `model_field` is only valid for the fallback case of
                    # `ModelField`, which is used when no other typed field
                    # matched to the model field.
                    kwargs.pop('model_field', None)
                if not issubclass(field_cls, CharField):
                    # `allow_blank` is only valid for textual fields.
                    kwargs.pop('allow_blank', None)

            elif field_name in info.relations:
                # Create forward and reverse relationships.
                relation_info = info.relations[field_name]
                if depth:
                    field_cls = self._get_nested_class(depth, relation_info)
                    kwargs = get_nested_relation_kwargs(relation_info)
                else:
                    field_cls = self._related_class
                    kwargs = get_relation_kwargs(field_name, relation_info)
                    # `view_name` is only valid for hyperlinked relationships.
                    if not issubclass(field_cls, HyperlinkedRelatedField):
                        kwargs.pop('view_name', None)

            elif hasattr(model, field_name):
                # Create a read only field for model methods and properties.
                field_cls = ReadOnlyField
                kwargs = {}

            elif field_name == api_settings.URL_FIELD_NAME:
                # Create the URL field.
                field_cls = HyperlinkedIdentityField
                kwargs = get_url_kwargs(model)

            else:
                raise ImproperlyConfigured(
                    'Field name `%s` is not valid for model `%s`.' %
                    (field_name, model.__class__.__name__)
                )

            # Check that any fields declared on the class are
            # also explicity included in `Meta.fields`.
            missing_fields = set(declared_fields.keys()) - set(fields)
            if missing_fields:
                missing_field = list(missing_fields)[0]
                raise ImproperlyConfigured(
                    'Field `%s` has been declared on serializer `%s`, but '
                    'is missing from `Meta.fields`.' %
                    (missing_field, self.__class__.__name__)
                )

            # Populate any kwargs defined in `Meta.extra_kwargs`
            extras = extra_kwargs.get(field_name, {})
            if extras.get('read_only', False):
                for attr in [
                    'required', 'default', 'allow_blank', 'allow_null',
                    'min_length', 'max_length', 'min_value', 'max_value',
                    'validators', 'queryset'
                ]:
                    kwargs.pop(attr, None)

            if extras.get('default') and kwargs.get('required') is False:
                kwargs.pop('required')

            kwargs.update(extras)

            # Create the serializer field.
            ret[field_name] = field_cls(**kwargs)

        for field_name, field in hidden_fields.items():
            ret[field_name] = field

        return ret

    def _include_additional_options(self, extra_kwargs):
        read_only_fields = getattr(self.Meta, 'read_only_fields', None)
        if read_only_fields is not None:
            for field_name in read_only_fields:
                kwargs = extra_kwargs.get(field_name, {})
                kwargs['read_only'] = True
                extra_kwargs[field_name] = kwargs

        # These are all pending deprecation.
        write_only_fields = getattr(self.Meta, 'write_only_fields', None)
        if write_only_fields is not None:
            warnings.warn(
                "The `Meta.write_only_fields` option is pending deprecation. "
                "Use `Meta.extra_kwargs={<field_name>: {'write_only': True}}` instead.",
                PendingDeprecationWarning,
                stacklevel=3
            )
            for field_name in write_only_fields:
                kwargs = extra_kwargs.get(field_name, {})
                kwargs['write_only'] = True
                extra_kwargs[field_name] = kwargs

        view_name = getattr(self.Meta, 'view_name', None)
        if view_name is not None:
            warnings.warn(
                "The `Meta.view_name` option is pending deprecation. "
                "Use `Meta.extra_kwargs={'url': {'view_name': ...}}` instead.",
                PendingDeprecationWarning,
                stacklevel=3
            )
            kwargs = extra_kwargs.get(api_settings.URL_FIELD_NAME, {})
            kwargs['view_name'] = view_name
            extra_kwargs[api_settings.URL_FIELD_NAME] = kwargs

        lookup_field = getattr(self.Meta, 'lookup_field', None)
        if lookup_field is not None:
            warnings.warn(
                "The `Meta.lookup_field` option is pending deprecation. "
                "Use `Meta.extra_kwargs={'url': {'lookup_field': ...}}` instead.",
                PendingDeprecationWarning,
                stacklevel=3
            )
            kwargs = extra_kwargs.get(api_settings.URL_FIELD_NAME, {})
            kwargs['lookup_field'] = lookup_field
            extra_kwargs[api_settings.URL_FIELD_NAME] = kwargs

        return extra_kwargs

    def _get_default_field_names(self, declared_fields, model_info):
        return (
            [model_info.pk.name] +
            list(declared_fields.keys()) +
            list(model_info.fields.keys()) +
            list(model_info.forward_relations.keys())
        )

    def _get_nested_class(self, nested_depth, relation_info):
        class NestedSerializer(ModelSerializer):
            class Meta:
                model = relation_info.related
                depth = nested_depth
        return NestedSerializer
Beispiel #16
0
default_style = ClassLookupDict({
    serializers.Field: {
        "tag": "input",
        "input_type": "text"
    },
    serializers.EmailField: {
        "tag": "input",
        "input_type": "email"
    },
    serializers.URLField: {
        "tag": "input",
        "input_type": "url"
    },
    serializers.IntegerField: {
        "tag": "input",
        "input_type": "number"
    },
    serializers.FloatField: {
        "tag": "input",
        "input_type": "number"
    },
    serializers.DateTimeField: {
        "tag": "input",
        "input_type": "datetime-local"
    },
    serializers.DateField: {
        "tag": "input",
        "input_type": "date"
    },
    serializers.TimeField: {
        "tag": "input",
        "input_type": "time"
    },
    serializers.FileField: {
        "tag": "input",
        "input_type": "file"
    },
    serializers.BooleanField: {
        "tag": "checkbox"
    },
    serializers.ChoiceField: {
        "tag": "select",
    },  # Also valid: 'radio'
    serializers.MultipleChoiceField: {
        "tag": "select_multiple",  # Also valid: 'checkbox_multiple'
    },
    serializers.RelatedField: {
        "tag": "select",
    },  # Also valid: 'radio'
    serializers.ManyRelatedField: {
        "tag": "select multiple",  # Also valid: 'checkbox_multiple'
    },
    serializers.Serializer: {
        "tag": "fieldset"
    },
    serializers.ListSerializer: {
        "tag": "list_fieldset"
    },
    serializers.ListField: {
        "tag": "list_field"
    },
    serializers.DictField: {
        "tag": "dict_field"
    },
    serializers.FilePathField: {
        "tag": "input",
        "input_type": "file"
    },
    serializers.JSONField: {
        "tag": "textarea",
    },
})
Beispiel #17
0
INPUT_TYPE_DATETIME = "datetime"
INPUT_TYPE_CHOICE = "select"
INPUT_TYPE_MULTI_CHOICE = "multi_select"

label_lookup = ClassLookupDict({
    serializers.Field: "field",
    serializers.BooleanField: "boolean",
    serializers.NullBooleanField: "boolean",
    serializers.CharField: "string",
    serializers.UUIDField: "string",
    serializers.URLField: "url",
    serializers.EmailField: "email",
    serializers.RegexField: "regex",
    serializers.SlugField: "slug",
    serializers.IntegerField: "integer",
    serializers.FloatField: "float",
    serializers.DecimalField: "decimal",
    serializers.DateField: "date",
    serializers.DateTimeField: "datetime",
    serializers.TimeField: "time",
    serializers.ChoiceField: "choice",
    serializers.MultipleChoiceField: "multiple choice",
    serializers.FileField: "file upload",
    serializers.ImageField: "image upload",
    serializers.ListField: "list",
    serializers.DictField: "nested object",
    serializers.Serializer: "nested object",
})

search_type_lookup = ClassLookupDict({
    serializers.CharField:
    INPUT_TYPE_INPUT,
class JSONAPIMetadata(SimpleMetadata):
    """
    This is the JSON:API metadata implementation.
    It returns an ad-hoc set of information about the view.
    There are not any formalized standards for `OPTIONS` responses
    for us to base this on.
    """
    type_lookup = ClassLookupDict({
        serializers.Field: 'GenericField',
        serializers.RelatedField: 'Relationship',
        serializers.BooleanField: 'Boolean',
        serializers.NullBooleanField: 'Boolean',
        serializers.CharField: 'String',
        serializers.URLField: 'URL',
        serializers.EmailField: 'Email',
        serializers.RegexField: 'Regex',
        serializers.SlugField: 'Slug',
        serializers.IntegerField: 'Integer',
        serializers.FloatField: 'Float',
        serializers.DecimalField: 'Decimal',
        serializers.DateField: 'Date',
        serializers.DateTimeField: 'DateTime',
        serializers.TimeField: 'Time',
        serializers.ChoiceField: 'Choice',
        serializers.MultipleChoiceField: 'MultipleChoice',
        serializers.FileField: 'File',
        serializers.ImageField: 'Image',
        serializers.ListField: 'List',
        serializers.DictField: 'Dict',
        serializers.Serializer: 'Serializer',
    })

    try:
        relation_type_lookup = ClassLookupDict({
            related.ManyToManyDescriptor:
            'ManyToMany',
            related.ReverseManyToOneDescriptor:
            'OneToMany',
            related.ForwardManyToOneDescriptor:
            'ManyToOne',
        })
    except AttributeError:
        relation_type_lookup = ClassLookupDict({
            related.ManyRelatedObjectsDescriptor:
            'ManyToMany',
            related.ReverseManyRelatedObjectsDescriptor:
            'ManyToMany',
            related.ForeignRelatedObjectsDescriptor:
            'OneToMany',
            related.ReverseSingleRelatedObjectDescriptor:
            'ManyToOne',
        })

    def determine_metadata(self, request, view):
        metadata = OrderedDict()
        metadata['name'] = view.get_view_name()
        metadata['description'] = view.get_view_description()
        metadata['renders'] = [
            renderer.media_type for renderer in view.renderer_classes
        ]
        metadata['parses'] = [
            parser.media_type for parser in view.parser_classes
        ]
        metadata['allowed_methods'] = view.allowed_methods
        if hasattr(view, 'get_serializer'):
            actions = self.determine_actions(request, view)
            if actions:
                metadata['actions'] = actions
        return metadata

    def get_serializer_info(self, serializer):
        """
        Given an instance of a serializer, return a dictionary of metadata
        about its fields.
        """
        if hasattr(serializer, 'child'):
            # If this is a `ListSerializer` then we want to examine the
            # underlying child serializer instance instead.
            serializer = serializer.child

        # Remove the URL field if present
        serializer.fields.pop(api_settings.URL_FIELD_NAME, None)

        return OrderedDict([
            (format_value(field_name), self.get_field_info(field))
            for field_name, field in serializer.fields.items()
        ])

    def get_field_info(self, field):
        """
        Given an instance of a serializer field, return a dictionary
        of metadata about it.
        """
        field_info = OrderedDict()
        serializer = field.parent

        if isinstance(field, serializers.ManyRelatedField):
            field_info['type'] = self.type_lookup[field.child_relation]
        else:
            field_info['type'] = self.type_lookup[field]

        try:
            serializer_model = getattr(serializer.Meta, 'model')
            field_info['relationship_type'] = self.relation_type_lookup[
                getattr(serializer_model, field.field_name)]
        except KeyError:
            pass
        except AttributeError:
            pass
        else:
            field_info['relationship_resource'] = get_related_resource_type(
                field)

        field_info['required'] = getattr(field, 'required', False)

        attrs = [
            'read_only', 'write_only', 'label', 'help_text', 'min_length',
            'max_length', 'min_value', 'max_value', 'initial'
        ]

        for attr in attrs:
            value = getattr(field, attr, None)
            if value is not None and value != '':
                field_info[attr] = force_text(value, strings_only=True)

        if getattr(field, 'child', None):
            field_info['child'] = self.get_field_info(field.child)
        elif getattr(field, 'fields', None):
            field_info['children'] = self.get_serializer_info(field)

        if (not field_info.get('read_only')
                and not field_info.get('relationship_resource')
                and hasattr(field, 'choices')):
            field_info['choices'] = [{
                'value':
                choice_value,
                'display_name':
                force_text(choice_name, strings_only=True)
            } for choice_value, choice_name in field.choices.items()]

        if hasattr(serializer, 'included_serializers'
                   ) and 'relationship_resource' in field_info:
            field_info[
                'allows_include'] = field.field_name in serializer.included_serializers

        return field_info
Beispiel #19
0
# convert a serializer to a JSON Schema.
from rest_framework import serializers
from rest_framework.utils.field_mapping import ClassLookupDict

field_to_converter = ClassLookupDict({})


def converter(converter_class):
    """Decorator to register a converter class"""
    if isinstance(converter_class.field_class, list):
        field_classes = converter_class.field_class
    else:
        field_classes = [converter_class.field_class]
    for field_class in field_classes:
        field_to_converter[field_class] = converter_class()
    return converter_class


def field_to_jsonschema(field):
    if isinstance(field, serializers.Serializer):
        result = to_jsonschema(field)
    else:
        converter = field_to_converter[field]
        result = converter.convert(field)
    if field.label:
        result['title'] = field.label
    if field.help_text:
        result['description'] = field.help_text
    return result

Beispiel #20
0
class HTMLFormRenderer(BaseRenderer):
    """
    Renderers serializer data into an HTML form.

    If the serializer was instantiated without an object then this will
    return an HTML form not bound to any object,
    otherwise it will return an HTML form with the appropriate initial data
    populated from the object.

    Note that rendering of field and form errors is not currently supported.
    """

    media_type = "text/html"
    format = "form"
    charset = "utf-8"
    template_pack = "rest_framework/vertical/"
    base_template = "form.html"

    default_style = ClassLookupDict({
        serializers.Field: {
            "base_template": "input.html",
            "input_type": "text"
        },
        serializers.EmailField: {
            "base_template": "input.html",
            "input_type": "email",
        },
        serializers.URLField: {
            "base_template": "input.html",
            "input_type": "url"
        },
        serializers.IntegerField: {
            "base_template": "input.html",
            "input_type": "number",
        },
        serializers.FloatField: {
            "base_template": "input.html",
            "input_type": "number",
        },
        serializers.DateTimeField: {
            "base_template": "input.html",
            "input_type": "datetime-local",
        },
        serializers.DateField: {
            "base_template": "input.html",
            "input_type": "date",
        },
        serializers.TimeField: {
            "base_template": "input.html",
            "input_type": "time",
        },
        serializers.FileField: {
            "base_template": "input.html",
            "input_type": "file",
        },
        serializers.BooleanField: {
            "base_template": "checkbox.html"
        },
        serializers.ChoiceField: {
            "base_template": "select.html",  # Also valid: 'radio.html'
        },
        serializers.MultipleChoiceField: {
            "base_template":
            "select_multiple.html",  # Also valid: 'checkbox_multiple.html'
        },
        serializers.RelatedField: {
            "base_template": "select.html",  # Also valid: 'radio.html'
        },
        serializers.ManyRelatedField: {
            "base_template":
            "select_multiple.html",  # Also valid: 'checkbox_multiple.html'
        },
        serializers.Serializer: {
            "base_template": "fieldset.html"
        },
        serializers.ListSerializer: {
            "base_template": "list_fieldset.html"
        },
        serializers.ListField: {
            "base_template": "list_field.html"
        },
        serializers.DictField: {
            "base_template": "dict_field.html"
        },
        serializers.FilePathField: {
            "base_template": "select.html",
        },
        serializers.JSONField: {
            "base_template": "textarea.html",
        },
    })

    def render_field(self, field, parent_style):
        if isinstance(field._field, serializers.HiddenField):
            return ""

        style = self.default_style[field].copy()
        style.update(field.style)
        if "template_pack" not in style:
            style["template_pack"] = parent_style.get("template_pack",
                                                      self.template_pack)
        style["renderer"] = self

        # Get a clone of the field with text-only value representation.
        field = field.as_form_field()

        if style.get("input_type") == "datetime-local" and isinstance(
                field.value, str):
            field.value = field.value.rstrip("Z")

        if "template" in style:
            template_name = style["template"]
        else:
            template_name = (style["template_pack"].strip("/") + "/" +
                             style["base_template"])

        template = loader.get_template(template_name)
        context = {"field": field, "style": style}
        return template.render(context)

    def render(self, data, accepted_media_type=None, renderer_context=None):
        """
        Render serializer data and return an HTML form, as a string.
        """
        renderer_context = renderer_context or {}
        form = data.serializer

        style = renderer_context.get("style", {})
        if "template_pack" not in style:
            style["template_pack"] = self.template_pack
        style["renderer"] = self

        template_pack = style["template_pack"].strip("/")
        template_name = template_pack + "/" + self.base_template
        template = loader.get_template(template_name)
        context = {"form": form, "style": style}
        return template.render(context)
Beispiel #21
0
class SimpleMetadata(BaseMetadata):
    """
    This is the default metadata implementation.
    It returns an ad-hoc set of information about the view.
    There are not any formalized standards for `OPTIONS` responses
    for us to base this on.
    """
    label_lookup = ClassLookupDict({
        serializers.Field: 'field',
        serializers.BooleanField: 'boolean',
        serializers.NullBooleanField: 'boolean',
        serializers.CharField: 'string',
        serializers.UUIDField: 'string',
        serializers.URLField: 'url',
        serializers.EmailField: 'email',
        serializers.RegexField: 'regex',
        serializers.SlugField: 'slug',
        serializers.IntegerField: 'integer',
        serializers.FloatField: 'float',
        serializers.DecimalField: 'decimal',
        serializers.DateField: 'date',
        serializers.DateTimeField: 'datetime',
        serializers.TimeField: 'time',
        serializers.ChoiceField: 'choice',
        serializers.MultipleChoiceField: 'multiple choice',
        serializers.FileField: 'file upload',
        serializers.ImageField: 'image upload',
        serializers.ListField: 'list',
        serializers.DictField: 'nested object',
        serializers.Serializer: 'nested object',
    })

    def determine_metadata(self, request, view):
        metadata = OrderedDict()
        metadata['name'] = view.get_view_name()
        metadata['description'] = view.get_view_description()
        metadata['renders'] = [
            renderer.media_type for renderer in view.renderer_classes
        ]
        metadata['parses'] = [
            parser.media_type for parser in view.parser_classes
        ]
        if hasattr(view, 'get_serializer'):
            actions = self.determine_actions(request, view)
            if actions:
                metadata['actions'] = actions
        return metadata

    def determine_actions(self, request, view):
        """
        For generic class based views we return information about
        the fields that are accepted for 'PUT' and 'POST' methods.
        """
        actions = {}
        for method in {'PUT', 'POST'} & set(view.allowed_methods):
            view.request = clone_request(request, method)
            try:
                # Test global permissions
                if hasattr(view, 'check_permissions'):
                    view.check_permissions(view.request)
                # Test object permissions
                if method == 'PUT' and hasattr(view, 'get_object'):
                    view.get_object()
            except (exceptions.APIException, PermissionDenied, Http404):
                pass
            else:
                # If user has appropriate permissions for the view, include
                # appropriate metadata about the fields that should be supplied.
                serializer = view.get_serializer()
                actions[method] = self.get_serializer_info(serializer)
            finally:
                view.request = request

        return actions

    def get_serializer_info(self, serializer):
        """
        Given an instance of a serializer, return a dictionary of metadata
        about its fields.
        """
        if hasattr(serializer, 'child'):
            # If this is a `ListSerializer` then we want to examine the
            # underlying child serializer instance instead.
            serializer = serializer.child
        return OrderedDict([(field_name, self.get_field_info(field))
                            for field_name, field in serializer.fields.items()
                            ])

    def get_field_info(self, field):
        """
        Given an instance of a serializer field, return a dictionary
        of metadata about it.
        """
        field_info = OrderedDict()
        field_info['type'] = self.label_lookup[field]
        field_info['required'] = getattr(field, 'required', False)

        attrs = [
            'read_only', 'label', 'help_text', 'min_length', 'max_length',
            'min_value', 'max_value'
        ]

        for attr in attrs:
            value = getattr(field, attr, None)
            if value is not None and value != '':
                field_info[attr] = force_text(value, strings_only=True)

        if getattr(field, 'child', None):
            field_info['child'] = self.get_field_info(field.child)
        elif getattr(field, 'fields', None):
            field_info['children'] = self.get_serializer_info(field)

        if (not field_info.get('read_only') and not isinstance(
                field,
            (serializers.RelatedField, serializers.ManyRelatedField))
                and hasattr(field, 'choices')):
            field_info['choices'] = [{
                'value':
                choice_value,
                'display_name':
                force_text(choice_name, strings_only=True)
            } for choice_value, choice_name in field.choices.items()]

        return field_info
Beispiel #22
0
class VueTableMetadata(BaseMetadata):
    """
    This is the default metadata implementation.
    It returns an ad-hoc set of information about the view.
    There are not any formalized standards for `OPTIONS` responses
    for us to base this on.
    """

    style_class = Style
    label_lookup = ClassLookupDict({
        serializers.Field: 'field',
        serializers.BooleanField: 'boolean',
        serializers.NullBooleanField: 'boolean',
        serializers.CharField: 'string',
        serializers.UUIDField: 'string',
        serializers.URLField: 'url',
        serializers.EmailField: 'email',
        serializers.RegexField: 'regex',
        serializers.SlugField: 'slug',
        serializers.IntegerField: 'integer',
        serializers.FloatField: 'float',
        serializers.DecimalField: 'decimal',
        serializers.DateField: 'date',
        serializers.DateTimeField: 'datetime',
        serializers.TimeField: 'time',
        serializers.ChoiceField: 'choice',
        serializers.MultipleChoiceField: 'multiple choice',
        serializers.FileField: 'file upload',
        serializers.ImageField: 'image upload',
        serializers.ListField: 'list',
        serializers.DictField: 'nested object',
        serializers.Serializer: 'nested object',
    })

    search_type_lookup = ClassLookupDict({
        serializers.Field:
        'input',
        serializers.BooleanField:
        'select',
        serializers.NullBooleanField:
        'select',
        serializers.IntegerField:
        'num',
        serializers.FloatField:
        'input',
        serializers.DecimalField:
        'input',
        serializers.DateField:
        'date',
        serializers.DateTimeField:
        'date',
        serializers.TimeField:
        'date',
        serializers.ChoiceField:
        'select',
        serializers.MultipleChoiceField:
        'select',
    })

    def determine_metadata(self, request, view):
        metadata = dict()
        metadata['name'] = view.get_view_name()
        metadata['description'] = view.get_view_description()
        metadata['renders'] = [
            renderer.media_type for renderer in view.renderer_classes
        ]
        metadata['parses'] = [
            parser.media_type for parser in view.parser_classes
        ]
        if hasattr(view, 'get_serializer'):
            actions = self.determine_actions(request, view)
            if actions:
                metadata['actions'] = actions
        return metadata

    def determine_actions(self, request, view):
        """
        For generic class based views we return information about
        the fields that are accepted for 'PUT' and 'POST' methods.
        """
        setattr(self, "request", request)
        setattr(self, "view", view)
        actions = {}
        action_names = []
        extra_actions = view.get_extra_actions()
        extra_action_names = [
            act.mapping['get'] for act in extra_actions
            if 'get' in act.mapping and act.mapping['get'] not in ("export",
                                                                   "meta")
        ]
        if 'get' in view.action_map:
            action_names = [view.action_map["get"]] + extra_action_names

        for action in action_names:
            actions[action] = dict()
            origin_action = view.action
            view.action = action
            serializer = view.get_serializer()
            actions[action]["meta"] = self.get_serializer_meta(
                view, serializer)
            actions[action]["columns"] = self.get_serializer_info(serializer)
            view.action = origin_action

        # TODO check permissions
        # for method in {'GET'} & set(view.allowed_methods):
        #     view.request = clone_request(request, method)
        #     try:
        #         # Test global permissions
        #         if hasattr(view, 'check_permissions'):
        #             view.check_permissions(view.request)
        #     except (exceptions.APIException, PermissionDenied, Http404):
        #         pass
        #     else:
        #         # If user has appropriate permissions for the view, include
        #         # appropriate metadata about the fields that should be supplied.
        #         serializer = view.get_serializer()
        #         actions[method] = self.get_serializer_info(serializer)
        #     finally:
        #         view.request = request

        return actions

    def get_serializer_meta(self, view, serializer):
        """
        :param view: [description]
        :type view: [type]
        :param serializer: [description]
        :type serializer: [type]
        """
        table_meta = getattr(view, "table_meta", {})
        data_field_name = table_meta.get('data_field')
        data_field = serializer.fields.get(data_field_name)
        if data_field and hasattr(data_field, "child"):
            table_meta['fields'] = self.get_serializer_info(data_field.child)

        return table_meta

    def get_serializer_info(self, serializer):
        """
        Given an instance of a serializer, return a dictionary of metadata
        about its fields.
        """
        if hasattr(serializer, 'child'):
            # If this is a `ListSerializer` then we want to examine the
            # underlying child serializer instance instead.
            serializer = serializer.child

        return [
            self.get_field_style(field)
            for field_name, field in serializer.fields.items()
            if not isinstance(field, serializers.HiddenField)
            and not field.write_only
        ]

    def get_field_style(self, field, sub_table=True):
        """
        Given an instance of a serializer field, return a dictionary
        of metadata about it.
        """
        # validate style
        if isinstance(field.style, dict):
            copy_style = copy.deepcopy(field.style)
            style = self.style_class(**copy_style)
        elif isinstance(field.style, self.style_class):
            style = field.style
        else:
            raise ValueError("Unsupported style type.")

        style.field = field.field_name
        style.title = field.label

        if isinstance(field,
                      (ChoiceField, MultipleChoiceField)) and field.choices:
            style.cellRender = to_table_choices(field.choices)

        return style.dict(exclude_none=True)
Beispiel #23
0
class SimpleMetadata(BaseMetadata):
    """
    This is the default metadata implementation.
    It returns an ad-hoc set of information about the view.
    There are not any formalized standards for `OPTIONS` responses
    for us to base this on.
    """

    label_lookup = ClassLookupDict(
        {
            serializers.Field: "field",
            serializers.BooleanField: "boolean",
            serializers.NullBooleanField: "boolean",
            serializers.CharField: "string",
            serializers.UUIDField: "string",
            serializers.URLField: "url",
            serializers.EmailField: "email",
            serializers.RegexField: "regex",
            serializers.SlugField: "slug",
            serializers.IntegerField: "integer",
            serializers.FloatField: "float",
            serializers.DecimalField: "decimal",
            serializers.DateField: "date",
            serializers.DateTimeField: "datetime",
            serializers.TimeField: "time",
            serializers.ChoiceField: "choice",
            serializers.MultipleChoiceField: "multiple choice",
            serializers.FileField: "file upload",
            serializers.ImageField: "image upload",
            serializers.ListField: "list",
            serializers.DictField: "nested object",
            serializers.Serializer: "nested object",
        }
    )

    def determine_metadata(self, request, view):
        metadata = OrderedDict()
        metadata["name"] = view.get_view_name()
        metadata["description"] = view.get_view_description()
        metadata["renders"] = [
            renderer.media_type for renderer in view.renderer_classes
        ]
        metadata["parses"] = [parser.media_type for parser in view.parser_classes]
        if hasattr(view, "get_serializer"):
            actions = self.determine_actions(request, view)
            if actions:
                metadata["actions"] = actions
        return metadata

    def determine_actions(self, request, view):
        """
        For generic class based views we return information about
        the fields that are accepted for 'PUT' and 'POST' methods.
        """
        actions = {}
        for method in {"PUT", "POST"} & set(view.allowed_methods):
            view.request = clone_request(request, method)
            try:
                # Test global permissions
                if hasattr(view, "check_permissions"):
                    view.check_permissions(view.request)
                # Test object permissions
                if method == "PUT" and hasattr(view, "get_object"):
                    view.get_object()
            except (exceptions.APIException, PermissionDenied, Http404):
                pass
            else:
                # If user has appropriate permissions for the view, include
                # appropriate metadata about the fields that should be supplied.
                serializer = view.get_serializer()
                actions[method] = self.get_serializer_info(serializer)
            finally:
                view.request = request

        return actions

    def get_serializer_info(self, serializer):
        """
        Given an instance of a serializer, return a dictionary of metadata
        about its fields.
        """
        if hasattr(serializer, "child"):
            # If this is a `ListSerializer` then we want to examine the
            # underlying child serializer instance instead.
            serializer = serializer.child
        return OrderedDict(
            [
                (field_name, self.get_field_info(field))
                for field_name, field in serializer.fields.items()
                if not isinstance(field, serializers.HiddenField)
            ]
        )

    def get_field_info(self, field):
        """
        Given an instance of a serializer field, return a dictionary
        of metadata about it.
        """
        field_info = OrderedDict()
        field_info["type"] = self.label_lookup[field]
        field_info["required"] = getattr(field, "required", False)

        attrs = [
            "read_only",
            "label",
            "help_text",
            "min_length",
            "max_length",
            "min_value",
            "max_value",
        ]

        for attr in attrs:
            value = getattr(field, attr, None)
            if value is not None and value != "":
                field_info[attr] = force_str(value, strings_only=True)

        if getattr(field, "child", None):
            field_info["child"] = self.get_field_info(field.child)
        elif getattr(field, "fields", None):
            field_info["children"] = self.get_serializer_info(field)

        if (
            not field_info.get("read_only")
            and not isinstance(
                field, (serializers.RelatedField, serializers.ManyRelatedField)
            )
            and hasattr(field, "choices")
        ):
            field_info["choices"] = [
                {
                    "value": choice_value,
                    "display_name": force_str(choice_name, strings_only=True),
                }
                for choice_value, choice_name in field.choices.items()
            ]

        return field_info
Beispiel #24
0
FILTER_FOR_SERIALIZER_FIELD_DEFAULTS = ClassLookupDict(
    {
        serializers.IntegerField: {'filter_class': ExtendedNumberFilter},
        serializers.DateField: {'filter_class': ExtendedDateFromToRangeFilter},
        serializers.DateTimeField: {'filter_class': ExtendedDateFromToRangeFilter},
        serializers.TimeField: {'filter_class': TimeFilter},
        serializers.DecimalField: {'filter_class': ExtendedNumberFilter},
        serializers.FloatField: {'filter_class': ExtendedNumberFilter},
        serializers.BooleanField: {'filter_class': BooleanFilter},
        serializers.EmailField: {'filter_class': CharFilter},
        serializers.FileField: {'filter_class': CharFilter},
        serializers.URLField: {'filter_class': CharFilter},
        serializers.UUIDField: {'filter_class': UUIDFilter},
        serializers.PrimaryKeyRelatedField: {
            'filter_class': ExtendedModelMultipleChoiceFilter,
            'extra': lambda f: {"queryset": f.queryset, "distinct": False},
        },
        serializers.SlugRelatedField: {
            'filter_class': ExtendedModelMultipleChoiceFilter,
            'extra': lambda f: {"queryset": f.queryset, "to_field_name": f.slug_field, "distinct": False},
        },
        serializers.ManyRelatedField: {
            'filter_class': ExtendedModelMultipleChoiceFilter,
            'extra': lambda f: {"queryset": f.child_relation.queryset},
        },
        serializers.RelatedField: {'filter_class': MultipleSelectFilter},
        serializers.JSONField: {'filter_class': CharFilter, 'extra': lambda f: {"lookup_expr": "icontains"}},
        serializers.ListField: {'filter_class': CharFilter, 'extra': lambda f: {"lookup_expr": "contains"}},
        IsNotNullField: {'filter_class': IsNotNullFilter},
        IsNullField: {'filter_class': IsNullFilter},
        serializers.ReadOnlyField: {'filter_class': CharFilter},
        DisplayChoiceField: {
            'filter_class': ExtendedDisplayMultipleChoiceFilter,
            'extra': lambda f: {"choices": list(f.choices.items()), 'distinct': False},
        },
        serializers.ChoiceField: {
            'filter_class': ExtendedMultipleChoiceFilter,
            'extra': lambda f: {"choices": list(f.choices.items()), "distinct": False},
        },
        serializers.CharField: {'filter_class': ExtendedCharFilter},
    }
)
Beispiel #25
0
class HaystackSerializer(
        six.with_metaclass(HaystackSerializerMeta, serializers.Serializer)):
    """
    A `HaystackSerializer` which populates fields based on
    which models that are available in the SearchQueryset.
    """

    _abstract = True

    _field_mapping = ClassLookupDict({
        haystack_fields.BooleanField:
        HaystackBooleanField,
        haystack_fields.CharField:
        HaystackCharField,
        haystack_fields.DateField:
        HaystackDateField,
        haystack_fields.DateTimeField:
        HaystackDateTimeField,
        haystack_fields.DecimalField:
        HaystackDecimalField,
        haystack_fields.EdgeNgramField:
        HaystackCharField,
        haystack_fields.FacetBooleanField:
        HaystackBooleanField,
        haystack_fields.FacetCharField:
        HaystackCharField,
        haystack_fields.FacetDateField:
        HaystackDateField,
        haystack_fields.FacetDateTimeField:
        HaystackDateTimeField,
        haystack_fields.FacetDecimalField:
        HaystackDecimalField,
        haystack_fields.FacetFloatField:
        HaystackFloatField,
        haystack_fields.FacetIntegerField:
        HaystackIntegerField,
        haystack_fields.FacetMultiValueField:
        HaystackMultiValueField,
        haystack_fields.FloatField:
        HaystackFloatField,
        haystack_fields.IntegerField:
        HaystackIntegerField,
        haystack_fields.LocationField:
        HaystackCharField,
        haystack_fields.MultiValueField:
        HaystackMultiValueField,
        haystack_fields.NgramField:
        HaystackCharField,
    })

    def __init__(self, instance=None, data=empty, **kwargs):
        super(HaystackSerializer, self).__init__(instance, data, **kwargs)

        if not self.Meta.index_classes and not self.Meta.serializers:
            raise ImproperlyConfigured(
                "You must set either the 'index_classes' or 'serializers' "
                "attribute on the serializer Meta class.")

        if not self.instance:
            self.instance = EmptySearchQuerySet()

    @staticmethod
    def _get_default_field_kwargs(model, field):
        """
        Get the required attributes from the model field in order
        to instantiate a REST Framework serializer field.
        """
        kwargs = {}
        if field.model_attr in model._meta.get_fields():
            model_field = model._meta.get_field(field.model_attr)[0]
            kwargs = get_field_kwargs(field.model_attr, model_field)

            # Remove stuff we don't care about!
            delete_attrs = [
                "allow_blank",
                "choices",
                "model_field",
            ]
            for attr in delete_attrs:
                if attr in kwargs:
                    del kwargs[attr]

        return kwargs

    def _get_index_field(self, field_name):
        """
        Returns the correct index field.
        """
        return field_name

    def _get_index_class_name(self, index_cls):
        """
        Converts in index model class to a name suitable for use as a field name prefix. A user
        may optionally specify custom aliases via an 'index_aliases' attribute on the Meta class
        """
        cls_name = index_cls.__name__
        aliases = self.Meta.index_aliases
        return aliases.get(cls_name, cls_name.split('.')[-1])

    def get_fields(self):
        """
        Get the required fields for serializing the result.
        """

        fields = self.Meta.fields
        exclude = self.Meta.exclude
        ignore_fields = self.Meta.ignore_fields
        indices = self.Meta.index_classes

        declared_fields = copy.deepcopy(self._declared_fields)
        prefix_field_names = len(indices) > 1
        field_mapping = OrderedDict()

        # overlapping fields on multiple indices is supported by internally prefixing the field
        # names with the index class to which they belong or, optionally, a user-provided alias
        # for the index.
        for index_cls in self.Meta.index_classes:
            prefix = ""
            if prefix_field_names:
                prefix = "_%s__" % self._get_index_class_name(index_cls)
            for field_name, field_type in six.iteritems(index_cls.fields):
                orig_name = field_name
                field_name = "%s%s" % (prefix, field_name)

                # Don't use this field if it is in `ignore_fields`
                if orig_name in ignore_fields or field_name in ignore_fields:
                    continue
                # When fields to include are decided by `exclude`
                if exclude:
                    if orig_name in exclude or field_name in exclude:
                        continue
                # When fields to include are decided by `fields`
                if fields:
                    if orig_name not in fields and field_name not in fields:
                        continue

                # Look up the field attributes on the current index model,
                # in order to correctly instantiate the serializer field.
                model = index_cls().get_model()
                kwargs = self._get_default_field_kwargs(model, field_type)
                kwargs['prefix_field_names'] = prefix_field_names
                field_mapping[field_name] = self._field_mapping[field_type](
                    **kwargs)

        # Add any explicitly declared fields. They *will* override any index fields
        # in case of naming collision!.
        if declared_fields:
            for field_name in declared_fields:
                field_mapping[field_name] = declared_fields[field_name]
        return field_mapping

    def to_representation(self, instance):
        """
        If we have a serializer mapping, use that.  Otherwise, use standard serializer behavior
        Since we might be dealing with multiple indexes, some fields might
        not be valid for all results. Do not render the fields which don't belong
        to the search result.
        """
        if self.Meta.serializers:
            ret = self.multi_serializer_representation(instance)
        else:
            ret = super(HaystackSerializer, self).to_representation(instance)
            prefix_field_names = len(getattr(self.Meta, "index_classes")) > 1
            current_index = self._get_index_class_name(
                type(instance.searchindex))
            for field in self.fields.keys():
                orig_field = field
                if prefix_field_names:
                    parts = field.split("__")
                    if len(parts) > 1:
                        index = parts[0][1:]  # trim the preceding '_'
                        field = parts[1]
                        if index == current_index:
                            ret[field] = ret[orig_field]
                        del ret[orig_field]
                elif field not in chain(instance.searchindex.fields.keys(),
                                        self._declared_fields.keys()):
                    del ret[orig_field]

        # include the highlighted field in either case
        if getattr(instance, "highlighted", None):
            ret["highlighted"] = instance.highlighted[0]
        return ret

    def multi_serializer_representation(self, instance):
        serializers = self.Meta.serializers
        index = instance.searchindex
        serializer_class = serializers.get(type(index), None)
        if not serializer_class:
            raise ImproperlyConfigured(
                "Could not find serializer for %s in mapping" % index)
        return serializer_class(
            context=self._context).to_representation(instance)
Beispiel #26
0
default_style = ClassLookupDict({
    serializers.Field: {
        'tag': 'input',
        'input_type': 'text'
    },
    serializers.EmailField: {
        'tag': 'input',
        'input_type': 'email'
    },
    serializers.URLField: {
        'tag': 'input',
        'input_type': 'url'
    },
    serializers.IntegerField: {
        'tag': 'input',
        'input_type': 'number'
    },
    serializers.FloatField: {
        'tag': 'input',
        'input_type': 'number'
    },
    serializers.DateTimeField: {
        'tag': 'input',
        'input_type': 'datetime-local'
    },
    serializers.DateField: {
        'tag': 'input',
        'input_type': 'date'
    },
    serializers.TimeField: {
        'tag': 'input',
        'input_type': 'time'
    },
    serializers.FileField: {
        'tag': 'input',
        'input_type': 'file'
    },
    serializers.BooleanField: {
        'tag': 'checkbox'
    },
    serializers.ChoiceField: {
        'tag': 'select',  # Also valid: 'radio'
    },
    serializers.MultipleChoiceField: {
        'tag': 'select_multiple',  # Also valid: 'checkbox_multiple'
    },
    serializers.RelatedField: {
        'tag': 'select',  # Also valid: 'radio'
    },
    serializers.ManyRelatedField: {
        'tag': 'select multiple',  # Also valid: 'checkbox_multiple'
    },
    serializers.Serializer: {
        'tag': 'fieldset'
    },
    serializers.ListSerializer: {
        'tag': 'list_fieldset'
    },
    serializers.ListField: {
        'tag': 'list_field'
    },
    serializers.DictField: {
        'tag': 'dict_field'
    },
    serializers.FilePathField: {
        'tag': 'input',
        'input_type': 'file',
    },
    serializers.JSONField: {
        'tag': 'textarea',
    },
})
Beispiel #27
0
class HTMLFormRenderer(BaseRenderer):
    """
    Renderers serializer data into an HTML form.

    If the serializer was instantiated without an object then this will
    return an HTML form not bound to any object,
    otherwise it will return an HTML form with the appropriate initial data
    populated from the object.

    Note that rendering of field and form errors is not currently supported.
    """
    media_type = 'text/html'
    format = 'form'
    charset = 'utf-8'
    template_pack = 'rest_framework/vertical/'
    base_template = 'form.html'

    default_style = ClassLookupDict({
        serializers.Field: {
            'base_template': 'input.html',
            'input_type': 'text'
        },
        serializers.EmailField: {
            'base_template': 'input.html',
            'input_type': 'email'
        },
        serializers.URLField: {
            'base_template': 'input.html',
            'input_type': 'url'
        },
        serializers.IntegerField: {
            'base_template': 'input.html',
            'input_type': 'number'
        },
        serializers.FloatField: {
            'base_template': 'input.html',
            'input_type': 'number'
        },
        serializers.DateTimeField: {
            'base_template': 'input.html',
            'input_type': 'datetime-local'
        },
        serializers.DateField: {
            'base_template': 'input.html',
            'input_type': 'date'
        },
        serializers.TimeField: {
            'base_template': 'input.html',
            'input_type': 'time'
        },
        serializers.FileField: {
            'base_template': 'input.html',
            'input_type': 'file'
        },
        serializers.BooleanField: {
            'base_template': 'checkbox.html'
        },
        serializers.ChoiceField: {
            'base_template': 'select.html',  # Also valid: 'radio.html'
        },
        serializers.MultipleChoiceField: {
            'base_template':
            'select_multiple.html',  # Also valid: 'checkbox_multiple.html'
        },
        serializers.RelatedField: {
            'base_template': 'select.html',  # Also valid: 'radio.html'
        },
        serializers.ManyRelatedField: {
            'base_template':
            'select_multiple.html',  # Also valid: 'checkbox_multiple.html'
        },
        serializers.Serializer: {
            'base_template': 'fieldset.html'
        },
        serializers.ListSerializer: {
            'base_template': 'list_fieldset.html'
        },
        serializers.ListField: {
            'base_template': 'list_field.html'
        },
        serializers.DictField: {
            'base_template': 'dict_field.html'
        },
        serializers.FilePathField: {
            'base_template': 'select.html',
        },
        serializers.JSONField: {
            'base_template': 'textarea.html',
        },
    })

    def render_field(self, field, parent_style):
        if isinstance(field._field, serializers.HiddenField):
            return ''

        style = dict(self.default_style[field])
        style.update(field.style)
        if 'template_pack' not in style:
            style['template_pack'] = parent_style.get('template_pack',
                                                      self.template_pack)
        style['renderer'] = self

        # Get a clone of the field with text-only value representation.
        field = field.as_form_field()

        if style.get('input_type') == 'datetime-local' and isinstance(
                field.value, str):
            field.value = field.value.rstrip('Z')

        if 'template' in style:
            template_name = style['template']
        else:
            template_name = style['template_pack'].strip(
                '/') + '/' + style['base_template']

        template = loader.get_template(template_name)
        context = {'field': field, 'style': style}
        return template.render(context)

    def render(self, data, accepted_media_type=None, renderer_context=None):
        """
        Render serializer data and return an HTML form, as a string.
        """
        renderer_context = renderer_context or {}
        form = data.serializer

        style = renderer_context.get('style', {})
        if 'template_pack' not in style:
            style['template_pack'] = self.template_pack
        style['renderer'] = self

        template_pack = style['template_pack'].strip('/')
        template_name = template_pack + '/' + self.base_template
        template = loader.get_template(template_name)
        context = {'form': form, 'style': style}
        return template.render(context)
Beispiel #28
0
class Metadata(BaseMetadata):
    label_lookup = ClassLookupDict(
        {
            serializers.Field: "field",
            serializers.BooleanField: "boolean",
            serializers.NullBooleanField: "boolean",
            serializers.CharField: "string",
            serializers.URLField: "url",
            serializers.EmailField: "email",
            serializers.RegexField: "regex",
            serializers.SlugField: "slug",
            serializers.IntegerField: "integer",
            serializers.FloatField: "float",
            serializers.DecimalField: "decimal",
            serializers.DateField: "date",
            serializers.DateTimeField: "date",
            serializers.TimeField: "time",
            serializers.ChoiceField: "choice",
            serializers.MultipleChoiceField: "multiple choice",
            serializers.FileField: "file upload",
            serializers.ImageField: "image upload",
            serializers.ListField: "list",
            serializers.DictField: "nested object",
            serializers.Serializer: "nested object",
            serializers.ManyRelatedField: "multiple choice",
            serializers.HiddenField: "hidden",
            PasswordField: "password",
            ModelMultipleChoiceField: "multiple choice",
            TextField: "textarea",
            EntityText: "entity text",
        }
    )

    def determine_metadata(self, request, view):  # pragma: no cover
        metadata = OrderedDict()
        metadata["name"] = view.get_view_name()
        metadata["description"] = view.get_view_description()
        metadata["renders"] = [
            renderer.media_type for renderer in view.renderer_classes
        ]
        metadata["parses"] = [parser.media_type for parser in view.parser_classes]
        if hasattr(view, "get_serializer"):
            actions = self.determine_actions(request, view)
            if actions:
                metadata["actions"] = actions
        return metadata

    def determine_actions(self, request, view):  # pragma: no cover
        actions = {}
        for method in {"PUT", "POST"} & set(view.allowed_methods):
            serializer = view.get_serializer()
            actions[method] = self.get_serializer_info(serializer)
            view.request = request
        return actions

    def get_serializer_info(self, serializer):  # pragma: no cover
        if hasattr(serializer, "child"):
            serializer = serializer.child
        return OrderedDict(
            [
                (field_name, self.get_field_info(field))
                for field_name, field in serializer.fields.items()
            ]
        )

    def get_field_info(self, field):  # pragma: no cover
        field_info = OrderedDict()
        field_info["type"] = self.label_lookup[field] or "field"
        field_info["required"] = getattr(field, "required", False)

        attrs = [
            "read_only",
            "label",
            "help_text",
            "min_length",
            "max_length",
            "min_value",
            "max_value",
            "style",
        ]

        for attr in attrs:
            value = getattr(field, attr, None)
            if value is not None and value != "":
                field_info[attr] = (
                    value
                    if isinstance(value, dict)
                    else force_text(value, strings_only=True)
                )

        if isinstance(field, ModelMultipleChoiceField):
            field_info["choices"] = [
                {
                    "value": choice_value,
                    "display_name": force_text(choice_name, strings_only=True),
                }
                for choice_value, choice_name in field.choices.items()
            ]

        if getattr(field, "child", None):
            field_info["child"] = self.get_field_info(field.child)
        elif getattr(field, "fields", None):
            field_info["children"] = self.get_serializer_info(field)

        if not field_info.get("read_only") and hasattr(field, "choices"):
            field_info["choices"] = [
                {
                    "value": choice_value,
                    "display_name": force_text(choice_name, strings_only=True),
                }
                for choice_value, choice_name in field.choices.items()
            ]

        return field_info
class HTMLFormRenderer(BaseRenderer):
    """
    Renderers serializer data into an HTML form.

    If the serializer was instantiated without an object then this will
    return an HTML form not bound to any object,
    otherwise it will return an HTML form with the appropriate initial data
    populated from the object.

    Note that rendering of field and form errors is not currently supported.
    """
    media_type = 'text/html'
    format = 'form'
    charset = 'utf-8'
    template_pack = 'rest_framework/horizontal/'
    base_template = 'form.html'

    default_style = ClassLookupDict({
        serializers.Field: {
            'base_template': 'input.html',
            'input_type': 'text'
        },
        serializers.EmailField: {
            'base_template': 'input.html',
            'input_type': 'email'
        },
        serializers.URLField: {
            'base_template': 'input.html',
            'input_type': 'url'
        },
        serializers.IntegerField: {
            'base_template': 'input.html',
            'input_type': 'number'
        },
        serializers.DateTimeField: {
            'base_template': 'input.html',
            'input_type': 'datetime-local'
        },
        serializers.DateField: {
            'base_template': 'input.html',
            'input_type': 'date'
        },
        serializers.TimeField: {
            'base_template': 'input.html',
            'input_type': 'time'
        },
        serializers.FileField: {
            'base_template': 'input.html',
            'input_type': 'file'
        },
        serializers.BooleanField: {
            'base_template': 'checkbox.html'
        },
        serializers.ChoiceField: {
            'base_template': 'select.html',  # Also valid: 'radio.html'
        },
        serializers.MultipleChoiceField: {
            'base_template':
            'select_multiple.html',  # Also valid: 'checkbox_multiple.html'
        },
        serializers.RelatedField: {
            'base_template': 'select.html',  # Also valid: 'radio.html'
        },
        serializers.ManyRelatedField: {
            'base_template':
            'select_multiple.html',  # Also valid: 'checkbox_multiple.html'
        },
        serializers.Serializer: {
            'base_template': 'fieldset.html'
        },
        serializers.ListSerializer: {
            'base_template': 'list_fieldset.html'
        }
    })

    def render_field(self, field, parent_style):
        if isinstance(field._field, serializers.HiddenField):
            return ''

        style = dict(self.default_style[field])
        style.update(field.style)
        if 'template_pack' not in style:
            style['template_pack'] = parent_style.get('template_pack',
                                                      self.template_pack)
        style['renderer'] = self

        if style.get('input_type') == 'datetime-local' and isinstance(
                field.value, six.text_type):
            field.value = field.value.rstrip('Z')

        if 'template' in style:
            template_name = style['template']
        else:
            template_name = style['template_pack'].strip(
                '/') + '/' + style['base_template']

        template = loader.get_template(template_name)
        context = Context({'field': field, 'style': style})
        return template.render(context)

    def render(self, data, accepted_media_type=None, renderer_context=None):
        """
        Render serializer data and return an HTML form, as a string.
        """
        form = data.serializer
        meta = getattr(form, 'Meta', None)
        style = getattr(meta, 'style', {})
        if 'template_pack' not in style:
            style['template_pack'] = self.template_pack
        if 'base_template' not in style:
            style['base_template'] = self.base_template
        style['renderer'] = self

        # This API needs to be finessed and finalized for 3.1
        if 'template' in renderer_context:
            template_name = renderer_context['template']
        elif 'template' in style:
            template_name = style['template']
        else:
            template_name = style['template_pack'].strip(
                '/') + '/' + style['base_template']

        renderer_context = renderer_context or {}
        request = renderer_context['request']
        template = loader.get_template(template_name)
        context = RequestContext(request, {'form': form, 'style': style})
        return template.render(context)
Beispiel #30
0
class Metadata(BaseMetadata):
    label_lookup = ClassLookupDict({
        serializers.Field:
        'field',
        serializers.BooleanField:
        'boolean',
        serializers.NullBooleanField:
        'boolean',
        serializers.CharField:
        'string',
        serializers.URLField:
        'url',
        serializers.EmailField:
        'email',
        serializers.RegexField:
        'regex',
        serializers.SlugField:
        'slug',
        serializers.IntegerField:
        'integer',
        serializers.FloatField:
        'float',
        serializers.DecimalField:
        'decimal',
        serializers.DateField:
        'date',
        serializers.DateTimeField:
        'datetime',
        serializers.TimeField:
        'time',
        serializers.ChoiceField:
        'choice',
        serializers.MultipleChoiceField:
        'multiple choice',
        serializers.FileField:
        'file upload',
        serializers.ImageField:
        'image upload',
        serializers.ListField:
        'list',
        serializers.DictField:
        'nested object',
        serializers.Serializer:
        'nested object',
        serializers.ManyRelatedField:
        'multiple choice',
    })

    def determine_metadata(self, request, view):
        metadata = OrderedDict()
        metadata['name'] = view.get_view_name()
        metadata['description'] = view.get_view_description()
        metadata['renders'] = [
            renderer.media_type for renderer in view.renderer_classes
        ]
        metadata['parses'] = [
            parser.media_type for parser in view.parser_classes
        ]
        if hasattr(view, 'get_serializer'):
            actions = self.determine_actions(request, view)
            if actions:
                metadata['actions'] = actions
        return metadata

    def determine_actions(self, request, view):
        actions = {}
        for method in ['PUT', 'POST']:
            serializer = view.get_serializer()
            actions[method] = self.get_serializer_info(serializer)
            view.request = request
        return actions

    def get_serializer_info(self, serializer):
        if hasattr(serializer, 'child'):
            serializer = serializer.child
        return OrderedDict([(field_name, self.get_field_info(field))
                            for field_name, field in serializer.fields.items()
                            ])

    def get_field_info(self, field):
        field_info = OrderedDict()
        field_info['type'] = self.label_lookup[field] or 'field'
        field_info['required'] = getattr(field, 'required', False)

        attrs = [
            'read_only',
            'label',
            'help_text',
            'min_length',
            'max_length',
            'min_value',
            'max_value',
            'style',
        ]

        for attr in attrs:
            value = getattr(field, attr, None)
            if value is not None and value != '':
                field_info[attr] = value if isinstance(
                    value,
                    dict,
                ) else force_text(
                    value,
                    strings_only=True,
                )

        if getattr(field, 'child', None):
            field_info['child'] = self.get_field_info(field.child)
        elif getattr(field, 'fields', None):
            field_info['children'] = self.get_serializer_info(field)

        if (not field_info.get('read_only') and hasattr(field, 'choices')):
            field_info['choices'] = [{
                'value':
                choice_value,
                'display_name':
                force_text(choice_name, strings_only=True)
            } for choice_value, choice_name in field.choices.items()]

        return field_info