DjangoRestFramework not validating required = True - python

I am facing a very weird issue today.
Here is my serializer class.
class Connectivity(serializers.Serializer):
device_type = serializers.CharField(max_length=100,required=True)
device_name = serializers.CharField(max_length=100,required=True)
class Connections(serializers.Serializer):
device_name = serializers.CharField(max_length=100,required=True)
connectivity = Connectivity(required = True, many = True)
class Topologyserializer(serializers.Serializer):
name = serializers.CharField(max_length=100,required=True, \
validators=[UniqueValidator(queryset=Topology.objects.all())])
json = Connections(required=True,many=True)
def create(self, validated_data):
return validated_data
I am calling Topologyserializer from a Django view and I am passing a json like:
{
"name":"tokpwol",
"json": [
]
}
As per my experience with DRF since I have mentioned required = True in json field it should not accept the above json.
But I am able to create record.
Can anyone suggest me why it is not validating the json field and how it accepting empty list as json field?
I am using django rest framework 3.0.3

DRF does not clearly state what required stands for for lists.
In its code, it appears that validation passes as long as a value is supplied, even if that value is an empty list.
If you want to ensure the list is not empty, you'll need to validate its content manually. You would do that by adding the following method on your TopologySerializer:
def validate_json(self, value):
if not value:
raise serializers.ValidationError("Connections list is empty")
return value
I cannot test it right now, but it should work.

Related

How Do I Make a Field Either fields.Dict or Specify a Schema?

I have a field on a schema that should be a specific schema based on a value in that field if it exists. To elaborate, the field should be fields.Dict() if it doesn't contain a version property. Otherwise, the schema should be retrieved from a map
def pricing_schema_serialization(base_object, parent_obj):
# Use dictionary access to support using this function to look up schema for both both serialization directions,
# and test data
if type(base_object) is not dict:
object_dict = base_object.__dict__
else:
object_dict = base_object
pricing_version = object_dict.get("version", None)
if pricing_version in version_to_schema:
return version_to_schema[pricing_version]() # Works fine
return fields.Dict(missing=dict, default=dict) # Produces error
class Product:
# pricing = fields.Dict(missing=dict, default=dict) # This worked
pricing = PolyField(
serialization_schema_selector=pricing_schema_serialization,
deserialization_schema_selector=pricing_schema_serialization,
required=False,
missing=dict,
default=dict,
)
This is the error produced:
{'pricing': ["Unable to use schema. Ensure there is a deserialization_schema_selector and that it returns a schema when the function is passed in {}. This is the class I got. Make sure it is a schema: <class 'marshmallow.fields.Dict'>"]}
I saw that marhmallow 3.x has a from_dict() method. Unfortunately, we're stuck on 2.x.
Things I tried:
The code above
Creating my own from_dict class method following the same implementation here. Same result

How to receive get parameter in viewset in django restframe work?

For the part of developing an API using restframe work and DJango, I need to receive few parameters through get method in 'list' function of my Viewset. In client side they are sending data as query prams, I somehow managed to get the data using 'request.query_params.get()' but received data is text format instead of bool, I had to convert this to boolean . see the code how I did this
if 'status' in (request.query_params):
"""receiving input argument is string need to convert that to boolean for further processing"""
input_status=(request.query_params.get('status', None)=='true')
is there any better way to getting data without losing datatype. is it possible to receive 'bool' or 'int' values directly in get parameter?
My model and view set classes are given below
class ModuleType(models.Model):
"""
This module type class used to represent each module of the application. It is inherited from
django content type which holds the complete model informations
"""
#this active field is true this module will be active on our user controller
content_type = models.OneToOneField(
ContentType,
on_delete=models.CASCADE,
related_name='status',
null=True
)
active=models.BooleanField(default=False)
class Meta:
db_table = 'module_types'
My Viewset
class ContentTypeViewSet(viewsets.ModelViewSet):
"""
This api deals all operations related with module management
You will have `list`, `create`, `retrieve`,
update` and `destroy` actions.
Additionally we also provide an action to update status.
"""
queryset = ContentType.objects.all()
serializer_class = ContentTypeSerializer
permission_classes = [permissions.AllowAny]
"""
Over ride list method to list values
"""
def list(self, request):
status='Sucess'
message='details of all modules included'
print(request)
try:
if 'status' in (request.query_params):
#input argument is striing need to convert that to boolean
input_status=(request.query_params.get('status', None)=='true')
validated_value= self.validate_input_params({'status':input_status})
if (validated_value==1):
all_modules = ContentType.objects.filter(status__active=input_status)
message='All value set status as '+str(input_status)
else:
message='Failed to validate all input values'
status='fail'
return Response({"status":status,"data":[],"message":message})
else:
all_modules=ContentType.objects.all()
serializer = self.get_serializer(
all_modules,
many=True
)
return Response({"status":status,"data":serializer.data,'message':message})
except:
message='unknown exception'
status='Fail'
return Response({"status":status,"data":[],"message":message})
Your view will read query params as string.
I suggest you use query_params = dict(request.GET.items())
That way you can parse parameters easier by using dict.

Flask Sqlalchemy add multiple row

