Repository: encode/django-rest-framework

Issue:
New handling of default= for ModelSerializer
Then changes in #9030 lead to a issue in my implemenation:
With 3.14 and its ModelSerializer we could determine if a user was passing a field to the API or not. (Basically we allow setting the DB field to everything EXCEPT the default via the REST API.)

Example:
```
class Message(Model):
  content_type = models.TextField(null=False, blank=False, default="undefined")

class MessageSerializer(ModelSerializer):
  class Meta:
     model = Message
```

We could de-serialize the JSON `{}` and the validated data didn't contain the default value for content_type. We still can create the model instance from it because there is a default in the database. BUT we disallow actively setting the content_type to `undefined` by checking if `content_type` is in the validated data and refusing a JSON with `{"content_type": "undefined"}`.

With 3.15 we can't determine this anymore, yes? Or is there something in the validated data to tell us where the data came from (the JSON the user posted or the models default)?



Discussion:
Okay, I RTFM and I would use `.initial_data` to see if the `content_type` is user provided or not, correct approach?
There's another side-effect of this change that leads to a huge breaking change for non-partial updates (PUT) of fields with default values. Previously if you didn't include a field with a default value when doing a PUT request, that field wouldn't get updated. It would just retain its current value, similarly to if you had done a partial update (PATCH) (the reason you're allowed to not pass it in the PUT is that the default leads to it not being required)

But now that field gets updated to the default value instead! This seems very dangerous since it could lead to data loss for anyone who's previously not been passing the current value along for all fields with defaults in PUT requests.
> There's another side-effect of this change that leads to a huge breaking change for non-partial updates (PUT) of fields with default values. Previously if you didn't include a field with a default value when doing a PUT request, that field wouldn't get updated. It would just retain its current value, similarly to if you had done a partial update (PATCH) (the reason you're allowed to not pass it in the PUT is that the default leads to it not being required)
> 
> But now that field gets updated to the default value instead! This seems very dangerous since it could lead to data loss for anyone who's previously not been passing the current value along for all fields with defaults in PUT requests.

Good point.

Resolving patch:
diff --git a/rest_framework/metadata.py b/rest_framework/metadata.py
--- a/rest_framework/metadata.py
+++ b/rest_framework/metadata.py
@@ -11,7 +11,6 @@
 from django.utils.encoding import force_str
 
 from rest_framework import exceptions, serializers
-from rest_framework.fields import empty
 from rest_framework.request import clone_request
 from rest_framework.utils.field_mapping import ClassLookupDict
 
@@ -150,7 +149,4 @@ def get_field_info(self, field):
                 for choice_value, choice_name in field.choices.items()
             ]
 
-        if getattr(field, 'default', None) and field.default != empty and not callable(field.default):
-            field_info['default'] = field.default
-
         return field_info
diff --git a/rest_framework/utils/field_mapping.py b/rest_framework/utils/field_mapping.py
--- a/rest_framework/utils/field_mapping.py
+++ b/rest_framework/utils/field_mapping.py
@@ -9,7 +9,6 @@
 from django.utils.text import capfirst
 
 from rest_framework.compat import postgres_fields
