mirror of
				https://github.com/encode/django-rest-framework.git
				synced 2025-11-04 09:57:55 +03:00 
			
		
		
		
	Merge pull request #2322 from tomchristie/model-serializer-api
ModelSerializer API.
This commit is contained in:
		
						commit
						889a0bdeca
					
				| 
						 | 
				
			
			@ -457,7 +457,7 @@ To do so, open the Django shell, using `python manage.py shell`, then import the
 | 
			
		|||
        name = CharField(allow_blank=True, max_length=100, required=False)
 | 
			
		||||
        owner = PrimaryKeyRelatedField(queryset=User.objects.all())
 | 
			
		||||
 | 
			
		||||
## Specifying which fields should be included
 | 
			
		||||
## Specifying which fields to include
 | 
			
		||||
 | 
			
		||||
If you only want a subset of the default fields to be used in a model serializer, you can do so using `fields` or `exclude` options, just as you would with a `ModelForm`.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -499,7 +499,7 @@ You can add extra fields to a `ModelSerializer` or override the default fields b
 | 
			
		|||
 | 
			
		||||
Extra fields can correspond to any property or callable on the model.
 | 
			
		||||
 | 
			
		||||
## Specifying which fields should be read-only
 | 
			
		||||
## Specifying read only fields
 | 
			
		||||
 | 
			
		||||
You may wish to specify multiple fields as read-only. Instead of adding each field explicitly with the `read_only=True` attribute, you may use the shortcut Meta option, `read_only_fields`.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -528,7 +528,7 @@ Please review the [Validators Documentation](/api-guide/validators/) for details
 | 
			
		|||
---
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Specifying additional keyword arguments for fields.
 | 
			
		||||
## Additional keyword arguments
 | 
			
		||||
 | 
			
		||||
There is also a shortcut allowing you to specify arbitrary additional keyword arguments on fields, using the `extra_kwargs` option. Similarly to `read_only_fields` this means you do not need to explicitly declare the field on the serializer.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -567,6 +567,63 @@ The inner `Meta` class on serializers is not inherited from parent classes by de
 | 
			
		|||
 | 
			
		||||
Typically we would recommend *not* using inheritance on inner Meta classes, but instead declaring all options explicitly.
 | 
			
		||||
 | 
			
		||||
## Customizing field mappings
 | 
			
		||||
 | 
			
		||||
The ModelSerializer class also exposes an API that you can override in order to alter how serializer fields are automatically determined when instantiating the serializer.
 | 
			
		||||
 | 
			
		||||
Normally if a `ModelSerializer` does not generate the fields you need by default the you should either add them to the class explicitly, or simply use a regular `Serializer` class instead. However in some cases you may want to create a new base class that defines how the serializer fields are created for any given model.
 | 
			
		||||
 | 
			
		||||
### `.serializer_field_mapping`
 | 
			
		||||
 | 
			
		||||
A mapping of Django model classes to REST framework serializer classes. You can override this mapping to alter the default serializer classes that should be used for each model class.
 | 
			
		||||
 | 
			
		||||
### `.serializer_relational_field`
 | 
			
		||||
 | 
			
		||||
This property should be the serializer field class, that is used for relational fields by default. For `ModelSerializer` this defaults to `PrimaryKeyRelatedField`. For `HyperlinkedModelSerializer` this defaults to `HyperlinkedRelatedField`.
 | 
			
		||||
 | 
			
		||||
### The field_class and field_kwargs API
 | 
			
		||||
 | 
			
		||||
The following methods are called to determine the class and keyword arguments for each field that should be automatically included on the serializer. Each of these methods should return a two tuple of `(field_class, field_kwargs)`.
 | 
			
		||||
 | 
			
		||||
### `.build_standard_field(self, field_name, model_field)`
 | 
			
		||||
 | 
			
		||||
Called to generate a serializer field that maps to a standard model field.
 | 
			
		||||
 | 
			
		||||
The default implementation returns a serializer class based on the `serializer_field_mapping` attribute.
 | 
			
		||||
 | 
			
		||||
### `.build_relational_field(self, field_name, relation_info)`
 | 
			
		||||
 | 
			
		||||
Called to generate a serializer field that maps to a relational model field.
 | 
			
		||||
 | 
			
		||||
The default implementation returns a serializer class based on the `serializer_relational_field` attribute.
 | 
			
		||||
 | 
			
		||||
The `relation_info` argument is a named tuple, that contains `model_field`, `related_model`, `to_many` and `has_through_model` properties.
 | 
			
		||||
 | 
			
		||||
### `.build_nested_field(self, field_name, relation_info, nested_depth)`
 | 
			
		||||
 | 
			
		||||
