mirror of
				https://github.com/encode/django-rest-framework.git
				synced 2025-11-04 09:57:55 +03:00 
			
		
		
		
	request.data attribute
This commit is contained in:
		
							parent
							
								
									417fe1b675
								
							
						
					
					
						commit
						2859eaf524
					
				| 
						 | 
					@ -4,36 +4,65 @@ See the [Version 3.0 GitHub issue](https://github.com/tomchristie/django-rest-fr
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# REST framework 3.0
 | 
					# REST framework 3.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
**Note incremental nature, discuss upgrading.**
 | 
					**TODO**: Note incremental nature, discuss upgrading, motivation, features.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Motivation
 | 
					* Serializer reprs.
 | 
				
			||||||
 | 
					* Non-magical model serializers.
 | 
				
			||||||
**TODO**
 | 
					* Base serializer class.
 | 
				
			||||||
 | 
					* Clean logic in views, serializers, fields.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
---
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Request objects
 | 
					## Request objects
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#### The `request.data` property.
 | 
					#### The `.data` and `.query_params` properties.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
**TODO**
 | 
					The usage of `request.DATA` and `request.FILES` is now discouraged in favor of a single `request.data` attribute that contains *all* the parsed data.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#### The parser API.
 | 
					Having seperate attributes is reasonable for web applications that only ever parse URL encoded or MultiPart requests, but makes less sense for the general-purpose request parsing that REST framework supports.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
**TODO**
 | 
					You may now pass all the request data to a serializer class in a single argument:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ExampleSerializer(data=request.data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Instead of passing the files argument seperately:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Don't do this...
 | 
				
			||||||
 | 
					    ExampleSerializer(data=request.DATA, files=request.FILES)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The usage of `request.QUERY_PARAMS` is now discouraged in favor of the lowercased `request.query_params`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Serializers
 | 
					## Serializers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#### Single-step object creation.
 | 
					#### Single-step object creation.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### The `.create()` and `.update()` methods.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
**TODO**: Drop `.restore_object()`, use `.create()` and `.update()` which should save the instance.
 | 
					**TODO**: Drop `.restore_object()`, use `.create()` and `.update()` which should save the instance.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
**TODO**: Drop`.object`, use `.validated_data` or get the instance with `.save()`.
 | 
					#### Use `.validated_data` instead of `.object`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#### The `BaseSerializer` class.
 | 
					You must now use the `.validated_data` attribute if you need to inspect the data before saving, rather than using the `.object` attribute, which no longer exists.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
**TODO**
 | 
					For example the following code *is no longer valid*:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if serializer.is_valid():
 | 
				
			||||||
 | 
					        name = serializer.object.name  # Inspect validated field data.
 | 
				
			||||||
 | 
					        logging.info('Creating ticket "%s"' % name)
 | 
				
			||||||
 | 
					        serializer.object.user = request.user  # Include the user when saving.
 | 
				
			||||||
 | 
					        serializer.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Instead of using `.object` to inspect a partially constructed instance, you would now use `.validated_data` to inspect the cleaned incoming values. Also you can't set extra attributes on the instance directly, but instead pass them to the `.save()` method using the `extras` keyword argument.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The corresponding code would now look like this:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if serializer.is_valid():
 | 
				
			||||||
 | 
					        name = serializer.validated_data['name']  # Inspect validated field data.
 | 
				
			||||||
 | 
					        logging.info('Creating ticket "%s"' % name)
 | 
				
			||||||
 | 
					        extras = {'user': request.user}  # Include the user when saving.
 | 
				
			||||||
 | 
					        serializer.save(extras=extras)
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
#### Always use `fields`, not `exclude`.
 | 
					#### Always use `fields`, not `exclude`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The `exclude` option is no longer available. You should use the more explicit `fields` option instead.
 | 
					The `exclude` option is no longer available. You should use the more explicit `fields` option instead.
 | 
				
			||||||
| 
						 | 
					@ -111,42 +140,287 @@ These fields will be mapped to `serializers.ReadOnlyField()` instances.
 | 
				
			||||||
        message = CharField(max_length=1000)
 | 
					        message = CharField(max_length=1000)
 | 
				
			||||||
        expiry_date = ReadOnlyField()
 | 
					        expiry_date = ReadOnlyField()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### The `ListSerializer` class.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The `ListSerializer` class has now been added, and allows you to create base serializer classes for only accepting multiple inputs.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class MultipleUserSerializer(ListSerializer):
 | 
				
			||||||
 | 
					        child = UserSerializer()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					You can also still use the `many=True` argument to serializer classes. It's worth noting that `many=True` argument transparently creates a `ListSerializer` instance, allowing the validation logic for list and non-list data to be cleanly seperated in the REST framework codebase. 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					See also the new `ListField` class, which validates input in the same way, but does not include the serializer interfaces of `.is_valid()`, `.data`, `.save()` and so on.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### The `BaseSerializer` class.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					REST framework now includes a simple `BaseSerializer` class that can be used to easily support alternative serialization and deserialization styles.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This class implements the same basic API as the `Serializer` class:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* `.data` - Returns the outgoing primative representation.
 | 
				
			||||||
 | 
					* `.is_valid()` - Deserializes and validates incoming data.
 | 
				
			||||||
 | 
					* `.validated_data` - Returns the validated incoming data.
 | 
				
			||||||
 | 
					* `.errors` - Returns an errors during validation.
 | 
				
			||||||
 | 
					* `.save()` - Persists the validated data into an object instance.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					There are four mathods that can be overriding, depending on what functionality you want the serializer class to support:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* `.to_representation()` - Override this to support serialization, for read operations.
 | 
				
			||||||
 | 
					* `.to_internal_value()` - Override this to support deserialization, for write operations.
 | 
				
			||||||
 | 
					* `.create()` and `.update()` - Overide either or both of these to support saving instances.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					##### Read-only serializers.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					To implement a read-only serializer using the `BaseSerializer` class, we just need to override the `.to_representation()` method. Let's take a look at an example using a simple Django model:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class HighScore(models.Model):
 | 
				
			||||||
 | 
					        created = models.DateTimeField(auto_now_add=True)
 | 
				
			||||||
 | 
					        player_name = models.CharField(max_length=10)
 | 
				
			||||||
 | 
					        score = models.IntegerField()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					It's simple to create a read-only serializer for converting `HighScore` instances into primative data types.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class HighScoreSerializer(serializers.BaseSerializer):
 | 
				
			||||||
 | 
					        def to_representation(self, obj):
 | 
				
			||||||
 | 
					            return {
 | 
				
			||||||
 | 
					                'score': obj.score,
 | 
				
			||||||
 | 
					                'player_name': obj.player_name
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					We can now use this class to serialize single `HighScore` instances:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @api_view(['GET'])
 | 
				
			||||||
 | 
					    def high_score(request, pk):
 | 
				
			||||||
 | 
					        instance = HighScore.objects.get(pk=pk)
 | 
				
			||||||
 | 
					        serializer = HighScoreSerializer(instance)
 | 
				
			||||||
 | 
						    return Response(serializer.data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Or use it to serialize multiple instances:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @api_view(['GET'])
 | 
				
			||||||
 | 
					    def all_high_scores(request):
 | 
				
			||||||
 | 
					        queryset = HighScore.objects.order_by('-score')
 | 
				
			||||||
 | 
					        serializer = HighScoreSerializer(queryset, many=True)
 | 
				
			||||||
 | 
						    return Response(serializer.data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					##### Read-write serializers.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					To create a read-write serializer we first need to implement a `.to_internal_value()` method. This method returns the validated values that will be used to construct the object instance, and may raise a `ValidationError` if the supplied data is in an incorrect format.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Once you've implemented `.to_internal_value()`, the basic validation API will be available on the serializer, and you will be able to use `.is_valid()`, `.validated_data` and `.errors`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					If you want to also support `.save()` you'll need to also implement either or both of the `.create()` and `.update()` methods.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Here's a complete example of our previous `HighScoreSerializer`, that's been updated to support both read and write operations.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class HighScoreSerializer(serializers.BaseSerializer):
 | 
				
			||||||
 | 
					        def to_internal_value(self, data):
 | 
				
			||||||
 | 
					            score = data.get('score')
 | 
				
			||||||
 | 
					            player_name = data.get('player_name')
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            # Perform the data validation.
 | 
				
			||||||
 | 
					            if not score:
 | 
				
			||||||
 | 
					                raise ValidationError({
 | 
				
			||||||
 | 
					                    'score': 'This field is required.'
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					            if not player_name:
 | 
				
			||||||
 | 
					                raise ValidationError({
 | 
				
			||||||
 | 
					                    'player_name': 'This field is required.'
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					            if len(player_name) > 10:
 | 
				
			||||||
 | 
					                raise ValidationError({
 | 
				
			||||||
 | 
					                    'player_name': 'May not be more than 10 characters.'
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
								# Return the validated values. This will be available as
 | 
				
			||||||
 | 
								# the `.validated_data` property.
 | 
				
			||||||
 | 
					            return {
 | 
				
			||||||
 | 
					                'score': int(score),
 | 
				
			||||||
 | 
					                'player_name': player_name
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def to_representation(self, obj):
 | 
				
			||||||
 | 
					            return {
 | 
				
			||||||
 | 
					                'score': obj.score,
 | 
				
			||||||
 | 
					                'player_name': obj.player_name
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        def create(self, validated_data):
 | 
				
			||||||
 | 
					            return HighScore.objects.create(**validated_data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### Creating new base classes with `BaseSerializer`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The `BaseSerializer` class is also useful if you want to implement new generic serializer classes for dealing with particular serialization styles or for integrating with different storage backends.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The following class is an example of a generic serializer that can handle coercing aribitrary objects into primative representations. 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class ObjectSerializer(serializers.BaseSerializer):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        A read-only serializer that coerces arbitrary complex objects
 | 
				
			||||||
 | 
					        into primative representations.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        def to_representation(self, obj):
 | 
				
			||||||
 | 
					            for attribute_name in dir(obj):
 | 
				
			||||||
 | 
					                attribute = getattr(obj, attribute_name)
 | 
				
			||||||
 | 
					                if attribute_name('_'):
 | 
				
			||||||
 | 
					                    # Ignore private attributes.
 | 
				
			||||||
 | 
					                    pass
 | 
				
			||||||
 | 
					                elif hasattr(attribute, '__call__'):
 | 
				
			||||||
 | 
					                    # Ignore methods and other callables.
 | 
				
			||||||
 | 
					                    pass
 | 
				
			||||||
 | 
					                elif isinstance(attribute, (str, int, bool, float, type(None))):
 | 
				
			||||||
 | 
					                    # Primative types can be passed through unmodified.
 | 
				
			||||||
 | 
					                    output[attribute_name] = attribute
 | 
				
			||||||
 | 
					                elif isinstance(attribute, list):
 | 
				
			||||||
 | 
					                    # Recursivly deal with items in lists.
 | 
				
			||||||
 | 
					                    output[attribute_name] = [
 | 
				
			||||||
 | 
					                        self.to_representation(item) for item in attribute
 | 
				
			||||||
 | 
					                    ]
 | 
				
			||||||
 | 
					                elif isinstance(attribute, dict):
 | 
				
			||||||
 | 
					                    # Recursivly deal with items in dictionarys.
 | 
				
			||||||
 | 
					                    output[attribute_name] = {
 | 
				
			||||||
 | 
					                        str(key): self.to_representation(value)
 | 
				
			||||||
 | 
					                        for key, value in attribute.items()
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                else:
 | 
				
			||||||
 | 
					                    # Force anything else to its string representation.
 | 
				
			||||||
 | 
					                    output[attribute_name] = str(attribute)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Serializer fields
 | 
					## Serializer fields
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#### The `Field` and `ReadOnly` field classes.
 | 
					#### The `Field` and `ReadOnly` field classes.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
**TODO**
 | 
					There are some minor tweaks to the field base classes.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Previously we had these two base classes:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* `Field` as the base class for read-only fields. A default implementation was included for serializing data.
 | 
				
			||||||
 | 
					* `WriteableField` as the base class for read-write fields.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					We now use the following:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* `Field` is the base class for all fields. It does not include any default implementation for either serializing or deserializing data.
 | 
				
			||||||
 | 
					* `ReadOnlyField` is a concrete implementation for read-only fields that simply returns the attribute value without modification.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### The `required`, `allow_none`, `allow_blank` and `default` arguments.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					REST framework now has more explict and clear control over validating empty values for fields.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Previously the meaning of the `required=False` keyword argument was underspecified. In practice it's use meant that a field could either be not included in the input, or it could be included, but be `None`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					We now have a better seperation, with seperate `required` and `allow_none` arguments.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The following set of arguments are used to control validation of empty values:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* `required=False`: The value does not need to be present in the input, and will not be passed to `.create()` or `.update()` if it is not seen.
 | 
				
			||||||
 | 
					* `default=<value>`: The value does not need to be present in the input, and a default value will be passed to `.create()` or `.update()` if it is not seen.
 | 
				
			||||||
 | 
					* `allow_none=True`: `None` is a valid input.
 | 
				
			||||||
 | 
					* `allow_blank=True`: `''` is valid input. For `CharField` and subclasses only.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Typically you'll want to use `required=False` if the corresponding model field has a default value, and additionally set either `allow_none=True` or `allow_blank=True` if required.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The `default` argument is there if you need it, but you'll more typically want defaults to be set on model fields, rather than serializer fields.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#### Coercing output types.
 | 
					#### Coercing output types.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
**TODO**
 | 
					The previous field implementations did not forcibly coerce returned values into the correct type in many cases. For example, an `IntegerField` would return a string output if the attribute value was a string. We now more strictly coerce to the correct return type, leading to more constrained and expected behavior. 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#### The `ListSerializer` class.
 | 
					#### The `ListField` class.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
**TODO**
 | 
					The `ListField` class has now been added. This field validates list input. It takes a `child` keyword argument which is used to specify the field used to validate each item in the list. For example:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    scores = ListField(child=IntegerField(min_value=0, max_value=100))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					You can also use a declarative style to create new subclasses of `ListField`, like this:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class ScoresField(ListField):
 | 
				
			||||||
 | 
					        child = IntegerField(min_value=0, max_value=100)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					We can now use the `ScoresField` class inside another serializer:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    scores = ScoresField()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					See also the new `ListSerializer` class, which validates input in the same way, but also includes the serializer interfaces of `.is_valid()`, `.data`, `.save()` and so on.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### The `ChoiceField` class may now accept a flat list.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The `ChoiceField` class may now accept a list of choices in addition to the existing style of using a list of pairs of `(name, display_value)`. The following is now valid:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    color = ChoiceField(choices=['red', 'green', 'blue'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#### The `MultipleChoiceField` class.
 | 
					#### The `MultipleChoiceField` class.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
**TODO**
 | 
					The `MultipleChoiceField` class has been added. This field acts like `ChoiceField`, but returns a set, which may include none, one or many of the valid choices.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#### Changes to the custom field API.
 | 
					#### Changes to the custom field API.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
**TODO** `to_representation`, `to_internal_value`.
 | 
					The `from_native(self, value)` and `to_native(self, data)` method names have been replaced with the more obviously named `to_representation(self, value)` and `to_internal_value(self, data)`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#### Explicit `querysets` required on relational fields.
 | 
					The `field_from_native()` and `field_to_native()` methods are removed.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
**TODO**
 | 
					#### Explicit `queryset` required on relational fields.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Previously relational fields that were explicitly declared on a serializer class could omit the queryset argument if (and only if) they were declared on a `ModelSerializer`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This code *would be valid* in `2.4.3`:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class AccountSerializer(serializers.ModelSerializer):
 | 
				
			||||||
 | 
					        organisations = serializers.SlugRelatedField(slug_field='name')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        class Meta:
 | 
				
			||||||
 | 
					            model = Account
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					However this code *would not be valid* in `2.4.3`:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Missing `queryset`
 | 
				
			||||||
 | 
					    class AccountSerializer(serializers.Serializer):
 | 
				
			||||||
 | 
					        organisations = serializers.SlugRelatedField(slug_field='name')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def restore_object(self, attrs, instance=None):
 | 
				
			||||||
 | 
					            # ...
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The queryset argument is now always required for writable relational fields.
 | 
				
			||||||
 | 
					This removes some magic and makes it easier and more obvious to move between implict `ModelSerializer` classes and explicit `Serializer` classes.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class AccountSerializer(serializers.ModelSerializer):
 | 
				
			||||||
 | 
					        organisations = serializers.SlugRelatedField(
 | 
				
			||||||
 | 
					            slug_field='name',
 | 
				
			||||||
 | 
					            queryset=Organisation.objects.all()
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        class Meta:
 | 
				
			||||||
 | 
					            model = Account
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The `queryset` argument is only ever required for writable fields, and is not required or valid for fields with `read_only=True`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#### Optional argument to `SerializerMethodField`.
 | 
					#### Optional argument to `SerializerMethodField`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
**TODO**
 | 
					The argument to `SerializerMethodField` is now optional, and defaults to `get_<field_name>`. For example the following is valid:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class AccountSerializer(serializers.Serializer):
 | 
				
			||||||
 | 
					        # `method_name='get_billing_details'` by default.
 | 
				
			||||||
 | 
					        billing_details = serializers.SerializerMethodField()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def get_billing_details(self, account):
 | 
				
			||||||
 | 
					            return calculate_billing(account)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In order to ensure a consistent code style an assertion error will be raised if you include a redundant method name argument that matches the default method name. For example, the following code *will raise an error*:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    billing_details = serializers.SerializerMethodField('get_billing_details')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### Enforcing consistent `source` usage.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					I've see several codebases that unneccessarily include the `source` argument, setting it to the same value as the field name. This usage is redundant and confusing, making it less obvious that `source` is usually not required.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The following usage will *now raise an error*:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    email = serializers.EmailField(source='email')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Generic views
 | 
					## Generic views
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#### Simplification of view logic.
 | 
					#### Simplification of view logic.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
**TODO**
 | 
					The view logic for the default method handlers has been significantly simplified, due to the new serializers API.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#### Removal of pre/post save hooks. 
 | 
					#### Removal of pre/post save hooks. 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -169,6 +443,20 @@ I would personally recommend that developers treat view instances as immutable o
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#### PUT as create.
 | 
					#### PUT as create.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Allowing `PUT` as create operations is problematic, as it neccessarily exposes information about the existence or non-existance of objects. It's also not obvious that transparently allowing re-creating of previously deleted instances is neccessarily a better default behavior than simply returning `404` responses.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Both styles "`PUT` as 404" and "`PUT` as create" can be valid in different circumstances, but we've now opted for the 404 behavior as the default, due to it being simpler and more obvious.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					If you need to restore the previous behavior you can include the `AllowPUTAsCreateMixin` class in your view. This class can be imported from `rest_framework.mixins`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### Customizing error responses.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The generic views now raise `ValidationError` for invalid data. This exception is then dealt with by the exception handler, rather than the view returning a `400 Bad Request` response directly.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This change means that you can now easily cusomize the style of error responses across your entire API, without having to modify any of the generic views.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## The metadata API
 | 
				
			||||||
 | 
					
 | 
				
			||||||
**TODO**
 | 
					**TODO**
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## API style
 | 
					## API style
 | 
				
			||||||
| 
						 | 
					@ -241,3 +529,17 @@ Or modify it on an individual serializer field, using the `corece_to_string` key
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The default JSON renderer will return float objects for uncoerced `Decimal` instances. This allows you to easily switch between string or float representations for decimals depending on your API design needs.
 | 
					The default JSON renderer will return float objects for uncoerced `Decimal` instances. This allows you to easily switch between string or float representations for decimals depending on your API design needs.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## What's coming next.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					3.0 is an incremental release, and there are several upcoming features that will build on the baseline improvements that it makes.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The 3.1 release is planned to address improvements in the following components:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* Request parsing, mediatypes & the implementation of the browsable API.
 | 
				
			||||||
 | 
					* Introduction of a new pagination API.
 | 
				
			||||||
 | 
					* Better support for API versioning.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The 3.2 release is planned to introduce an alternative admin-style interface to the browsable API.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					You can follow development on the GitHub site, where we use [milestones to indicate planning timescales](https://github.com/tomchristie/django-rest-framework/milestones).
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -16,7 +16,7 @@ class ObtainAuthToken(APIView):
 | 
				
			||||||
    model = Token
 | 
					    model = Token
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def post(self, request):
 | 
					    def post(self, request):
 | 
				
			||||||
        serializer = self.serializer_class(data=request.DATA)
 | 
					        serializer = self.serializer_class(data=request.data)
 | 
				
			||||||
        if serializer.is_valid():
 | 
					        if serializer.is_valid():
 | 
				
			||||||
            user = serializer.validated_data['user']
 | 
					            user = serializer.validated_data['user']
 | 
				
			||||||
            token, created = Token.objects.get_or_create(user=user)
 | 
					            token, created = Token.objects.get_or_create(user=user)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -56,7 +56,7 @@ def get_attribute(instance, attrs):
 | 
				
			||||||
        except AttributeError as exc:
 | 
					        except AttributeError as exc:
 | 
				
			||||||
            try:
 | 
					            try:
 | 
				
			||||||
                return instance[attr]
 | 
					                return instance[attr]
 | 
				
			||||||
            except (KeyError, TypeError):
 | 
					            except (KeyError, TypeError, AttributeError):
 | 
				
			||||||
                raise exc
 | 
					                raise exc
 | 
				
			||||||
    return instance
 | 
					    return instance
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -90,6 +90,7 @@ NOT_READ_ONLY_WRITE_ONLY = 'May not set both `read_only` and `write_only`'
 | 
				
			||||||
NOT_READ_ONLY_REQUIRED = 'May not set both `read_only` and `required`'
 | 
					NOT_READ_ONLY_REQUIRED = 'May not set both `read_only` and `required`'
 | 
				
			||||||
NOT_READ_ONLY_DEFAULT = 'May not set both `read_only` and `default`'
 | 
					NOT_READ_ONLY_DEFAULT = 'May not set both `read_only` and `default`'
 | 
				
			||||||
NOT_REQUIRED_DEFAULT = 'May not set both `required` and `default`'
 | 
					NOT_REQUIRED_DEFAULT = 'May not set both `required` and `default`'
 | 
				
			||||||
 | 
					USE_READONLYFIELD = 'Field(read_only=True) should be ReadOnlyField'
 | 
				
			||||||
MISSING_ERROR_MESSAGE = (
 | 
					MISSING_ERROR_MESSAGE = (
 | 
				
			||||||
    'ValidationError raised by `{class_name}`, but error key `{key}` does '
 | 
					    'ValidationError raised by `{class_name}`, but error key `{key}` does '
 | 
				
			||||||
    'not exist in the `error_messages` dictionary.'
 | 
					    'not exist in the `error_messages` dictionary.'
 | 
				
			||||||
| 
						 | 
					@ -105,9 +106,10 @@ class Field(object):
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    default_validators = []
 | 
					    default_validators = []
 | 
				
			||||||
    default_empty_html = None
 | 
					    default_empty_html = None
 | 
				
			||||||
 | 
					    initial = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, read_only=False, write_only=False,
 | 
					    def __init__(self, read_only=False, write_only=False,
 | 
				
			||||||
                 required=None, default=empty, initial=None, source=None,
 | 
					                 required=None, default=empty, initial=empty, source=None,
 | 
				
			||||||
                 label=None, help_text=None, style=None,
 | 
					                 label=None, help_text=None, style=None,
 | 
				
			||||||
                 error_messages=None, validators=[], allow_null=False):
 | 
					                 error_messages=None, validators=[], allow_null=False):
 | 
				
			||||||
        self._creation_counter = Field._creation_counter
 | 
					        self._creation_counter = Field._creation_counter
 | 
				
			||||||
| 
						 | 
					@ -122,13 +124,14 @@ class Field(object):
 | 
				
			||||||
        assert not (read_only and required), NOT_READ_ONLY_REQUIRED
 | 
					        assert not (read_only and required), NOT_READ_ONLY_REQUIRED
 | 
				
			||||||
        assert not (read_only and default is not empty), NOT_READ_ONLY_DEFAULT
 | 
					        assert not (read_only and default is not empty), NOT_READ_ONLY_DEFAULT
 | 
				
			||||||
        assert not (required and default is not empty), NOT_REQUIRED_DEFAULT
 | 
					        assert not (required and default is not empty), NOT_REQUIRED_DEFAULT
 | 
				
			||||||
 | 
					        assert not (read_only and self.__class__ == Field), USE_READONLYFIELD
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.read_only = read_only
 | 
					        self.read_only = read_only
 | 
				
			||||||
        self.write_only = write_only
 | 
					        self.write_only = write_only
 | 
				
			||||||
        self.required = required
 | 
					        self.required = required
 | 
				
			||||||
        self.default = default
 | 
					        self.default = default
 | 
				
			||||||
        self.source = source
 | 
					        self.source = source
 | 
				
			||||||
        self.initial = initial
 | 
					        self.initial = self.initial if (initial is empty) else initial
 | 
				
			||||||
        self.label = label
 | 
					        self.label = label
 | 
				
			||||||
        self.help_text = help_text
 | 
					        self.help_text = help_text
 | 
				
			||||||
        self.style = {} if style is None else style
 | 
					        self.style = {} if style is None else style
 | 
				
			||||||
| 
						 | 
					@ -146,24 +149,10 @@ class Field(object):
 | 
				
			||||||
        messages.update(error_messages or {})
 | 
					        messages.update(error_messages or {})
 | 
				
			||||||
        self.error_messages = messages
 | 
					        self.error_messages = messages
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __new__(cls, *args, **kwargs):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        When a field is instantiated, we store the arguments that were used,
 | 
					 | 
				
			||||||
        so that we can present a helpful representation of the object.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        instance = super(Field, cls).__new__(cls)
 | 
					 | 
				
			||||||
        instance._args = args
 | 
					 | 
				
			||||||
        instance._kwargs = kwargs
 | 
					 | 
				
			||||||
        return instance
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __deepcopy__(self, memo):
 | 
					 | 
				
			||||||
        args = copy.deepcopy(self._args)
 | 
					 | 
				
			||||||
        kwargs = copy.deepcopy(self._kwargs)
 | 
					 | 
				
			||||||
        return self.__class__(*args, **kwargs)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def bind(self, field_name, parent):
 | 
					    def bind(self, field_name, parent):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Setup the context for the field instance.
 | 
					        Initializes the field name and parent for the field instance.
 | 
				
			||||||
 | 
					        Called when a field is added to the parent serializer instance.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # In order to enforce a consistent style, we error if a redundant
 | 
					        # In order to enforce a consistent style, we error if a redundant
 | 
				
			||||||
| 
						 | 
					@ -244,9 +233,9 @@ class Field(object):
 | 
				
			||||||
        validated data.
 | 
					        validated data.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        if data is empty:
 | 
					        if data is empty:
 | 
				
			||||||
 | 
					            if getattr(self.root, 'partial', False):
 | 
				
			||||||
 | 
					                raise SkipField()
 | 
				
			||||||
            if self.required:
 | 
					            if self.required:
 | 
				
			||||||
                if getattr(self.root, 'partial', False):
 | 
					 | 
				
			||||||
                    raise SkipField()
 | 
					 | 
				
			||||||
                self.fail('required')
 | 
					                self.fail('required')
 | 
				
			||||||
            return self.get_default()
 | 
					            return self.get_default()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -314,6 +303,25 @@ class Field(object):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        return getattr(self.root, '_context', {})
 | 
					        return getattr(self.root, '_context', {})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __new__(cls, *args, **kwargs):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        When a field is instantiated, we store the arguments that were used,
 | 
				
			||||||
 | 
					        so that we can present a helpful representation of the object.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        instance = super(Field, cls).__new__(cls)
 | 
				
			||||||
 | 
					        instance._args = args
 | 
				
			||||||
 | 
					        instance._kwargs = kwargs
 | 
				
			||||||
 | 
					        return instance
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __deepcopy__(self, memo):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        When cloning fields we instantiate using the arguments it was
 | 
				
			||||||
 | 
					        originally created with, rather than copying the complete state.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        args = copy.deepcopy(self._args)
 | 
				
			||||||
 | 
					        kwargs = copy.deepcopy(self._kwargs)
 | 
				
			||||||
 | 
					        return self.__class__(*args, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __repr__(self):
 | 
					    def __repr__(self):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Fields are represented using their initial calling arguments.
 | 
					        Fields are represented using their initial calling arguments.
 | 
				
			||||||
| 
						 | 
					@ -358,6 +366,7 @@ class NullBooleanField(Field):
 | 
				
			||||||
        'invalid': _('`{input}` is not a valid boolean.')
 | 
					        'invalid': _('`{input}` is not a valid boolean.')
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    default_empty_html = None
 | 
					    default_empty_html = None
 | 
				
			||||||
 | 
					    initial = None
 | 
				
			||||||
    TRUE_VALUES = set(('t', 'T', 'true', 'True', 'TRUE', '1', 1, True))
 | 
					    TRUE_VALUES = set(('t', 'T', 'true', 'True', 'TRUE', '1', 1, True))
 | 
				
			||||||
    FALSE_VALUES = set(('f', 'F', 'false', 'False', 'FALSE', '0', 0, 0.0, False))
 | 
					    FALSE_VALUES = set(('f', 'F', 'false', 'False', 'FALSE', '0', 0, 0.0, False))
 | 
				
			||||||
    NULL_VALUES = set(('n', 'N', 'null', 'Null', 'NULL', '', None))
 | 
					    NULL_VALUES = set(('n', 'N', 'null', 'Null', 'NULL', '', None))
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -64,7 +64,7 @@ class DjangoFilterBackend(BaseFilterBackend):
 | 
				
			||||||
        filter_class = self.get_filter_class(view, queryset)
 | 
					        filter_class = self.get_filter_class(view, queryset)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if filter_class:
 | 
					        if filter_class:
 | 
				
			||||||
            return filter_class(request.QUERY_PARAMS, queryset=queryset).qs
 | 
					            return filter_class(request.query_params, queryset=queryset).qs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return queryset
 | 
					        return queryset
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -78,7 +78,7 @@ class SearchFilter(BaseFilterBackend):
 | 
				
			||||||
        Search terms are set by a ?search=... query parameter,
 | 
					        Search terms are set by a ?search=... query parameter,
 | 
				
			||||||
        and may be comma and/or whitespace delimited.
 | 
					        and may be comma and/or whitespace delimited.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        params = request.QUERY_PARAMS.get(self.search_param, '')
 | 
					        params = request.query_params.get(self.search_param, '')
 | 
				
			||||||
        return params.replace(',', ' ').split()
 | 
					        return params.replace(',', ' ').split()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def construct_search(self, field_name):
 | 
					    def construct_search(self, field_name):
 | 
				
			||||||
| 
						 | 
					@ -121,7 +121,7 @@ class OrderingFilter(BaseFilterBackend):
 | 
				
			||||||
        the `ordering_param` value on the OrderingFilter or by
 | 
					        the `ordering_param` value on the OrderingFilter or by
 | 
				
			||||||
        specifying an `ORDERING_PARAM` value in the API settings.
 | 
					        specifying an `ORDERING_PARAM` value in the API settings.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        params = request.QUERY_PARAMS.get(self.ordering_param)
 | 
					        params = request.query_params.get(self.ordering_param)
 | 
				
			||||||
        if params:
 | 
					        if params:
 | 
				
			||||||
            return [param.strip() for param in params.split(',')]
 | 
					            return [param.strip() for param in params.split(',')]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -112,7 +112,7 @@ class GenericAPIView(views.APIView):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        paginator = self.paginator_class(queryset, page_size)
 | 
					        paginator = self.paginator_class(queryset, page_size)
 | 
				
			||||||
        page_kwarg = self.kwargs.get(self.page_kwarg)
 | 
					        page_kwarg = self.kwargs.get(self.page_kwarg)
 | 
				
			||||||
        page_query_param = self.request.QUERY_PARAMS.get(self.page_kwarg)
 | 
					        page_query_param = self.request.query_params.get(self.page_kwarg)
 | 
				
			||||||
        page = page_kwarg or page_query_param or 1
 | 
					        page = page_kwarg or page_query_param or 1
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            page_number = paginator.validate_number(page)
 | 
					            page_number = paginator.validate_number(page)
 | 
				
			||||||
| 
						 | 
					@ -166,7 +166,7 @@ class GenericAPIView(views.APIView):
 | 
				
			||||||
        if self.paginate_by_param:
 | 
					        if self.paginate_by_param:
 | 
				
			||||||
            try:
 | 
					            try:
 | 
				
			||||||
                return strict_positive_int(
 | 
					                return strict_positive_int(
 | 
				
			||||||
                    self.request.QUERY_PARAMS[self.paginate_by_param],
 | 
					                    self.request.query_params[self.paginate_by_param],
 | 
				
			||||||
                    cutoff=self.max_paginate_by
 | 
					                    cutoff=self.max_paginate_by
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
            except (KeyError, ValueError):
 | 
					            except (KeyError, ValueError):
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,7 +18,7 @@ class CreateModelMixin(object):
 | 
				
			||||||
    Create a model instance.
 | 
					    Create a model instance.
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    def create(self, request, *args, **kwargs):
 | 
					    def create(self, request, *args, **kwargs):
 | 
				
			||||||
        serializer = self.get_serializer(data=request.DATA)
 | 
					        serializer = self.get_serializer(data=request.data)
 | 
				
			||||||
        serializer.is_valid(raise_exception=True)
 | 
					        serializer.is_valid(raise_exception=True)
 | 
				
			||||||
        serializer.save()
 | 
					        serializer.save()
 | 
				
			||||||
        headers = self.get_success_headers(serializer.data)
 | 
					        headers = self.get_success_headers(serializer.data)
 | 
				
			||||||
| 
						 | 
					@ -62,7 +62,7 @@ class UpdateModelMixin(object):
 | 
				
			||||||
    def update(self, request, *args, **kwargs):
 | 
					    def update(self, request, *args, **kwargs):
 | 
				
			||||||
        partial = kwargs.pop('partial', False)
 | 
					        partial = kwargs.pop('partial', False)
 | 
				
			||||||
        instance = self.get_object()
 | 
					        instance = self.get_object()
 | 
				
			||||||
        serializer = self.get_serializer(instance, data=request.DATA, partial=partial)
 | 
					        serializer = self.get_serializer(instance, data=request.data, partial=partial)
 | 
				
			||||||
        serializer.is_valid(raise_exception=True)
 | 
					        serializer.is_valid(raise_exception=True)
 | 
				
			||||||
        serializer.save()
 | 
					        serializer.save()
 | 
				
			||||||
        return Response(serializer.data)
 | 
					        return Response(serializer.data)
 | 
				
			||||||
| 
						 | 
					@ -95,7 +95,7 @@ class AllowPUTAsCreateMixin(object):
 | 
				
			||||||
    def update(self, request, *args, **kwargs):
 | 
					    def update(self, request, *args, **kwargs):
 | 
				
			||||||
        partial = kwargs.pop('partial', False)
 | 
					        partial = kwargs.pop('partial', False)
 | 
				
			||||||
        instance = self.get_object_or_none()
 | 
					        instance = self.get_object_or_none()
 | 
				
			||||||
        serializer = self.get_serializer(instance, data=request.DATA, partial=partial)
 | 
					        serializer = self.get_serializer(instance, data=request.data, partial=partial)
 | 
				
			||||||
        serializer.is_valid(raise_exception=True)
 | 
					        serializer.is_valid(raise_exception=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if instance is None:
 | 
					        if instance is None:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -38,7 +38,7 @@ class DefaultContentNegotiation(BaseContentNegotiation):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        # Allow URL style format override.  eg. "?format=json
 | 
					        # Allow URL style format override.  eg. "?format=json
 | 
				
			||||||
        format_query_param = self.settings.URL_FORMAT_OVERRIDE
 | 
					        format_query_param = self.settings.URL_FORMAT_OVERRIDE
 | 
				
			||||||
        format = format_suffix or request.QUERY_PARAMS.get(format_query_param)
 | 
					        format = format_suffix or request.query_params.get(format_query_param)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if format:
 | 
					        if format:
 | 
				
			||||||
            renderers = self.filter_renderers(renderers, format)
 | 
					            renderers = self.filter_renderers(renderers, format)
 | 
				
			||||||
| 
						 | 
					@ -87,5 +87,5 @@ class DefaultContentNegotiation(BaseContentNegotiation):
 | 
				
			||||||
        Allows URL style accept override.  eg. "?accept=application/json"
 | 
					        Allows URL style accept override.  eg. "?accept=application/json"
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        header = request.META.get('HTTP_ACCEPT', '*/*')
 | 
					        header = request.META.get('HTTP_ACCEPT', '*/*')
 | 
				
			||||||
        header = request.QUERY_PARAMS.get(self.settings.URL_ACCEPT_OVERRIDE, header)
 | 
					        header = request.query_params.get(self.settings.URL_ACCEPT_OVERRIDE, header)
 | 
				
			||||||
        return [token.strip() for token in header.split(',')]
 | 
					        return [token.strip() for token in header.split(',')]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -120,7 +120,7 @@ class JSONPRenderer(JSONRenderer):
 | 
				
			||||||
        Determine the name of the callback to wrap around the json output.
 | 
					        Determine the name of the callback to wrap around the json output.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        request = renderer_context.get('request', None)
 | 
					        request = renderer_context.get('request', None)
 | 
				
			||||||
        params = request and request.QUERY_PARAMS or {}
 | 
					        params = request and request.query_params or {}
 | 
				
			||||||
        return params.get(self.callback_parameter, self.default_callback)
 | 
					        return params.get(self.callback_parameter, self.default_callback)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def render(self, data, accepted_media_type=None, renderer_context=None):
 | 
					    def render(self, data, accepted_media_type=None, renderer_context=None):
 | 
				
			||||||
| 
						 | 
					@ -426,7 +426,7 @@ class BrowsableAPIRenderer(BaseRenderer):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        if request.method == method:
 | 
					        if request.method == method:
 | 
				
			||||||
            try:
 | 
					            try:
 | 
				
			||||||
                data = request.DATA
 | 
					                data = request.data
 | 
				
			||||||
                # files = request.FILES
 | 
					                # files = request.FILES
 | 
				
			||||||
            except ParseError:
 | 
					            except ParseError:
 | 
				
			||||||
                data = None
 | 
					                data = None
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,7 +4,7 @@ The Request class is used as a wrapper around the standard request object.
 | 
				
			||||||
The wrapped request then offers a richer API, in particular :
 | 
					The wrapped request then offers a richer API, in particular :
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    - content automatically parsed according to `Content-Type` header,
 | 
					    - content automatically parsed according to `Content-Type` header,
 | 
				
			||||||
      and available as `request.DATA`
 | 
					      and available as `request.data`
 | 
				
			||||||
    - full support of PUT method, including support for file uploads
 | 
					    - full support of PUT method, including support for file uploads
 | 
				
			||||||
    - form overloading of HTTP method, content type and content
 | 
					    - form overloading of HTTP method, content type and content
 | 
				
			||||||
"""
 | 
					"""
 | 
				
			||||||
| 
						 | 
					@ -13,6 +13,7 @@ from django.conf import settings
 | 
				
			||||||
from django.http import QueryDict
 | 
					from django.http import QueryDict
 | 
				
			||||||
from django.http.multipartparser import parse_header
 | 
					from django.http.multipartparser import parse_header
 | 
				
			||||||
from django.utils.datastructures import MultiValueDict
 | 
					from django.utils.datastructures import MultiValueDict
 | 
				
			||||||
 | 
					from django.utils.datastructures import MergeDict as DjangoMergeDict
 | 
				
			||||||
from rest_framework import HTTP_HEADER_ENCODING
 | 
					from rest_framework import HTTP_HEADER_ENCODING
 | 
				
			||||||
from rest_framework import exceptions
 | 
					from rest_framework import exceptions
 | 
				
			||||||
from rest_framework.compat import BytesIO
 | 
					from rest_framework.compat import BytesIO
 | 
				
			||||||
| 
						 | 
					@ -58,6 +59,15 @@ class override_method(object):
 | 
				
			||||||
            self.view.action = self.action
 | 
					            self.view.action = self.action
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MergeDict(DjangoMergeDict, dict):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Using this as a workaround until the parsers API is properly
 | 
				
			||||||
 | 
					    addressed in 3.1.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    def __init__(self, *dicts):
 | 
				
			||||||
 | 
					        self.dicts = dicts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Empty(object):
 | 
					class Empty(object):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    Placeholder for unset attributes.
 | 
					    Placeholder for unset attributes.
 | 
				
			||||||
| 
						 | 
					@ -82,6 +92,7 @@ def clone_request(request, method):
 | 
				
			||||||
                  parser_context=request.parser_context)
 | 
					                  parser_context=request.parser_context)
 | 
				
			||||||
    ret._data = request._data
 | 
					    ret._data = request._data
 | 
				
			||||||
    ret._files = request._files
 | 
					    ret._files = request._files
 | 
				
			||||||
 | 
					    ret._full_data = request._full_data
 | 
				
			||||||
    ret._content_type = request._content_type
 | 
					    ret._content_type = request._content_type
 | 
				
			||||||
    ret._stream = request._stream
 | 
					    ret._stream = request._stream
 | 
				
			||||||
    ret._method = method
 | 
					    ret._method = method
 | 
				
			||||||
| 
						 | 
					@ -133,6 +144,7 @@ class Request(object):
 | 
				
			||||||
        self.parser_context = parser_context
 | 
					        self.parser_context = parser_context
 | 
				
			||||||
        self._data = Empty
 | 
					        self._data = Empty
 | 
				
			||||||
        self._files = Empty
 | 
					        self._files = Empty
 | 
				
			||||||
 | 
					        self._full_data = Empty
 | 
				
			||||||
        self._method = Empty
 | 
					        self._method = Empty
 | 
				
			||||||
        self._content_type = Empty
 | 
					        self._content_type = Empty
 | 
				
			||||||
        self._stream = Empty
 | 
					        self._stream = Empty
 | 
				
			||||||
| 
						 | 
					@ -186,12 +198,25 @@ class Request(object):
 | 
				
			||||||
        return self._stream
 | 
					        return self._stream
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def QUERY_PARAMS(self):
 | 
					    def query_params(self):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        More semantically correct name for request.GET.
 | 
					        More semantically correct name for request.GET.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        return self._request.GET
 | 
					        return self._request.GET
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def QUERY_PARAMS(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Synonym for `.query_params`, for backwards compatibility.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        return self._request.GET
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def data(self):
 | 
				
			||||||
 | 
					        if not _hasattr(self, '_full_data'):
 | 
				
			||||||
 | 
					            self._load_data_and_files()
 | 
				
			||||||
 | 
					        return self._full_data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def DATA(self):
 | 
					    def DATA(self):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
| 
						 | 
					@ -272,6 +297,10 @@ class Request(object):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if not _hasattr(self, '_data'):
 | 
					        if not _hasattr(self, '_data'):
 | 
				
			||||||
            self._data, self._files = self._parse()
 | 
					            self._data, self._files = self._parse()
 | 
				
			||||||
 | 
					            if self._files:
 | 
				
			||||||
 | 
					                self._full_data = MergeDict(self._data, self._files)
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                self._full_data = self._data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _load_method_and_content_type(self):
 | 
					    def _load_method_and_content_type(self):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
| 
						 | 
					@ -333,6 +362,7 @@ class Request(object):
 | 
				
			||||||
        # At this point we're committed to parsing the request as form data.
 | 
					        # At this point we're committed to parsing the request as form data.
 | 
				
			||||||
        self._data = self._request.POST
 | 
					        self._data = self._request.POST
 | 
				
			||||||
        self._files = self._request.FILES
 | 
					        self._files = self._request.FILES
 | 
				
			||||||
 | 
					        self._full_data = MergeDict(self._data, self._files)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Method overloading - change the method and remove the param from the content.
 | 
					        # Method overloading - change the method and remove the param from the content.
 | 
				
			||||||
        if (
 | 
					        if (
 | 
				
			||||||
| 
						 | 
					@ -350,7 +380,7 @@ class Request(object):
 | 
				
			||||||
        ):
 | 
					        ):
 | 
				
			||||||
            self._content_type = self._data[self._CONTENTTYPE_PARAM]
 | 
					            self._content_type = self._data[self._CONTENTTYPE_PARAM]
 | 
				
			||||||
            self._stream = BytesIO(self._data[self._CONTENT_PARAM].encode(self.parser_context['encoding']))
 | 
					            self._stream = BytesIO(self._data[self._CONTENT_PARAM].encode(self.parser_context['encoding']))
 | 
				
			||||||
            self._data, self._files = (Empty, Empty)
 | 
					            self._data, self._files, self._full_data = (Empty, Empty, Empty)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _parse(self):
 | 
					    def _parse(self):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
| 
						 | 
					@ -380,6 +410,7 @@ class Request(object):
 | 
				
			||||||
            # logging the request or similar.
 | 
					            # logging the request or similar.
 | 
				
			||||||
            self._data = QueryDict('', encoding=self._request._encoding)
 | 
					            self._data = QueryDict('', encoding=self._request._encoding)
 | 
				
			||||||
            self._files = MultiValueDict()
 | 
					            self._files = MultiValueDict()
 | 
				
			||||||
 | 
					            self._full_data = self._data
 | 
				
			||||||
            raise
 | 
					            raise
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Parser classes may return the raw data, or a
 | 
					        # Parser classes may return the raw data, or a
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -57,21 +57,24 @@ class BaseSerializer(Field):
 | 
				
			||||||
    def to_representation(self, instance):
 | 
					    def to_representation(self, instance):
 | 
				
			||||||
        raise NotImplementedError('`to_representation()` must be implemented.')
 | 
					        raise NotImplementedError('`to_representation()` must be implemented.')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def update(self, instance, attrs):
 | 
					    def update(self, instance, validated_data):
 | 
				
			||||||
        raise NotImplementedError('`update()` must be implemented.')
 | 
					        raise NotImplementedError('`update()` must be implemented.')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def create(self, attrs):
 | 
					    def create(self, validated_data):
 | 
				
			||||||
        raise NotImplementedError('`create()` must be implemented.')
 | 
					        raise NotImplementedError('`create()` must be implemented.')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def save(self, extras=None):
 | 
					    def save(self, extras=None):
 | 
				
			||||||
        attrs = self.validated_data
 | 
					        validated_data = self.validated_data
 | 
				
			||||||
        if extras is not None:
 | 
					        if extras is not None:
 | 
				
			||||||
            attrs = dict(list(attrs.items()) + list(extras.items()))
 | 
					            validated_data = dict(
 | 
				
			||||||
 | 
					                list(validated_data.items()) +
 | 
				
			||||||
 | 
					                list(extras.items())
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if self.instance is not None:
 | 
					        if self.instance is not None:
 | 
				
			||||||
            self.update(self.instance, attrs)
 | 
					            self.update(self.instance, validated_data)
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            self.instance = self.create(attrs)
 | 
					            self.instance = self.create(validated_data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return self.instance
 | 
					        return self.instance
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -321,12 +324,6 @@ class ListSerializer(BaseSerializer):
 | 
				
			||||||
    def create(self, attrs_list):
 | 
					    def create(self, attrs_list):
 | 
				
			||||||
        return [self.child.create(attrs) for attrs in attrs_list]
 | 
					        return [self.child.create(attrs) for attrs in attrs_list]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def save(self):
 | 
					 | 
				
			||||||
        if self.instance is not None:
 | 
					 | 
				
			||||||
            self.update(self.instance, self.validated_data)
 | 
					 | 
				
			||||||
        self.instance = self.create(self.validated_data)
 | 
					 | 
				
			||||||
        return self.instance
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __repr__(self):
 | 
					    def __repr__(self):
 | 
				
			||||||
        return representation.list_repr(self, indent=1)
 | 
					        return representation.list_repr(self, indent=1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,7 +9,10 @@ import pytest
 | 
				
			||||||
# Tests for field keyword arguments and core functionality.
 | 
					# Tests for field keyword arguments and core functionality.
 | 
				
			||||||
# ---------------------------------------------------------
 | 
					# ---------------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TestFieldOptions:
 | 
					class TestEmpty:
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Tests for `required`, `allow_null`, `allow_blank`, `default`.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
    def test_required(self):
 | 
					    def test_required(self):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        By default a field must be included in the input.
 | 
					        By default a field must be included in the input.
 | 
				
			||||||
| 
						 | 
					@ -69,6 +72,17 @@ class TestFieldOptions:
 | 
				
			||||||
        output = field.run_validation()
 | 
					        output = field.run_validation()
 | 
				
			||||||
        assert output is 123
 | 
					        assert output is 123
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestSource:
 | 
				
			||||||
 | 
					    def test_source(self):
 | 
				
			||||||
 | 
					        class ExampleSerializer(serializers.Serializer):
 | 
				
			||||||
 | 
					            example_field = serializers.CharField(source='other')
 | 
				
			||||||
 | 
					        serializer = ExampleSerializer(data={'example_field': 'abc'})
 | 
				
			||||||
 | 
					        print serializer.is_valid()
 | 
				
			||||||
 | 
					        print serializer.data
 | 
				
			||||||
 | 
					        assert serializer.is_valid()
 | 
				
			||||||
 | 
					        assert serializer.validated_data == {'other': 'abc'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_redundant_source(self):
 | 
					    def test_redundant_source(self):
 | 
				
			||||||
        class ExampleSerializer(serializers.Serializer):
 | 
					        class ExampleSerializer(serializers.Serializer):
 | 
				
			||||||
            example_field = serializers.CharField(source='example_field')
 | 
					            example_field = serializers.CharField(source='example_field')
 | 
				
			||||||
| 
						 | 
					@ -81,6 +95,128 @@ class TestFieldOptions:
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestReadOnly:
 | 
				
			||||||
 | 
					    def setup(self):
 | 
				
			||||||
 | 
					        class TestSerializer(serializers.Serializer):
 | 
				
			||||||
 | 
					            read_only = fields.ReadOnlyField()
 | 
				
			||||||
 | 
					            writable = fields.IntegerField()
 | 
				
			||||||
 | 
					        self.Serializer = TestSerializer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_validate_read_only(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Read-only fields should not be included in validation.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        data = {'read_only': 123, 'writable': 456}
 | 
				
			||||||
 | 
					        serializer = self.Serializer(data=data)
 | 
				
			||||||
 | 
					        assert serializer.is_valid()
 | 
				
			||||||
 | 
					        assert serializer.validated_data == {'writable': 456}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_serialize_read_only(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Read-only fields should be serialized.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        instance = {'read_only': 123, 'writable': 456}
 | 
				
			||||||
 | 
					        serializer = self.Serializer(instance)
 | 
				
			||||||
 | 
					        assert serializer.data == {'read_only': 123, 'writable': 456}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestWriteOnly:
 | 
				
			||||||
 | 
					    def setup(self):
 | 
				
			||||||
 | 
					        class TestSerializer(serializers.Serializer):
 | 
				
			||||||
 | 
					            write_only = fields.IntegerField(write_only=True)
 | 
				
			||||||
 | 
					            readable = fields.IntegerField()
 | 
				
			||||||
 | 
					        self.Serializer = TestSerializer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_validate_write_only(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Write-only fields should be included in validation.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        data = {'write_only': 123, 'readable': 456}
 | 
				
			||||||
 | 
					        serializer = self.Serializer(data=data)
 | 
				
			||||||
 | 
					        assert serializer.is_valid()
 | 
				
			||||||
 | 
					        assert serializer.validated_data == {'write_only': 123, 'readable': 456}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_serialize_write_only(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Write-only fields should not be serialized.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        instance = {'write_only': 123, 'readable': 456}
 | 
				
			||||||
 | 
					        serializer = self.Serializer(instance)
 | 
				
			||||||
 | 
					        assert serializer.data == {'readable': 456}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestInitial:
 | 
				
			||||||
 | 
					    def setup(self):
 | 
				
			||||||
 | 
					        class TestSerializer(serializers.Serializer):
 | 
				
			||||||
 | 
					            initial_field = fields.IntegerField(initial=123)
 | 
				
			||||||
 | 
					            blank_field = fields.IntegerField()
 | 
				
			||||||
 | 
					        self.serializer = TestSerializer()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_initial(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Initial values should be included when serializing a new representation.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        assert self.serializer.data == {
 | 
				
			||||||
 | 
					            'initial_field': 123,
 | 
				
			||||||
 | 
					            'blank_field': None
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestLabel:
 | 
				
			||||||
 | 
					    def setup(self):
 | 
				
			||||||
 | 
					        class TestSerializer(serializers.Serializer):
 | 
				
			||||||
 | 
					            labeled = fields.IntegerField(label='My label')
 | 
				
			||||||
 | 
					        self.serializer = TestSerializer()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_label(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        A field's label may be set with the `label` argument.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        fields = self.serializer.fields
 | 
				
			||||||
 | 
					        assert fields['labeled'].label == 'My label'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestInvalidErrorKey:
 | 
				
			||||||
 | 
					    def setup(self):
 | 
				
			||||||
 | 
					        class ExampleField(serializers.Field):
 | 
				
			||||||
 | 
					            def to_native(self, data):
 | 
				
			||||||
 | 
					                self.fail('incorrect')
 | 
				
			||||||
 | 
					        self.field = ExampleField()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_invalid_error_key(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        If a field raises a validation error, but does not have a corresponding
 | 
				
			||||||
 | 
					        error message, then raise an appropriate assertion error.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        with pytest.raises(AssertionError) as exc_info:
 | 
				
			||||||
 | 
					            self.field.to_native(123)
 | 
				
			||||||
 | 
					        expected = (
 | 
				
			||||||
 | 
					            'ValidationError raised by `ExampleField`, but error key '
 | 
				
			||||||
 | 
					            '`incorrect` does not exist in the `error_messages` dictionary.'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        assert str(exc_info.value) == expected
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestBooleanHTMLInput:
 | 
				
			||||||
 | 
					    def setup(self):
 | 
				
			||||||
 | 
					        class TestSerializer(serializers.Serializer):
 | 
				
			||||||
 | 
					            archived = fields.BooleanField()
 | 
				
			||||||
 | 
					        self.Serializer = TestSerializer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_empty_html_checkbox(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        HTML checkboxes do not send any value, but should be treated
 | 
				
			||||||
 | 
					        as `False` by BooleanField.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        # This class mocks up a dictionary like object, that behaves
 | 
				
			||||||
 | 
					        # as if it was returned for multipart or urlencoded data.
 | 
				
			||||||
 | 
					        class MockHTMLDict(dict):
 | 
				
			||||||
 | 
					            getlist = None
 | 
				
			||||||
 | 
					        serializer = self.Serializer(data=MockHTMLDict())
 | 
				
			||||||
 | 
					        assert serializer.is_valid()
 | 
				
			||||||
 | 
					        assert serializer.validated_data == {'archived': False}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Tests for field input and output values.
 | 
					# Tests for field input and output values.
 | 
				
			||||||
# ----------------------------------------
 | 
					# ----------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -495,7 +631,7 @@ class TestDateTimeField(FieldValues):
 | 
				
			||||||
        '2001-01-01T13:00Z': datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC()),
 | 
					        '2001-01-01T13:00Z': datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC()),
 | 
				
			||||||
        datetime.datetime(2001, 1, 1, 13, 00): datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC()),
 | 
					        datetime.datetime(2001, 1, 1, 13, 00): datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC()),
 | 
				
			||||||
        datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC()): datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC()),
 | 
					        datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC()): datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC()),
 | 
				
			||||||
        # Note that 1.4 does not support timezone string parsing.
 | 
					        # Django 1.4 does not support timezone string parsing.
 | 
				
			||||||
        '2001-01-01T14:00+01:00' if (django.VERSION > (1, 4)) else '2001-01-01T13:00Z': datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC())
 | 
					        '2001-01-01T14:00+01:00' if (django.VERSION > (1, 4)) else '2001-01-01T13:00Z': datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    invalid_inputs = {
 | 
					    invalid_inputs = {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -38,7 +38,7 @@ class FKInstanceView(generics.RetrieveUpdateDestroyAPIView):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class SlugSerializer(serializers.ModelSerializer):
 | 
					class SlugSerializer(serializers.ModelSerializer):
 | 
				
			||||||
    slug = serializers.Field(read_only=True)
 | 
					    slug = serializers.ReadOnlyField()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    class Meta:
 | 
					    class Meta:
 | 
				
			||||||
        model = SlugBasedModel
 | 
					        model = SlugBasedModel
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,5 @@
 | 
				
			||||||
from rest_framework import serializers
 | 
					from rest_framework import serializers
 | 
				
			||||||
 | 
					import pytest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Tests for core functionality.
 | 
					# Tests for core functionality.
 | 
				
			||||||
| 
						 | 
					@ -29,6 +30,67 @@ class TestSerializer:
 | 
				
			||||||
        assert serializer.validated_data == {'char': 'abc'}
 | 
					        assert serializer.validated_data == {'char': 'abc'}
 | 
				
			||||||
        assert serializer.errors == {}
 | 
					        assert serializer.errors == {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_empty_serializer(self):
 | 
				
			||||||
 | 
					        serializer = self.Serializer()
 | 
				
			||||||
 | 
					        assert serializer.data == {'char': '', 'integer': None}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_missing_attribute_during_serialization(self):
 | 
				
			||||||
 | 
					        class MissingAttributes:
 | 
				
			||||||
 | 
					            pass
 | 
				
			||||||
 | 
					        instance = MissingAttributes()
 | 
				
			||||||
 | 
					        serializer = self.Serializer(instance)
 | 
				
			||||||
 | 
					        with pytest.raises(AttributeError):
 | 
				
			||||||
 | 
					            serializer.data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestStarredSource:
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Tests for `source='*'` argument, which is used for nested representations.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    For example:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        nested_field = NestedField(source='*')
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    data = {
 | 
				
			||||||
 | 
					        'nested1': {'a': 1, 'b': 2},
 | 
				
			||||||
 | 
					        'nested2': {'c': 3, 'd': 4}
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def setup(self):
 | 
				
			||||||
 | 
					        class NestedSerializer1(serializers.Serializer):
 | 
				
			||||||
 | 
					            a = serializers.IntegerField()
 | 
				
			||||||
 | 
					            b = serializers.IntegerField()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        class NestedSerializer2(serializers.Serializer):
 | 
				
			||||||
 | 
					            c = serializers.IntegerField()
 | 
				
			||||||
 | 
					            d = serializers.IntegerField()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        class TestSerializer(serializers.Serializer):
 | 
				
			||||||
 | 
					            nested1 = NestedSerializer1(source='*')
 | 
				
			||||||
 | 
					            nested2 = NestedSerializer2(source='*')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.Serializer = TestSerializer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_nested_validate(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        A nested representation is validated into a flat internal object.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        serializer = self.Serializer(data=self.data)
 | 
				
			||||||
 | 
					        assert serializer.is_valid()
 | 
				
			||||||
 | 
					        assert serializer.validated_data == {
 | 
				
			||||||
 | 
					            'a': 1,
 | 
				
			||||||
 | 
					            'b': 2,
 | 
				
			||||||
 | 
					            'c': 3,
 | 
				
			||||||
 | 
					            'd': 4
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_nested_serialize(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        An object can be serialized into a nested representation.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        instance = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
 | 
				
			||||||
 | 
					        serializer = self.Serializer(instance)
 | 
				
			||||||
 | 
					        assert serializer.data == self.data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# # -*- coding: utf-8 -*-
 | 
					# # -*- coding: utf-8 -*-
 | 
				
			||||||
# from __future__ import unicode_literals
 | 
					# from __future__ import unicode_literals
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user