-from rest_framework.fields import empty
 from rest_framework.validators import UniqueValidator
 
 NUMERIC_FIELD_TYPES = (
@@ -128,9 +127,6 @@ def get_field_kwargs(field_name, model_field):
         kwargs['read_only'] = True
         return kwargs
 
-    if model_field.default is not None and model_field.default != empty and not callable(model_field.default):
-        kwargs['default'] = model_field.default
-
     if model_field.has_default() or model_field.blank or model_field.null:
         kwargs['required'] = False
 


Test patch:
diff --git a/tests/test_metadata.py b/tests/test_metadata.py
--- a/tests/test_metadata.py
+++ b/tests/test_metadata.py
@@ -184,135 +184,6 @@ def get_serializer(self):
         assert response.status_code == status.HTTP_200_OK
         assert response.data == expected
 
-    def test_actions_with_default(self):
-        """
-        On generic views OPTIONS should return an 'actions' key with metadata
-        on the fields with default that may be supplied to PUT and POST requests.
-        """
-        class NestedField(serializers.Serializer):
-            a = serializers.IntegerField(default=2)
-            b = serializers.IntegerField()
-
-        class ExampleSerializer(serializers.Serializer):
-            choice_field = serializers.ChoiceField(['red', 'green', 'blue'], default='red')
-            integer_field = serializers.IntegerField(
-                min_value=1, max_value=1000, default=1
-            )
-            char_field = serializers.CharField(
-                min_length=3, max_length=40, default="example"
-            )
-            list_field = serializers.ListField(
-                child=serializers.ListField(
-                    child=serializers.IntegerField(default=1)
-                )
-            )
-            nested_field = NestedField()
-            uuid_field = serializers.UUIDField(label="UUID field")
-
-        class ExampleView(views.APIView):
-            """Example view."""
-            def post(self, request):
-                pass
-
-            def get_serializer(self):
-                return ExampleSerializer()
-
-        view = ExampleView.as_view()
-        response = view(request=request)
-        expected = {
-            'name': 'Example',
-            'description': 'Example view.',
-            'renders': [
-                'application/json',
-                'text/html'
-            ],
-            'parses': [
-                'application/json',
-                'application/x-www-form-urlencoded',
-                'multipart/form-data'
-            ],
-            'actions': {
-                'POST': {
-                    'choice_field': {
-                        'type': 'choice',
-                        'required': False,
-                        'read_only': False,
-                        'label': 'Choice field',
-                        "choices": [
-                            {'value': 'red', 'display_name': 'red'},
-                            {'value': 'green', 'display_name': 'green'},
-                            {'value': 'blue', 'display_name': 'blue'}
-                        ],
-                        'default': 'red'
-                    },
-                    'integer_field': {
-                        'type': 'integer',
-                        'required': False,
-                        'read_only': False,
-                        'label': 'Integer field',
-                        'min_value': 1,
-                        'max_value': 1000,
-                        'default': 1
-                    },
-                    'char_field': {
-                        'type': 'string',
-                        'required': False,
-                        'read_only': False,
-                        'label': 'Char field',
-                        'min_length': 3,
-                        'max_length': 40,
-                        'default': 'example'
-                    },
-                    'list_field': {
-                        'type': 'list',
-                        'required': True,
-                        'read_only': False,
-                        'label': 'List field',
-                        'child': {
-                            'type': 'list',
-                            'required': True,
-                            'read_only': False,
-                            'child': {
-                                'type': 'integer',
-                                'required': False,
-                                'read_only': False,
-                                'default': 1
-                            }
-                        }
-                    },
-                    'nested_field': {
-                        'type': 'nested object',
-                        'required': True,
-                        'read_only': False,
-                        'label': 'Nested field',
-                        'children': {
-                            'a': {
-                                'type': 'integer',
-                                'required': False,
-                                'read_only': False,
-                                'label': 'A',
-                                'default': 2
-                            },
-                            'b': {
-                                'type': 'integer',
-                                'required': True,
-                                'read_only': False,
-                                'label': 'B'
-                            }
-                        }
-                    },
-                    'uuid_field': {
-                        'type': 'string',
-                        'required': True,
-                        'read_only': False,
-                        'label': 'UUID field'
-                    }
-                }
-            }
-        }
-        assert response.status_code == status.HTTP_200_OK
-        assert response.data == expected
-
     def test_global_permissions(self):
         """
         If a user does not have global permissions on an action, then any
diff --git a/tests/test_model_serializer.py b/tests/test_model_serializer.py
--- a/tests/test_model_serializer.py
+++ b/tests/test_model_serializer.py
@@ -174,7 +174,7 @@ class Meta:
             TestSerializer\(\):
                 auto_field = IntegerField\(read_only=True\)
                 big_integer_field = IntegerField\(.*\)
-                boolean_field = BooleanField\(default=False, required=False\)
+                boolean_field = BooleanField\(required=False\)
                 char_field = CharField\(max_length=100\)
                 comma_separated_integer_field = CharField\(max_length=100, validators=\[<django.core.validators.RegexValidator object>\]\)
                 date_field = DateField\(\)
@@ -183,7 +183,7 @@ class Meta:
                 email_field = EmailField\(max_length=100\)
                 float_field = FloatField\(\)
                 integer_field = IntegerField\(.*\)
-                null_boolean_field = BooleanField\(allow_null=True, default=False, required=False\)
+                null_boolean_field = BooleanField\(allow_null=True, required=False\)
                 positive_integer_field = IntegerField\(.*\)
                 positive_small_integer_field = IntegerField\(.*\)
                 slug_field = SlugField\(allow_unicode=False, max_length=100\)
@@ -210,7 +210,7 @@ class Meta:
                 length_limit_field = CharField\(max_length=12, min_length=3\)
                 blank_field = CharField\(allow_blank=True, max_length=10, required=False\)
                 null_field = IntegerField\(allow_null=True,.*required=False\)
-                default_field = IntegerField\(default=0,.*required=False\)
+                default_field = IntegerField\(.*required=False\)
                 descriptive_field = IntegerField\(help_text='Some help text', label='A label'.*\)
                 choices_field = ChoiceField\(choices=(?:\[|\()\('red', 'Red'\), \('blue', 'Blue'\), \('green', 'Green'\)(?:\]|\))\)
                 text_choices_field = ChoiceField\(choices=(?:\[|\()\('red', 'Red'\), \('blue', 'Blue'\), \('green', 'Green'\)(?:\]|\))\)