Validating optional field in marshmallow - python

I have a field in my model which is required in the resource body while creating the resource (POST) and is not passed in the request body(will be passed along with URL) while updating the resource (PUT).
I have created my marshmallow model:
class DummySchema():
field_1 = fields.Str(required=True)
field_2 = fields.Id()
If I leave field_1 to required=True, I get the following error on PUT :
{
"message": {
"field_1": [
"Missing data for required field."
]
}
}
I could leave it required in the model, but that would mean it would cause a problem in POST request.
Is there a way I could set the field as optional, but set it required for my POST request so that I can perform my validation?

I think I should've read the documentation thoroughly before :(
I could set a field as partial, so when it'll do validation marshmallow would skip the field
data, errors = DummySchema().load({'field_2': 42}, partial=('field_1',))
REF: https://marshmallow.readthedocs.io/en/2.x-line/quickstart.html#validation

If you want to use it for /POST request then this field can be added in dump_only list.
This can also be used for /PUT request.
class StrategySchema(ma.SQLAlchemyAutoSchema):
class Meta:
model = Strategy
sqla_session = db.session
ordered = True
load_instance = True
dump_only = ( # read-only
"id",
"created_by",
"created_by_id",
"created_at",
"updated_at",
)
dump_only means:
Consider these fields only while dumping a model to json (deserialization)
ignore it while loading a model from json
read-only fields in other words

Related

How to serialize JSON Request data from serializer in Django?

I am trying to serialize a json data through serializers.Serializer
{
"data": {
"phoneNumber": "1234567890",
"countryCode": "+11",
"otp": "73146",
}
}
The sterilizer class I wrote for it
class VerifyOtpSerializer(serializers.Serializer):
phone_number = serializers.CharField(max_length=225, source='phoneNumber', required=True)
country_code = serializers.CharField(max_length=225, source='countryCode', required=True)
otp = serializers.CharField(max_length=255, required=True)
and also
I don't know why source is not working, I tried the JSON in the picture below but still it's saying the field is required
source value is what the passed value's key will be changed into. So source value is expected to be on your Model.
The name of the attribute that will be used to populate the field.
What you really want is something that changes camel case payload into a snake case. Just use djangorestframework-camel-case and remove source from your serializer fields.
Your keys are wrong in the request. as Tom said the source should be an attribute of the model object. so you have to match keys in request and serializer
change phoneNumber > phone_number
change countryCode > country_code
The response object you are are sending to your serializer is in correct. The key of your request object should be exactly what you have defined in your serializer.
Try sending this to your serializer.
{
"data" : {
"phone_number":"1234567890",
"country_code":"+11",
"otp":"73146"
}
}

Django REST Framework - Reference to the User who added the order