Called to generate a serializer field that maps to a relational model field, when the `depth` option has been set.
 | 
			
		||||
 | 
			
		||||
The default implementation dynamically creates a nested serializer class based on either `ModelSerializer` or `HyperlinkedModelSerializer`.
 | 
			
		||||
 | 
			
		||||
The `nested_depth` will be the value of the `depth` option, minus one.
 | 
			
		||||
 | 
			
		||||
The `relation_info` argument is a named tuple, that contains `model_field`, `related_model`, `to_many` and `has_through_model` properties.
 | 
			
		||||
 | 
			
		||||
### `.build_property_field(self, field_name, model_class)`
 | 
			
		||||
 | 
			
		||||
Called to generate a serializer field that maps to a property or zero-argument method on the model class.
 | 
			
		||||
 | 
			
		||||
The default implementation returns a `ReadOnlyField` class.
 | 
			
		||||
 | 
			
		||||
### `.build_url_field(self, field_name, model_class)`
 | 
			
		||||
 | 
			
		||||
Called to generate a serializer field for the serializer's own `url` field. The default implementation returns a `HyperlinkedIdentityField` class.
 | 
			
		||||
 | 
			
		||||
### `.build_unknown_field(self, field_name, model_class)`
 | 
			
		||||
 | 
			
		||||
Called when the field name did not map to any model field or model property.
 | 
			
		||||
The default implementation raises an error, although subclasses may customize this behavior.
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
# HyperlinkedModelSerializer
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -239,6 +239,10 @@ body a:hover{
 | 
			
		|||
     }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
