Django update without required fields - django

I'm trying to execute an update through my API. When I push an update, I do not have all the required fields, because I'm only trying to update the ones that have changed. I get a 400 "This field is required." error.
I know the field is required, but I'm trying to just update, not put in all my required fields again. This is a PUT request. This occurs before create or update are called on my serializers. It fails on the is_valid() call (which I do not override). Honestly, there isn't any relevant code to show. To fix this do I have to override is_valid and provide a password there?
For example: Password is a required field in my model. However I only push "first_name" because that's the only field that changed. I will get: password":["This field is required."].

UPDATE is possible via 2 requests: PUT and PATCH
PUT updates all the fields of the object on which the operation is to be performed. It basically iterates over all the fields and updates them one by one. Thus, if a required field is not found in the supplied data, it will raise an error.
PATCH is what we call a Partial Update. You can update only the fields that are needed to be changed. Thus, in your case change your request method to PATCH and your work will be done.

I was facing the same error after many experiments found something, so added all fields in serializer.py in class meta, as shown below -
class Emp_UniSerializer( serializers.ModelSerializer ):
class Meta:
model = table
fields = '__all__' # To fetch For All Fields
extra_kwargs = {'std_code': {'required': False},'uni_code': {'required': False},'last_name': {'required': False},'first_name': {'required': False}}
Here, we can update any field, it won't show error ["This field is required."]

I'm not sure why it is sometimes that you look for hours for the answer, then ask, then immediately find the answer. I simply changed my request to be a 'PATCH' instead of a 'PUT'.

Add partial = True argument in serializer as such:
serializer = BookSerializer(book, data=request.data, partial=True)

Related

Django Rest Framework, ModelSerializers and custom fields validation

Using latest version of Django and DRF.
I have a rather complex requirement I can't find a solution for. I'll try to simplify it.
Let's say I have a model that has two fields. field_a and field_b
I have a ModelSerializer for it. I POST a request with its fields. The fields get validated with the model and then against my two serializer functions validate_field_a and validate_field_b. All is well.
Now I'd like my POST request to include a third field that is not a member of that model. let's call it field_c. I have a custom def create(self, validated_data): in my serializer which saves everything to the database.
with regards to field_c I would like to:
Custom Validate it. just like I do with the other two fields.
Require that it is mandatory for the whole request to succeed and if it's not, issue a "Field is required" error just like if I forgot to POST one of my required model fields.
Have the chance to take field_c and save it onto a totally different unrelated Model's row in the db.
I can't seem to get around that. If I add field_c to the fields meta - it throws an exception saying justifiably that field_c is not in my model. If I don't include it in fields, the validate_field_c which I really want to put there doesn't even get called.
What can I do?
You can add the custom field in your serializer as a write_only field and override the create method so that you can handle the custom field's value.
Something like this:
class MySerializer(serializers.ModelSerializer):
field_c = serializers.CharField(write_only=True)
class Meta:
model = MyModel
fields = ('field_a', 'field_b', 'field_c')
def validate_field_c(self, value):
if value is 'test':
raise ValidationError('Invalid')
return value
def create(self, validated_data, **kwargs):
field_c = validated_data.pop('field_c')
return MyModel.objects.create(**validated_data)
Don't use ModelSerializer for this - use a serializer that recreates the same fields as your model & include field_c as you would.
I understand that you want your model to do some of the work in the validation process but the design of DRF is such that it isolates these responsibilities. You can read more about it here. Basically, the serializer should be the one doing all the validation heavy-lifting.
Of course, this means that you'll have to explicitly define the validation methods in the serializer.
In your custom create() method you can create the model instance or do whatever you want in it as required.

Django Rest Framework JSON serializer field failing validation with required=False and no default?

How can I set required to be false for a django rest framework JSON serializer field? It seems to be enforcing validation regardless of the required flag:
serializer field
results = serializers.JSONField(required=False, label='Result')
model field
results = models.TextField(blank=True, default="")
But when I submit the form with a blank input, I get:
"results": [
"Value must be valid JSON."
],
I've also tried changing the model default to {} in both the model field and the serializer field, but have the same response.
UPDATE
Thanks to #Linovia for pointing out that "The required flag does mean that the serializer will not complain if that field isn't present"
After some digging, it looks like DRF is setting a default value of null on the input, which then is caught as invalid... How can I override this, as the serializer "default" attribute doesn't seem to have any effect.
"results": null,
The required flag does mean that the serializer will not complain if that field isn't present.
However, if it is present, it will follow the validation process. It doesn't mean at all that it will be discarded if it doesn't validate.
This appears to be an issue with DRF's empty class (used "to represent no data being provided for a given input or output value). The empty value for a JSONField is not json serializable, so you see
"results": [
"Value must be valid JSON."
],
To fix this, I overrode DRF's JSONField with my own NullableJSONField
from rest_framework.fields import empty
class NullableJSONField(serializers.JSONField):
def get_value(self, dictionary):
result = super().get_value(dictionary)
if result is empty:
return None
return result
and added allow_null=True to the field in the serializer
json_blob = NullableJSONField(required=False, allow_null=True)
The issue with this fix is that the json_blob field is then serialized with None in the response. (e.g. "json_blob": {})
I hope this resolves your issue, and I will post an update if I find a solution that complete removes this field from the response when it is not input.
For future reference, one way to successfully and quickly implement this is using the initial keyword on the serializer, which will save an empty dict to the model instance (ensure you have coupled this with a django JSONField in the model definition itself)
serializer:
results = serializers.JSONField(required=False, initial=dict)
model:
results = JSONField(default=dict)
The browsable api will render with {} as the initial value, which you may or may not choose to modify.
you can use syntax something like...
class DetailSerializer(serializers.ModelSerializer):
results = ResultSerializer(
allow_empty_file=True,required=False
)