I am creating an application in Django REST Fremework, in which the user can add an order.
I would like the serializer to set a reference to the user based on the token and complete the "Client" model field.
It's actually works with HiddenField, as shown in the documentation.
(Link: https://www.django-rest-framework.org/api-guide/fields/#hiddenfield)
class OrderSerializer(serializers.ModelSerializer):
client = serializers.HiddenField(default=serializers.CurrentUserDefault())
class Meta:
model = Order
fields = '__all__'
The problem is that when I fetch a single order or list of orders, Client field is of course hidden becouse of HiddenField type.
curl -X GET http://127.0.0.1:8000/api/orders/12
{
"id":12,
"name":"sprzatanie ogrodka",
"description":"dupa",
"price":"12.20",
"work_time_hours":2,
"work_time_minutes":50,
"workers_needed_num":3,
"coords_latitude":"-1.300000",
"coords_longitude":"1.100000",
"created_at":"2020-03-08T13:20:16.455289Z",
"finished_at":null,
"category":1,
"workers":[]
}
I would like the field to still capture reference to the logged in user, but at the same time to be visible when returning data from the API.
What serializers field type I need to use?
Thanks!
Going through the documentation i found: https://www.django-rest-framework.org/api-guide/validators/
Using a standard field with read_only=True, but that also includes a default=… argument. This field will be used in the serializer output representation, but cannot be set directly by the user.
this is what you need i think.
So whatever field type you have set in Model can be used with read_only=True
For example:
client = serializers.PrimaryKeyRelatedField(read_only=True, default=serializers.CurrentUserDefault())
Hope this helps

Nested validation in Django Rest Framework

Using django rest framework I want to validate fields.
Correct input request:
{
test_field_a: {test_field_c: 25},
test_field_b: {}
}
My serializers.py (I don't have any associated models and the models.py itself):
from rest_framework import serializers
class TestSerializer(serializers.Serializer):
test_field_a = serializers.JSONField(label='test_field_a', allow_null=False, required=True)
test_field_b = serializers.JSONField(label='test_field_b', required=True)
test_field_c = serializers.IntegerField(label='test_field_c)
Wrong input request (which should state that int field is required) :
{
test_field_a: {test_field_c: 'wrong'},
test_field_b: {}
}
Now test_field_a and test_field_b are validated as required. But how to make validation of fields on different levels of the request? (in this case test_field_c)
JSONField just checks that a field contains correct JSON structure. You need to do it plus check values from this JSON.
There are several ways to do it:
You can write your own custom field type (it's nice if you are planning to do something similar in other serializers);
You can change field validation (try something like this):
from rest_framework import serializers
class TestSerializer(serializers.Serializer)::
test_field_a = serializers.JSONField(label='test_field_a', allow_null=False, required=True)
test_field_b = serializers.JSONField(label='test_field_b', required=True)
def validate_test_field_a(self, value):
"""
Check that test_field_a .
"""
if not isinstance(value.get('test_field_c'), int):
raise serializers.ValidationError("Some error message")
return value
You can try nested validation:
from rest_framework import serializers
class Test1Serializer(serializers.Serializer):
test_field_c = serializers.IntegerField(label='test_field_c')
class TestSerializer(serializers.Serializer):
test_field_a = Test1Serializer()
test_field_b = serializers.JSONField(label='test_field_b', required=True)
The serializer's JSONField does not have a validation for nested fields because it is not meant to nest explicitly declared fields and as far as I know, there is currently no way to specify a json schema to validate it.
What you can do is validate the field yourself by declaring a validate_test_field_a validation method.
For example:
def validate_test_field_a(self, value):
if 'test_field_c' not in value:
raise serializers.ValidationError('`test_field_c` is required')
return value
Generally, if you find yourself needing to validate the nested type inside the JSONField, then it is a sign of bad architecture and you should consider using nested serializers instead. Same applies to using JSONField in the model

Django Rest Framework: Overwriting validation error keys

When translating my site to another language a problem occurred.
I want to handle Validation Errors properly and allow my front-end friends to display them well.
Is there a way to overwrite keys in response message of DRF when Validation Error happend?
What do I mean by that - I want to change this:
{
"name": ["This field is required."]
}
into:
{
"username": ["This field is required."]
}
Is there a way to do that without writing each and every one of validators?
You can change the name field in the ModelSerializer to username.
example:
class CustomSerializer(serializers.ModelSerializer):
username = serializers.CharField(source='name')
class Meta:
model = ...
fields = ('username', ...)
Now in validation errors it will have the key username instead.

How to post to a Django REST Framework API with Related Models

I have two related models (Events + Locations) with a serialzer shown below:
class Locations
title = models.CharField(max_length=250)
address = model.CharField(max_length=250)
class Events
title = models.CharField(max_length=250)
locations = models.ForeignKey(Locations, related_name='events'
class EventsSerializer(serializers.ModelSerializer):
class Meta:
model = Events
depth = 1
I set the depth to 1 in the serializer so I can get the information from the Locations model instead of a single id. When doing this however, I cant post to events with the location info. I can only perform a post with the title attribute. If I remove the depth option in the serializer, I can perform the post with both the title and location id.
I tried to create a second serializer (EventsSerialzerB) without the depth field with the intention of using the first one as a read-only response, however when I created a second serializer, viewset, and added it to the router, it would automatically override the original viewset.
Is it possible for me to create a serializer that outputs the related model fields, and allows you to post directly to the single model?
// EDIT - Here's what I'm trying to post
$scope.doClick = function (event) {
var test_data = {
title: 'Event Test',
content: 'Some test content here',
location: 2,
date: '2014-12-16T11:00:00Z'
}
// $resource.save() doesn't work?
$http.post('/api/events/', test_data).
success(function(data, status, headers, config) {
console.log('sucess', status);
}).
error(function(data, status, headers, config) {
console.log('error', status);
});
}
So when the serializers are flat, I can post all of these fields. The location field is the id of a location from the related Locations table. When they are nested, I can't include the location field in the test data.
By setting the depth option on the serializer, you are telling it to make any relation nested instead of flat. For the most part, nested serializers should be considered read-only by default, as they are buggy in Django REST Framework 2.4 and there are better ways to handle them in 3.0.
It sounds like you want a nested representation when reading, but a flat representation when writing. While this isn't recommended, as it means GET requests don't match PUT requests, it is possible to do this in a way to makes everyone happy.
In Django REST Framework 3.0, you can try the following to get what you want:
class LocationsSerializer(serializers.ModelSerializer):
class Meta:
model = Locations
fields = ('title', 'address', )
class EventsSerializer(serializers.ModelSerializer):
locations = LocationsSerializer(read_only=True)
class Meta:
model = Events
fields = ('locations', )
class EventViewSet(viewsets.ModelViewSet):
queryet = Event.objects.all()
serializer_class = EventsSerializer
def perform_create(self, serializer):
serializer.save(locations=self.request.data['locations'])
def perform_update(self, serializer):
serializer.save(locations=self.request.data['locations'])
A new LocationsSerializer was created, which will handle the read-only nested representation of the Locations object. By overriding perform_create and perform_update, we can pass in the location id that was passed in with the request body, so the location can still be updated.
Also, you should avoid having model names being plurals. It's confusing when Events.locations is a single location, even though Locations.events is a list of events for the location. Event.location and Location.events reads a bit more clearly, the Django admin will display them reasonably, and your fellow developers will be able to easily understand how the relations are set up.

Categories

Resources