I am using flask-restful this is
My class I want to insert
class OrderHistoryResource(Resource):
model = OrderHistoryModel
schema = OrderHistorySchema
order = OrderModel
product = ProductModel
def post(self):
value = req.get_json()
data = cls.schema(many=True).load(value)
data.insert()
In my model
def insert(self):
db.session.add(self)
db.session.commit()
schema
from config.ma import ma
from model.orderhistory import OrderHistoryModel
class OrderHistorySchema(ma.ModelSchema):
class Meta:
model = OrderHistoryModel
include_fk = True
Example Data I want to insert
[
{
"quantity":99,
"flaskSaleStatus":true,
"orderId":"ORDER_64a79028d1704406b6bb83b84ad8c02a_1568776516",
"proId":"PROD_9_1568779885_64a79028d1704406b6bb83b84ad8c02a"
},
{
"quantity":89,
"flaskSaleStatus":true,
"orderId":"ORDER_64a79028d1704406b6bb83b84ad8c02a_1568776516",
"proId":"PROD_9_1568779885_64a79028d1704406b6bb83b84ad8c02a"
}
]
this is what i got after insert method has started
TypeError: insert() takes exactly 2 arguments (0 given)
or there is another way to do this action?
Edited - released marshmallow-sqlalchemy loads directly to instance
You need to loop through the OrderModel instances in your list.
You can then use add_all to add the OrderModel objects to the session, then bulk update - see the docs
Should be something like:
db.session.add_all(data)
db.session.commit()
See this post for brief discussion on why add_all is best when you have complex ORM relationships.
Also - not sure you need to have all your models/schemas as class variables, it's fine to have them imported (or just present in the same file, as long as they're declared before the resource class).
You are calling insert on list cause data is list of model OrderHistoryModel instances.
Also post method doesn't need to be classmethod and you probably had an error there as well.
Since data is list of model instances you can use db.session.add_all method to add them to session in bulk.
def post(self):
value = req.get_json()
data = self.schema(many=True).load(value)
db.session.add_all(data)
db.session.commit()

Check if record exists in Django Rest Framework API LIST/DATABASE

I want to create a viewset/apiview with a path like this: list/<slug:entry>/ that once I provide the entry it will check if that entry exists in the database.
*Note: on list/ I have a path to a ViewSet. I wonder if I could change the id with the specific field that I want to check, so I could see if the entry exists or not, but I want to keep the id as it is, so
I tried:
class CheckCouponAPIView(APIView):
def get(self, request, format=None):
try:
Coupon.objects.get(coupon=self.kwargs.get('coupon'))
except Coupon.DoesNotExist:
return Response(data={'message': False})
else:
return Response(data={'message': True})
But I got an error: get() got an unexpected keyword argument 'coupon'.
Here's the path: path('check/<slug:coupon>/', CheckCouponAPIView.as_view()),
Is there any good practice that I could apply in my situation?
What about trying something like this,
class CheckCouponAPIView(viewsets.ModelViewSet):
# other fields
lookup_field = 'slug'
From the official DRF Doc,
lookup_field - The model field that should be used to for performing
object lookup of individual model instances. Defaults to pk

Suppress "field should be unique" error in Django REST framework

I have a model like
class MyModel(models.Model):
uuid = models.CharField(max_length=40, unique=True)
and a serializer
class MyModelSerializer(serializers.ModelSerializer):
class Meta:
model = MyModel
fields = ('uuid')
And I want to receive JSON with MyModel object but it can be existing objects. So, when I use serializer.is_valid() with data about existing object it gives me an error:
for record in request['records']:
# request - body of JSON request,
# 'records' - array of records I want to add or update
serializer = MyModelSerializer(data=record)
if serializer.is_valid():
# Do stuff
serializer.save()
Error:
{"uuid":["This field must be unique."]}
Is there a way to separate behavior for new and existing objects? Particularly, I want to create new MyModel object if it's not it database yet and update existing MyModel object if it's present.
You are basically overloading a single entry point of your REST API by trying to both create new instances and update existing instances using a POST request. In addition, it seems you are trying to create and update multiple instances simultaneously within a single POST request.
Django REST Framework (DRF) expects a POST request to only create new instances. Therefore, sending an existing instance record triggers a unique constraint violation for the uuid field since DRF tries to create that record as a new instance, as the existing instance already has that uuid value.
A solution to make your REST API more "RESTful" would be to separate the creation and updating of records into POST and PUT requests respectively. It is unclear if you are using the generic API views provided by DRF, but you can use the CreateAPIView to POST new instances, then create a separate UpdateAPIView to PUT and/or PATCH existing instances. Even better you could allow retrieval via GET for both of these endpoints using the generic views ListCreateAPIView and RetrieveUpdateAPIView.
Finally, for handling bulk requests (i.e. multi-instances in a single request) you can either override the built-in view methods or use a 3rd-party package such as django-rest-framework-bulk.
I had a situation where I had a deep create method, with 2 levels of hierarchy above the end point, that it was important that all models were idempotent.
I override the validation in the serializer, and created it by hand.
It is important that you add the field to the class at the top (otherwise the validator won't be run)
class ParticipantSerializer(serializers.HyperlinkedModelSerializer):
device = DeviceSerializer(required=False)
uuid = serializers.CharField()
def validate_uuid(self, value):
if value is not None and isinstance(value, basestring) and len(value) < 256:
return value
else:
if value is not None:
raise serializers.ValidationError("UUID can't be none")
elif isinstance(value, basestring):
raise serializers.ValidationError("UUID must be a string")
elif len(value) < 256:
raise serializers.ValidationError("UUID must be below 256 characters")
else:
raise serializers.ValidationError("UUID has failed validation")
class Meta:
model = Participant
fields = ("uuid", "platform", "device")

Categories

Resources