Modify data before validation step with django rest framework

I have a simple Model that stores the user that created it with a ForeignKey. The model has a corresponding ModelSerializer and ModelViewSet.
The problem is that when the user submits a POST to create a new record, the user should be set by the backend. I tried overriding perform_create on the ModelViewSet to set the user, but it actually still fails during the validation step (which makes sense). It comes back saying the user field is required.
I'm thinking about overriding the user field on the ModelSerializer to be optional, but I feel like there's probably a cleaner and more efficient way to do this. Any ideas?
I came across this answer while looking for a way to update values before the control goes to the validator.
This might be useful for someone else - here's how I finally did it (DRF 3) without rewriting the whole validator.
class MyModelSerializer(serializers.ModelSerializer):
def to_internal_value(self, data):
data['user'] = '<Set Value Here>'
return super(MyModelSerializer, self).to_internal_value(data)
For those who're curious, I used this to round decimal values to precision defined in the model so that the validator doesn't throw errors.
You can make the user field as read_only.
This will ensure that the field is used when serializing a representation, but is not used when creating or updating an instance during deserialization.
In your serializers, you can do something like:
class MyModelSerializer(serializers.ModelSerializer):
class Meta:
model = MyModel
extra_kwargs = {
'user' : {'read_only' : True} # define the 'user' field as 'read-only'
}
You can then override the perform_create() and set the user as per your requirements.
Old topic but it could be useful for someone.
If you want to alter your data before validation of serializer:
serializer.initial_data["your_key"] = "your data"
serializer.is_valid(raise_exception=True)

How to partially update properly?

I'm trying to partially update an instance with partial=True, but no matter which of the attribute is missing I'd get an error saying This field cannot be blank.. I thought partial=True enables partial updating. Am I missing something here?
class MySerializer(serializers.ModelSerializer):
class Meta:
model = MyModel
fields = ('meta_name', 'meta_num_files', 'meta_total_length')
...
class MyViewClass(APIView):
def post(self, request):
instance = get_object_or_404(MyModel, foo='foo')
...
serializer = MySerializer(instance, data, partial=True)
...
self.client.post(reverse('api_meta', data={'meta_name': '',
'meta_total_length': 1000000,
'meta_num_files': 10
}
)
There is a difference between partial updates, where you only update a subset of the fields on the serializer, and non-required fields, where the value can be blank.
When using partial updates, the client can send in only a subset of fields on the serializer, and the serializer will not require the ones that are not present. This has the side-effect of skipping some validation, but it expects that the fields not included in the request are already present on the model. Only the fields that are passed in with the request are validated, and only those fields are updated on the model.
You are passing in a blank value instead of not passing in the key at all. This is equivalent to doing an update that tries to clear the value of the field, which is perfectly normal in partial update situations, so Django REST Framework will run validation on the blank field. This does not appear to be documented anywhere, but that is how Django REST Framework determines what fields to run the validation on.
Because your field does not have empty=True specified, Django REST Framework will realize that it is a required field and cannot be empty. That is why you are getting an error that says This field cannot be blank, you are telling it to clear out the value of the field.

With django-rest-framework how do I assign a field to the request user when using a ModelViewSet?

The documentation recommends for APIViews:
def pre_save(self, obj):
obj.owner = self.request.user
This doesn't seem to work as I get back:
{
"owner": [
"This field cannot be null."
]
}
edit: The pre_save method never gets called. Checked by inserting a call to pdb in the pre_save method.
My serializer is a plain HyperlinkedModelSerializer with the model and list of fields specified in Meta.
On my model I had overridden clean_fields. The signature for clean_fields:
Model.clean_fields(exclude=None)
My custom clean_fields:
def clean_fields(self, exclude=None):
super(Applicant, self).clean_fields() # Bug here.
# custom clean_fields code
I didn't pass the exclude parameter to the super class!
Oh god this took me so many hours to debug.
Check DRF 3.0 release notes docs. They have an example doing exactly what you need with perform_create() in ViewSet .
Include the "owner" field in the serializer Meta.exclude (or just remove it from the field list if you are using the "positive" list).
class FooSerializer(serializers.Serializer):
class Meta(object):
model: Foo
exclude: ('owner',)
This way you will not trigger validation for this field. Also, make sure the endpoint is decorated with mandatory authentication or check if self.request.user.is_authenticated() is True.
The problem seems to be that the validation fails because the "owner" field is required but not given a value in the initial data(it gets a value after validation). Because serializer.is_valid() is run before pre_save() then it fails.
Why the version change fixes it I have no idea. I myself have set owner as required=False although an object always will get one from pre_save()