コード例 #1
0
ファイル: serializers.py プロジェクト: xutss123/weekstart
    def build_standard_field(self, field_name, model_field):
        """
        By default `ModelSerializer.build_standard_field` coerces any field
        that has a model field with choices to `ChoiceField` wich returns the
        value directly.

        Since enum values resemble `EnumClass.ENUM_INSTANCE`
        they won't be able to be encoded by the JSONEncoder when being passed
        to a `Response`.
        """

        if isinstance(model_field, ModelEnumChoiceField):
            # These are kwargs, generated by `get_field_kwargs`
            # but are not needed for our field.
            # `model_field` is used only in children of DRF's `ModelField`
            # `choices` is not used because we use `field.enum_class` to validate the choice
            # `max_length` is generated from the model field's max_length and we don't use it
            dump_kwargs = ('model_field', 'choices', 'max_length')

            initial_kwargs = {
                'enum_class': model_field.enum_class,
                'choice_builder': model_field.choice_builder,
                **get_field_kwargs(field_name, model_field)
            }
            finalized_kwargs = {
                key: value for key, value in initial_kwargs.items()
                if key not in dump_kwargs
            }

            return EnumChoiceField, finalized_kwargs

        return super().build_standard_field(field_name, model_field)
コード例 #2
0
    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 = {}
        try:
            field_name = field.model_attr or field.index_fieldname
            model_field = model._meta.get_field(field_name)
            kwargs.update(get_field_kwargs(field_name, model_field))

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

        return kwargs
コード例 #3
0
ファイル: serializers.py プロジェクト: jannon/drf-haystack
    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 = {}
        try:
            field_name = field.model_attr or field.index_fieldname
            model_field = model._meta.get_field(field_name)
            kwargs.update(get_field_kwargs(field_name, model_field))

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

        return kwargs
コード例 #4
0
    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
コード例 #5
0
    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
コード例 #6
0
ファイル: serializers.py プロジェクト: nycholas/drf-haystack
    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_all_field_names():
            model_field = model._meta.get_field_by_name(field.model_attr)[0]
            kwargs = get_field_kwargs(field.model_attr, model_field)

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

        return kwargs
コード例 #7
0
    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
コード例 #8
0
    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
コード例 #9
0
    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