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.
Related
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.
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
)
Currently, if a field is required, this can be enforced via the blank = False argument, such as:
models.py
address1 = models.CharField(max_length=255,null=False,blank=False)
However, the validation is performed prior to the POST action, yielding something like this when trying to submit the form containing an empty field:
I would prefer the validation to be done during the post step, like this:
models.py
address1 = models.CharField(max_length=255,null=False,blank=true)
forms.py
class AddressForm(forms.ModelForm):
def __init__(self,*args,**kwargs):
super(AddressForm,self).__init__(*args,**kwargs)
self.fields['address1'].required = True
And this yields the following result when trying to submit the form containing an empty field:
But the problem with this, (as far as I can tell) is that I need to explicitly state the required attribute for each field on a case-by-case basis.
Is there any way that I can associate blank=False as being representative of the required=True attribute, suppressing the first form validation (above), in favour of the second?
ModelForm runs form validation, then model validation:
There are two main steps involved in validating a ModelForm:
Validating the form
Validating the model instance
So you have to manually add the extra form validation that you want before the inherited model validations.
However, default ModelForm field for blank field is already required:
If the model field has blank=True, then required is set to False on
the form field. Otherwise, required=True
You can change the error message. If you use this additional validations a lot, you can use a Mixin:
class BlankToRequiredMixin(object):
def set_required(self):
model = self._meta.model
for field_name,form_field in self.fields.iteritems():
if not model._meta.get_field(field_name).blank:
form_field.error_messages={'required': 'This field is required'} # to make it required in addtion to non-blank set .required=True
Then, to set required=True for all fields that are non-blank in the model:
class AddressForm(forms.ModelForm,BlankToRequiredMixin):
def __init__(self,*args,**kwargs):
super(AddressForm,self).__init__(*args,**kwargs)
self.set_required()
In a similar way you can add other validations to the form fields, based on the model validation attributes. For the appearance, change the widget and set the field widget in the mixin.
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)
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)