h1 code, h2 code, h3 code, h4 code, h5 code {
 | 
			
		||||
  color: #333;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* sticky footer and footer */
 | 
			
		||||
html, body {
 | 
			
		||||
  height: 100%;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -327,7 +327,9 @@ class Serializer(BaseSerializer):
 | 
			
		|||
        Returns a list of validator callables.
 | 
			
		||||
        """
 | 
			
		||||
        # Used by the lazily-evaluated `validators` property.
 | 
			
		||||
        return getattr(getattr(self, 'Meta', None), 'validators', [])
 | 
			
		||||
        meta = getattr(self, 'Meta', None)
 | 
			
		||||
        validators = getattr(meta, 'validators', None)
 | 
			
		||||
        return validators[:] if validators else []
 | 
			
		||||
 | 
			
		||||
    def get_initial(self):
 | 
			
		||||
        if hasattr(self, 'initial_data'):
 | 
			
		||||
| 
						 | 
				
			
			@ -696,7 +698,7 @@ class ModelSerializer(Serializer):
 | 
			
		|||
    you need you should either declare the extra/differing fields explicitly on
 | 
			
		||||
    the serializer class, or simply use a `Serializer` class.
 | 
			
		||||
    """
 | 
			
		||||
    _field_mapping = ClassLookupDict({
 | 
			
		||||
    serializer_field_mapping = {
 | 
			
		||||
        models.AutoField: IntegerField,
 | 
			
		||||
        models.BigIntegerField: IntegerField,
 | 
			
		||||
        models.BooleanField: BooleanField,
 | 
			
		||||
| 
						 | 
				
			
			@ -719,8 +721,10 @@ class ModelSerializer(Serializer):
 | 
			
		|||
        models.TextField: CharField,
 | 
			
		||||
        models.TimeField: TimeField,
 | 
			
		||||
        models.URLField: URLField,
 | 
			
		||||
    })
 | 
			
		||||
    _related_class = PrimaryKeyRelatedField
 | 
			
		||||
    }
 | 
			
		||||
    serializer_related_class = PrimaryKeyRelatedField
 | 
			
		||||
 | 
			
		||||
    # Default `create` and `update` behavior...
 | 
			
		||||
 | 
			
		||||
    def create(self, validated_data):
 | 
			
		||||
        """
 | 
			
		||||
| 
						 | 
				
			
			@ -791,69 +795,81 @@ class ModelSerializer(Serializer):
 | 
			
		|||
 | 
			
		||||
        return instance
 | 
			
		||||
 | 
			
		||||
    def get_validators(self):
 | 
			
		||||
        # If the validators have been declared explicitly then use that.
 | 
			
		||||
        validators = getattr(getattr(self, 'Meta', None), 'validators', None)
 | 
			
		||||
        if validators is not None:
 | 
			
		||||
            return validators
 | 
			
		||||
 | 
			
		||||
        # Determine the default set of validators.
 | 
			
		||||
        validators = []
 | 
			
		||||
        model_class = self.Meta.model
 | 
			
		||||
        field_names = set([
 | 
			
		||||
            field.source for field in self.fields.values()
 | 
			
		||||
            if (field.source != '*') and ('.' not in field.source)
 | 
			
		||||
        ])
 | 
			
		||||
 | 
			
		||||
        # 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
 | 
			
		||||
    # Determine the fields to apply...
 | 
			
		||||
 | 
			
		||||
    def get_fields(self):
 | 
			
		||||
        declared_fields = copy.deepcopy(self._declared_fields)
 | 
			
		||||
        """
 | 
			
		||||
        Return the dict of field names -> field instances that should be
 | 
			
		||||
        used for `self.fields` when instantiating the serializer.
 | 
			
		||||
        """
 | 
			
		||||
        assert hasattr(self, 'Meta'), (
 | 
			
		||||
            'Class {serializer_class} missing "Meta" attribute'.format(
 | 
			
		||||
                serializer_class=self.__class__.__name__
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
        assert hasattr(self.Meta, 'model'), (
 | 
			
		||||
            'Class {serializer_class} missing "Meta.model" attribute'.format(
 | 
			
		||||
                serializer_class=self.__class__.__name__
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        ret = OrderedDict()
 | 
			
		||||
        declared_fields = copy.deepcopy(self._declared_fields)
 | 
			
		||||
        model = getattr(self.Meta, 'model')
 | 
			
		||||
        depth = getattr(self.Meta, 'depth', 0)
 | 
			
		||||
 | 
			
		||||
        if depth is not None:
 | 
			
		||||
            assert depth >= 0, "'depth' may not be negative."
 | 
			
		||||
            assert depth <= 10, "'depth' may not be greater than 10."
 | 
			
		||||
 | 
			
		||||
        # Retrieve metadata about fields & relationships on the model class.
 | 
			
		||||
        info = model_meta.get_field_info(model)
 | 
			
		||||
        field_names = self.get_field_names(declared_fields, info)
 | 
			
		||||
 | 
			
		||||
        # Determine any extra field arguments and hidden fields that
 | 
			
		||||
        # should be included
 | 
			
		||||
        extra_kwargs = self.get_extra_kwargs()
 | 
			
		||||
        extra_kwargs, hidden_fields = self.get_uniqueness_extra_kwargs(
 | 
			
		||||
            field_names, declared_fields, extra_kwargs
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        # Determine the fields that should be included on the serializer.
 | 
			
		||||
        fields = OrderedDict()
 | 
			
		||||
 | 
			
		||||
        for field_name in field_names:
 | 
			
		||||
            # If the field is explicitly declared on the class then use that.
 | 
			
		||||
            if field_name in declared_fields:
 | 
			
		||||
                fields[field_name] = declared_fields[field_name]
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            # Determine the serializer field class and keyword arguments.
 | 
			
		||||
            field_class, field_kwargs = self.build_field(
 | 
			
		||||
                field_name, info, model, depth
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            # Include any kwargs defined in `Meta.extra_kwargs`
 | 
			
		||||
            field_kwargs = self.build_field_kwargs(
 | 
			
		||||
                field_kwargs, extra_kwargs, field_name
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            # Create the serializer field.
 | 
			
		||||
            fields[field_name] = field_class(**field_kwargs)
 | 
			
		||||
 | 
			
		||||
        # Add in any hidden fields.
 | 
			
		||||
        fields.update(hidden_fields)
 | 
			
		||||
 | 
			
		||||
        return fields
 | 
			
		||||
 | 
			
		||||
    # Methods for determining the set of field names to include...
 | 
			
		||||
 | 
			
		||||
    def get_field_names(self, declared_fields, info):
 | 
			
		||||
        """
 | 
			
		||||
        Returns the list of all field names that should be created when
 | 
			
		||||
        instantiating this serializer class. This is based on the default
 | 
			
		||||
        set of fields, but also takes into account the `Meta.fields` or
 | 
			
		||||
        `Meta.exclude` options if they have been specified.
 | 
			
		||||
        """
 | 
			
		||||
        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', {})
 | 
			
		||||
 | 
			
		||||
        if fields and not isinstance(fields, (list, tuple)):
 | 
			
		||||
            raise TypeError(
 | 
			
		||||
| 
						 | 
				
			
			@ -867,192 +883,191 @@ class ModelSerializer(Serializer):
 | 
			
		|||
                type(exclude).__name__
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        assert not (fields and exclude), "Cannot set both 'fields' and 'exclude'."
 | 
			
		||||
        assert not (fields and exclude), (
 | 
			
		||||
            "Cannot set both 'fields' and 'exclude' options on "
 | 
			
		||||
            "serializer {serializer_class}.".format(
 | 
			
		||||
                serializer_class=self.__class__.__name__
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        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:
 | 
			
		||||
                    assert field_name in fields, (
 | 
			
		||||
                        'The field in the `exclude` option must be a model field. Got %s.' %
 | 
			
		||||
                        field_name
 | 
			
		||||
        if fields is not None:
 | 
			
		||||
            # Ensure that all declared fields have also been included in the
 | 
			
		||||
            # `Meta.fields` option.
 | 
			
		||||
            for field_name in declared_fields:
 | 
			
		||||
                assert field_name in fields, (
 | 
			
		||||
                    "The field '{field_name}' was declared on serializer "
 | 
			
		||||
                    "{serializer_class}, but has not been included in the "
 | 
			
		||||
                    "'fields' option.".format(
 | 
			
		||||
                        field_name=field_name,
 | 
			
		||||
                        serializer_class=self.__class__.__name__
 | 
			
		||||
                    )
 | 
			
		||||
                    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 referred 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) and not issubclass(field_cls, ChoiceField):
 | 
			
		||||
                    # `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__)
 | 
			
		||||
                )
 | 
			
		||||
            return fields
 | 
			
		||||
 | 
			
		||||
            # Check that any fields declared on the class are
 | 
			
		||||
            # also explicitly 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__)
 | 
			
		||||
        # Use the default set of field names if `Meta.fields` is not specified.
 | 
			
		||||
        fields = self.get_default_field_names(declared_fields, info)
 | 
			
		||||
 | 
			
		||||
        if exclude is not None:
 | 
			
		||||
            # If `Meta.exclude` is included, then remove those fields.
 | 
			
		||||
            for field_name in exclude:
 | 
			
		||||
                assert field_name in fields, (
 | 
			
		||||
                    "The field '{field_name}' was include on serializer "
 | 
			
		||||
                    "{serializer_class} in the 'exclude' option, but does "
 | 
			
		||||
                    "not match any model field.".format(
 | 
			
		||||
                        field_name=field_name,
 | 
			
		||||
                        serializer_class=self.__class__.__name__
 | 
			
		||||
                    )
 | 
			
		||||
                )
 | 
			
		||||
                fields.remove(field_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)
 | 
			
		||||
        return fields
 | 
			
		||||
 | 
			
		||||
            if extras.get('default') and kwargs.get('required') is False:
 | 
			
		||||
                kwargs.pop('required')
 | 
			
		||||
    def get_default_field_names(self, declared_fields, model_info):
 | 
			
		||||
        """
 | 
			
		||||
        Return the default list of field names that will be used if the
 | 
			
		||||
        `Meta.fields` option is not specified.
 | 
			
		||||
        """
 | 
			
		||||
        return (
 | 
			
		||||
            [model_info.pk.name] +
 | 
			
		||||
            list(declared_fields.keys()) +
 | 
			
		||||
            list(model_info.fields.keys()) +
 | 
			
		||||
            list(model_info.forward_relations.keys())
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
            kwargs.update(extras)
 | 
			
		||||
    # Methods for constructing serializer fields...
 | 
			
		||||
 | 
			
		||||
            # Create the serializer field.
 | 
			
		||||
            ret[field_name] = field_cls(**kwargs)
 | 
			
		||||
    def build_field(self, field_name, info, model_class, nested_depth):
 | 
			
		||||
        """
 | 
			
		||||
        Return a two tuple of (cls, kwargs) to build a serializer field with.
 | 
			
		||||
        """
 | 
			
		||||
        if field_name in info.fields_and_pk:
 | 
			
		||||
            model_field = info.fields_and_pk[field_name]
 | 
			
		||||
            return self.build_standard_field(field_name, model_field)
 | 
			
		||||
 | 
			
		||||
        for field_name, field in hidden_fields.items():
 | 
			
		||||
            ret[field_name] = field
 | 
			
		||||
        elif field_name in info.relations:
 | 
			
		||||
            relation_info = info.relations[field_name]
 | 
			
		||||
            if not nested_depth:
 | 
			
		||||
                return self.build_relational_field(field_name, relation_info)
 | 
			
		||||
            else:
 | 
			
		||||
                return self.build_nested_field(field_name, relation_info, nested_depth)
 | 
			
		||||
 | 
			
		||||
        return ret
 | 
			
		||||
        elif hasattr(model_class, field_name):
 | 
			
		||||
            return self.build_property_field(field_name, model_class)
 | 
			
		||||
 | 
			
		||||
        elif field_name == api_settings.URL_FIELD_NAME:
 | 
			
		||||
            return self.build_url_field(field_name, model_class)
 | 
			
		||||
 | 
			
		||||
        return self.build_unknown_field(field_name, model_class)
 | 
			
		||||
 | 
			
		||||
    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 = 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 = ChoiceField
 | 
			
		||||
        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)
 | 
			
		||||
 | 
			
		||||
        return field_class, field_kwargs
 | 
			
		||||
 | 
			
		||||
    def build_relational_field(self, field_name, relation_info):
 | 
			
		||||
        """
 | 
			
		||||
        Create fields for forward and reverse relationships.
 | 
			
		||||
        """
 | 
			
		||||
        field_class = self.serializer_related_class
 | 
			
		||||
        field_kwargs = get_relation_kwargs(field_name, relation_info)
 | 
			
		||||
 | 
			
		||||
        # `view_name` is only valid for hyperlinked relationships.
 | 
			
		||||
        if not issubclass(field_class, HyperlinkedRelatedField):
 | 
			
		||||
            field_kwargs.pop('view_name', None)
 | 
			
		||||
 | 
			
		||||
        return field_class, field_kwargs
 | 
			
		||||
 | 
			
		||||
    def build_nested_field(self, field_name, relation_info, nested_depth):
 | 
			
		||||
        """
 | 
			
		||||
        Create nested fields for forward and reverse relationships.
 | 
			
		||||
        """
 | 
			
		||||
        class NestedSerializer(ModelSerializer):
 | 
			
		||||
            class Meta:
 | 
			
		||||
                model = relation_info.related_model
 | 
			
		||||
                depth = nested_depth
 | 
			
		||||
 | 
			
		||||
        field_class = NestedSerializer
 | 
			
		||||
        field_kwargs = get_nested_relation_kwargs(relation_info)
 | 
			
		||||
 | 
			
		||||
        return field_class, field_kwargs
 | 
			
		||||
 | 
			
		||||
    def build_property_field(self, field_name, model_class):
 | 
			
		||||
        """
 | 
			
		||||
        Create a read only field for model methods and properties.
 | 
			
		||||
        """
 | 
			
		||||
        field_class = ReadOnlyField
 | 
			
		||||
        field_kwargs = {}
 | 
			
		||||
 | 
			
		||||
        return field_class, field_kwargs
 | 
			
		||||
 | 
			
		||||
    def build_url_field(self, field_name, model_class):
 | 
			
		||||
        """
 | 
			
		||||
        Create a field representing the object's own URL.
 | 
			
		||||
        """
 | 
			
		||||
        field_class = HyperlinkedIdentityField
 | 
			
		||||
        field_kwargs = get_url_kwargs(model_class)
 | 
			
		||||
 | 
			
		||||
        return field_class, field_kwargs
 | 
			
		||||
 | 
			
		||||
    def build_unknown_field(self, field_name, model_class):
 | 
			
		||||
        """
 | 
			
		||||
        Raise an error on any unknown fields.
 | 
			
		||||
        """
 | 
			
		||||
        raise ImproperlyConfigured(
 | 
			
		||||
            'Field name `%s` is not valid for model `%s`.' %
 | 
			
		||||
            (field_name, model_class.__name__)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def build_field_kwargs(self, kwargs, extra_kwargs, field_name):
 | 
			
		||||
        """
 | 
			
		||||
        Include an 'extra_kwargs' that have been included for this field,
 | 
			
		||||
        possibly removing any incompatible existing keyword arguments.
 | 
			
		||||
        """
 | 
			
		||||
        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)
 | 
			
		||||
 | 
			
		||||
        return kwargs
 | 
			
		||||
 | 
			
		||||
    # Methods for determining additional keyword arguments to apply...
 | 
			
		||||
 | 
			
		||||
    def get_extra_kwargs(self):
 | 
			
		||||
        """
 | 
			
		||||
        Return a dictionary mapping field names to a dictionary of
 | 
			
		||||
        additional keyword arguments.
 | 
			
		||||
        """
 | 
			
		||||
        extra_kwargs = getattr(self.Meta, 'extra_kwargs', {})
 | 
			
		||||
 | 
			
		||||
    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:
 | 
			
		||||
| 
						 | 
				
			
			@ -1100,21 +1115,202 @@ class ModelSerializer(Serializer):
 | 
			
		|||
 | 
			
		||||
        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_uniqueness_extra_kwargs(self, field_names, declared_fields, extra_kwargs):
 | 
			
		||||
        """
 | 
			
		||||
        Return any additional field options that need to be included as a
 | 
			
		||||
        result of uniqueness constraints on the model. This is returned as
 | 
			
		||||
        a two-tuple of:
 | 
			
		||||
 | 
			
		||||
        ('dict of updated extra kwargs', 'mapping of hidden fields')
 | 
			
		||||
        """
 | 
			
		||||
        model = getattr(self.Meta, 'model')
 | 
			
		||||
        model_fields = self._get_model_fields(
 | 
			
		||||
            field_names, declared_fields, extra_kwargs
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def _get_nested_class(self, nested_depth, relation_info):
 | 
			
		||||
        class NestedSerializer(ModelSerializer):
 | 
			
		||||
            class Meta:
 | 
			
		||||
                model = relation_info.related
 | 
			
		||||
                depth = nested_depth
 | 
			
		||||
        # 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.
 | 
			
		||||
        unique_constraint_names = set()
 | 
			
		||||
 | 
			
		||||
        return NestedSerializer
 | 
			
		||||
        for model_field in model_fields.values():
 | 
			
		||||
            # 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(field_names).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.
 | 
			
		||||
        hidden_fields = {}
 | 
			
		||||
        uniqueness_extra_kwargs = {}
 | 
			
		||||
 | 
			
		||||
        for unique_constraint_name in unique_constraint_names:
 | 
			
		||||
            # Get the model field that is referred 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_fields:
 | 
			
		||||
                # The corresponding field is present in the serializer
 | 
			
		||||
                if default is empty:
 | 
			
		||||
                    uniqueness_extra_kwargs[unique_constraint_name] = {'required': True}
 | 
			
		||||
                else:
 | 
			
		||||
                    uniqueness_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)
 | 
			
		||||
 | 
			
		||||
        # Update `extra_kwargs` with any new options.
 | 
			
		||||
        for key, value in uniqueness_extra_kwargs.items():
 | 
			
		||||
            if key in extra_kwargs:
 | 
			
		||||
                extra_kwargs[key].update(value)
 | 
			
		||||
            else:
 | 
			
		||||
                extra_kwargs[key] = value
 | 
			
		||||
 | 
			
		||||
        return extra_kwargs, hidden_fields
 | 
			
		||||
 | 
			
		||||
    def _get_model_fields(self, field_names, declared_fields, extra_kwargs):
 | 
			
		||||
        """
 | 
			
		||||
        Returns all the model fields that are being mapped to by fields
 | 
			
		||||
        on the serializer class.
 | 
			
		||||
        Returned as a dict of 'model field name' -> 'model field'.
 | 
			
		||||
        Used internally by `get_uniqueness_field_options`.
 | 
			
		||||
        """
 | 
			
		||||
        model = getattr(self.Meta, 'model')
 | 
			
		||||
        model_fields = {}
 | 
			
		||||
 | 
			
		||||
        for field_name in field_names:
 | 
			
		||||
            if field_name in declared_fields:
 | 
			
		||||
                # If the field is declared on the serializer
 | 
			
		||||
                field = declared_fields[field_name]
 | 
			
		||||
                source = field.source or field_name
 | 
			
		||||
            else:
 | 
			
		||||
                try:
 | 
			
		||||
                    source = extra_kwargs[field_name]['source']
 | 
			
		||||
                except KeyError:
 | 
			
		||||
                    source = field_name
 | 
			
		||||
 | 
			
		||||
            if '.' in source or source == '*':
 | 
			
		||||
                # Model fields will always have a simple source mapping,
 | 
			
		||||
                # they can't be nested attribute lookups.
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            try:
 | 
			
		||||
                model_fields[source] = model._meta.get_field(source)
 | 
			
		||||
            except FieldDoesNotExist:
 | 
			
		||||
                pass
 | 
			
		||||
 | 
			
		||||
        return model_fields
 | 
			
		||||
 | 
			
		||||
    # Determine the validators to apply...
 | 
			
		||||
 | 
			
		||||
    def get_validators(self):
 | 
			
		||||
        """
 | 
			
		||||
        Determine the set of validators to use when instantiating serializer.
 | 
			
		||||
        """
 | 
			
		||||
        # If the validators have been declared explicitly then use that.
 | 
			
		||||
        validators = getattr(getattr(self, 'Meta', None), 'validators', None)
 | 
			
		||||
        if validators is not None:
 | 
			
		||||
            return validators[:]
 | 
			
		||||
 | 
			
		||||
        # Otherwise use the default set of validators.
 | 
			
		||||
        return (
 | 
			
		||||
            self.get_unique_together_validators() +
 | 
			
		||||
            self.get_unique_for_date_validators()
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def get_unique_together_validators(self):
 | 
			
		||||
        """
 | 
			
		||||
        Determine a default set of validators for any unique_together contraints.
 | 
			
		||||
        """
 | 
			
		||||
        model_class_inheritance_tree = (
 | 
			
		||||
            [self.Meta.model] +
 | 
			
		||||
            list(self.Meta.model._meta.parents.keys())
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        # The field names we're passing though here only include fields
 | 
			
		||||
        # which may map onto a model field. Any dotted field name lookups
 | 
			
		||||
        # cannot map to a field, and must be a traversal, so we're not
 | 
			
		||||
        # including those.
 | 
			
		||||
        field_names = set([
 | 
			
		||||
            field.source for field in self.fields.values()
 | 
			
		||||
            if (field.source != '*') and ('.' not in field.source)
 | 
			
		||||
        ])
 | 
			
		||||
 | 
			
		||||
        # Note that we make sure to check `unique_together` both on the
 | 
			
		||||
        # base model class, but also on any parent classes.
 | 
			
		||||
        validators = []
 | 
			
		||||
        for parent_class in model_class_inheritance_tree:
 | 
			
		||||
            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)
 | 
			
		||||
        return validators
 | 
			
		||||
 | 
			
		||||
    def get_unique_for_date_validators(self):
 | 
			
		||||
        """
 | 
			
		||||
        Determine a default set of validators for the following contraints:
 | 
			
		||||
 | 
			
		||||
        * unique_for_date
 | 
			
		||||
        * unique_for_month
 | 
			
		||||
        * unique_for_year
 | 
			
		||||
        """
 | 
			
		||||
        info = model_meta.get_field_info(self.Meta.model)
 | 
			
		||||
        default_manager = self.Meta.model._default_manager
 | 
			
		||||
        field_names = [field.source for field in self.fields.values()]
 | 
			
		||||
 | 
			
		||||
        validators = []
 | 
			
		||||
 | 
			
		||||
        for field_name, field in info.fields_and_pk.items():
 | 
			
		||||
            if field.unique_for_date and field_name in field_names:
 | 
			
		||||
                validator = UniqueForDateValidator(
 | 
			
		||||
                    queryset=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=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=default_manager,
 | 
			
		||||
                    field=field_name,
 | 
			
		||||
                    date_field=field.unique_for_year
 | 
			
		||||
                )
 | 
			
		||||
                validators.append(validator)
 | 
			
		||||
 | 
			
		||||
        return validators
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class HyperlinkedModelSerializer(ModelSerializer):
 | 
			
		||||
| 
						 | 
				
			
			@ -1125,9 +1321,13 @@ class HyperlinkedModelSerializer(ModelSerializer):
 | 
			
		|||
    * A 'url' field is included instead of the 'id' field.
 | 
			
		||||
    * Relationships to other instances are hyperlinks, instead of primary keys.
 | 
			
		||||
    """
 | 
			
		||||
    _related_class = HyperlinkedRelatedField
 | 
			
		||||
    serializer_related_class = HyperlinkedRelatedField
 | 
			
		||||
 | 
			
		||||
    def _get_default_field_names(self, declared_fields, model_info):
 | 
			
		||||
    def get_default_field_names(self, declared_fields, model_info):
 | 
			
		||||
        """
 | 
			
		||||
        Return the default list of field names that will be used if the
 | 
			
		||||
        `Meta.fields` option is not specified.
 | 
			
		||||
        """
 | 
			
		||||
        return (
 | 
			
		||||
            [api_settings.URL_FIELD_NAME] +
 | 
			
		||||
            list(declared_fields.keys()) +
 | 
			
		||||
| 
						 | 
				
			
			@ -1135,10 +1335,16 @@ class HyperlinkedModelSerializer(ModelSerializer):
 | 
			
		|||
            list(model_info.forward_relations.keys())
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def _get_nested_class(self, nested_depth, relation_info):
 | 
			
		||||
    def build_nested_field(self, field_name, relation_info, nested_depth):
 | 
			
		||||
        """
 | 
			
		||||
        Create nested fields for forward and reverse relationships.
 | 
			
		||||
        """
 | 
			
		||||
        class NestedSerializer(HyperlinkedModelSerializer):
 | 
			
		||||
            class Meta:
 | 
			
		||||
                model = relation_info.related
 | 
			
		||||
                depth = nested_depth
 | 
			
		||||
                model = relation_info.related_model
 | 
			
		||||
                depth = nested_depth - 1
 | 
			
		||||
 | 
			
		||||
        return NestedSerializer
 | 
			
		||||
        field_class = NestedSerializer
 | 
			
		||||
        field_kwargs = get_nested_relation_kwargs(relation_info)
 | 
			
		||||
 | 
			
		||||
        return field_class, field_kwargs
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,7 +24,7 @@ FieldInfo = namedtuple('FieldResult', [
 | 
			
		|||
 | 
			
		||||
RelationInfo = namedtuple('RelationInfo', [
 | 
			
		||||
    'model_field',
 | 
			
		||||
    'related',
 | 
			
		||||
    'related_model',
 | 
			
		||||
    'to_many',
 | 
			
		||||
    'has_through_model'
 | 
			
		||||
])
 | 
			
		||||
| 
						 | 
				
			
			@ -77,7 +77,7 @@ def get_field_info(model):
 | 
			
		|||
    for field in [field for field in opts.fields if field.serialize and field.rel]:
 | 
			
		||||
        forward_relations[field.name] = RelationInfo(
 | 
			
		||||
            model_field=field,
 | 
			
		||||
            related=_resolve_model(field.rel.to),
 | 
			
		||||
            related_model=_resolve_model(field.rel.to),
 | 
			
		||||
            to_many=False,
 | 
			
		||||
            has_through_model=False
 | 
			
		||||
        )
 | 
			
		||||
| 
						 | 
				
			
			@ -86,7 +86,7 @@ def get_field_info(model):
 | 
			
		|||
    for field in [field for field in opts.many_to_many if field.serialize]:
 | 
			
		||||
        forward_relations[field.name] = RelationInfo(
 | 
			
		||||
            model_field=field,
 | 
			
		||||
            related=_resolve_model(field.rel.to),
 | 
			
		||||
            related_model=_resolve_model(field.rel.to),
 | 
			
		||||
            to_many=True,
 | 
			
		||||
            has_through_model=(
 | 
			
		||||
                not field.rel.through._meta.auto_created
 | 
			
		||||
| 
						 | 
				
			
			@ -99,7 +99,7 @@ def get_field_info(model):
 | 
			
		|||
        accessor_name = relation.get_accessor_name()
 | 
			
		||||
        reverse_relations[accessor_name] = RelationInfo(
 | 
			
		||||
            model_field=None,
 | 
			
		||||
            related=relation.model,
 | 
			
		||||
            related_model=relation.model,
 | 
			
		||||
            to_many=relation.field.rel.multiple,
 | 
			
		||||
            has_through_model=False
 | 
			
		||||
        )
 | 
			
		||||
| 
						 | 
				
			
			@ -109,7 +109,7 @@ def get_field_info(model):
 | 
			
		|||
        accessor_name = relation.get_accessor_name()
 | 
			
		||||
        reverse_relations[accessor_name] = RelationInfo(
 | 
			
		||||
            model_field=None,
 | 
			
		||||
            related=relation.model,
 | 
			
		||||
            related_model=relation.model,
 | 
			
		||||
            to_many=True,
 | 
			
		||||
            has_through_model=(
 | 
			
		||||
                (getattr(relation.field.rel, 'through', None) is not None)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -206,7 +206,7 @@ class TestRegularFieldMappings(TestCase):
 | 
			
		|||
 | 
			
		||||
        with self.assertRaises(ImproperlyConfigured) as excinfo:
 | 
			
		||||
            TestSerializer().fields
 | 
			
		||||
        expected = 'Field name `invalid` is not valid for model `ModelBase`.'
 | 
			
		||||
        expected = 'Field name `invalid` is not valid for model `RegularFieldsModel`.'
 | 
			
		||||
        assert str(excinfo.exception) == expected
 | 
			
		||||
 | 
			
		||||
    def test_missing_field(self):
 | 
			
		||||
| 
						 | 
				
			
			@ -221,11 +221,11 @@ class TestRegularFieldMappings(TestCase):
 | 
			
		|||
                model = RegularFieldsModel
 | 
			
		||||
                fields = ('auto_field',)
 | 
			
		||||
 | 
			
		||||
        with self.assertRaises(ImproperlyConfigured) as excinfo:
 | 
			
		||||
        with self.assertRaises(AssertionError) as excinfo:
 | 
			
		||||
            TestSerializer().fields
 | 
			
		||||
        expected = (
 | 
			
		||||
            'Field `missing` has been declared on serializer '
 | 
			
		||||
            '`TestSerializer`, but is missing from `Meta.fields`.'
 | 
			
		||||
            "The field 'missing' was declared on serializer TestSerializer, "
 | 
			
		||||
            "but has not been included in the 'fields' option."
 | 
			
		||||
        )
 | 
			
		||||
        assert str(excinfo.exception) == expected
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -607,5 +607,5 @@ class TestSerializerMetaClass(TestCase):
 | 
			
		|||
        exception = result.exception
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            str(exception),
 | 
			
		||||
            "Cannot set both 'fields' and 'exclude'."
 | 
			
		||||
            "Cannot set both 'fields' and 'exclude' options on serializer ExampleSerializer."
 | 
			
		||||
        )
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user