Django rest framework - serializer for dictionary structured payload - django

I'm trying to create a Serializer for a payload that looks something like this -
{
"2fd08845-9b21-4972-87ed-2e7fd03448c5": {
"operation": "Create",
"operationId": "356f6501-a117-4c8d-98ce-dcb4344d481b",
"user": "superuser",
"immediate": "true"
},
"fe6d0c85-0021-431e-9955-e8e1b1ebc414": {
"operation": "Create",
"operationId": "adcedb2f-c751-441f-8108-2c29667ea9cf",
"user": "employee",
"immediate": "false"
}
}
I thought of using DictField, but my problem is that there isn't a field name. it's only a dictionary of keys and values.
I tried something like:
class UserOperationSerializer(serializers.Serializer):
operation = serializers.ChoiceField(choices=["Create", "Delete"])
operationId = serializers.UUIDField()
user = serializers.CharField()
immediate = serializers.BooleanField()
class UserOperationsSerializer(serializers.Serializer):
test = serializers.DictField(child=RelationshipAuthorizeObjectSerializer())
But again, there isn't a 'test' field.

I think your easiest path forward would be to flatten the payload to the following format:
[
{
"request_id": "2fd08845-9b21-4972-87ed-2e7fd03448c5",
"operation": "Create",
"operationId": "356f6501-a117-4c8d-98ce-dcb4344d481b",
"user": "superuser",
"immediate": "true"
},
{
"request_id": "fe6d0c85-0021-431e-9955-e8e1b1ebc414",
"operation": "Create",
"operationId": "adcedb2f-c751-441f-8108-2c29667ea9cf",
"user": "employee",
"immediate": "false"
}
]
And then serialize it. Otherwise, you'd be creating custom fields/serializers which is not pretty.

The way I finally solved it was to add a dynamic 'body' field that contains the real payload of the request.
class UserOperationSerializer(serializers.Serializer):
operation = serializers.ChoiceField(choices=["Create", "Delete"])
operationId = serializers.UUIDField()
user = serializers.CharField()
immediate = serializers.BooleanField()
class UserOperationsSerializer(serializers.Serializer):
body = serializers.DictField(child=UserOperationSerializer())
def __init__(self, *args, **kwargs):
kwargs['data'] = {'body': kwargs['data']}
super().__init__(*args, **kwargs)
Then, in the View, I will use that data as serializer.validated_data['body']
That did the work for me.

Related

source parameter in django serializer not working

I am trying to serialize a nested request body data (just the part of it)
body -
{
"context": {
"timestamp": "2022-10-13T09:48:47.905Z",
},
"message": {
"intent": {
"item": {
"descriptor": {
"name": "apple"
}
},
}}}
serializer
class SearchSerilizer(serializers.Serializer):
timestamp = serializers.CharField(source="context.timestamp", max_length=35)
Caller snippet
serializer = SearchSerilizer(data=request.data)
if serializer.is_valid():
print(serializer.data)
return Response(serializer.data)
else:
print(serializer.errors)
return Response(serializer.errors)
And it prints
{'timestamp': [ErrorDetail(string='This field is required.', code='required')]}
How can I use the source here to serialize this data?

JSON fields serializer errors in Django rest framework

when I do input data then I want to check the data key is valid or not by Django rest API JSON serialize field.
I have a JSON serializer in serializers.py like as
class EmployBasicInfoSerializers(serializers.Serializer):
basic_info = serializers.JSONField()
designation = serializers.JSONField()
academic_info = serializers.JSONField()
address = serializers.JSONField()
password = serializers.JSONField()
and my views function is:
#api_view(['POST'])
def employ_create(request):
if request.method == 'POST':
json = JSONRenderer().render(request.data)
stream = io.BytesIO(json)
data = JSONParser().parse(stream)
data_serializer = EmployBasicInfoSerializers(data=data)
if data_serializer.is_valid():
pass
return Response({'data_status':data_serializer.errors}, status=rest_framework.status.HTTP_400_BAD_REQUEST)
Then I input JSON data like as:
{
"basic_info": {
"name":"tushar",
"gender":"male",
"date_of_birth":"2020-08-22",
"age":27,
"phone_number":8667,
"email":"mntushar25#gmail.com"
},
"designation":{
"id": 2
},
"academic_info":[
{
"degree":"hsc",
"last_passing_institution_name":"hgygjhgy",
"last_passing_year":"2020-08-22"
},
{
"degree":"hsc",
"last_passing_institution_name":"hgygjhgy",
"last_passing_year":"2020-08-22"
}
],
"address":{
"house_no":8787,
"village_name":"kushtia",
"post_office":"daulatpur",
"thana_name":"daulatpur",
"district_name":"kushtia"
},
"password":"admin"
}
But I get errors form data_serializer validation.
The errors are:
{
"data_status": {
"name": [
"This field is required."
],
"gender": [
"This field is required."
],
"date_of_birth": [
"This field is required."
],
"phone_number": [
"This field is required."
],
"email": [
"This field is required."
]
}
}
I can't understand why these errors occur. pls, help me to solve the errors.....

Django Rest Framework: Get unique list of values from nested structure

I want to be able to return a list of strings from a deeply nested structure of data. In this scenario, I have a API that manages a chain of bookstores with many locations in different regions.
Currently, I have an API endpoint that takes a region's ID and returns a nested JSON structure of details about the region, the individual bookstores, and the books that can be found in each store.
{
"region": [
{
"store": [
{
"book": {
"name": "Foo"
}
},
{
"book": {
"name": "Bar"
}
},
{
"book": {
"name": "Baz"
}
}
],
},
{
"store": [
{
"book": {
"name": "Foo"
}
},
{
"book": {
"name": "Bar"
}
}
],
},
{
"store": [
{
"book": {
"name": "Foo"
}
},
{
"book": {
"name": "Baz"
}
},
{
"book": {
"name": "Qux"
}
}
]
}
]
}
My models look like the following. I am aware these models don't make the most sense for this contrived example, but it does reflect my real world code:
class Book(TimeStampedModel):
name = models.CharField(default="", max_length=512)
class Bookstore(TimeStampedModel):
value = models.CharField(default="", max_length=1024)
book = models.ForeignKey(Book, on_delete=models.CASCADE)
class Region(TimeStampedModel):
stores = models.ManyToManyField(Bookstore)
class BookstoreChain(TimeStampedModel):
regions = models.ManyToManyField(Region)
The serializers I created for the above response look like:
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = "__all__"
class BookstoreSerializer(serializers.ModelSerializer):
books = BookSerializer()
class Meta:
model = Bookstore
fields = "__all__"
class RegionSerializer(serializers.ModelSerializer):
stores = BookstoreSerializer(many=True)
class Meta:
model = Region
fields = "__all__"
class BookstoreChainSerializer(serializers.ModelSerializer):
regions = RegionSerializer(many=True)
class Meta:
model = BookstoreChain
fields = "__all__"
I'm not sure what my view or serializer for this solution need to look like. I'm more familiar with writing raw SQL or using an ORM/Linq to get a set of results.
While the above response is certainty useful, what I really want is an API endpoint to return a unique list of book names that can be found in a given region (Foo, Bar, Baz, Qux). I would hope my response to look like:
{
"books": [
"Foo",
"Bar",
"Baz",
"Qux"
]
}
My feeble attempt so far has a urls.py with the following path:
path("api/regions/<int:pk>/uniqueBooks/", views.UniqueBooksForRegionView.as_view(), name="uniqueBooksForRegion")
My views.py looks like:
class UniqueBooksForRegionView(generics.RetrieveAPIView):
queryset = Regions.objects.all()
serializer_class = ???
So you start from region you have to get the stores, so you can filter the books in the stores, here is a solution which will work.
Note:
Avoid using .get() in *APIView because it will trigger an error if the request does not have the ID, you can use get_object_or_404(), but then you cannot log your error in Sentry.
To get an element from an *APIView, use filter().
import logging as L
class UniqueBooksForRegionView(generics.RetrieveAPIView):
lookup_field = 'pk'
def get(self, *args, **kwargs)
regions = Region.objects.filter(pk=self.kwargs[self.lookup_field])
if regions.exists():
region = regions.first()
stores_qs = region.stores.all()
books_qs = Book.objects.filter(store__in=stores_qs).distinct()
# use your book serializer
serializer = BookSerializer(books_qs, many=True)
return Response(serializer.data, HTTP_200_OK)
else:
L.error(f'Region with id {self.kwargs[self.lookup_field]} not found.')
return Response({'detail':f'Region with id {self.kwargs[self.lookup_field]} not found.'}, HTTP_404_NOT_FOUND)
Note
Here is the flow, the code may need some tweaks, but I hope it helps you understand the flow

Get random questions in a quizgame django API

I am trying to recreate game of hacks because there isnt an API to create my own questions , and implement on external site , however I am using django with restful framework for this task. (I am not sure , if this is the right to achieve this). I will do this via server because I dont want people change js and bypass the stuff or even disable js and stop time , and continue with the same question
apiview.py
#api_view(['GET', 'POST'])
def questions_view(request):
if request.method == 'GET':
questions = Question.objects.all()
serializer = QuestionListPageSerializer(questions, many=True)
return Response(serializer.data)
elif request.method == 'POST':
serializer = QuestionListPageSerializer(data=request.data)
if serializer.is_valid():
question = serializer.save()
return Response(QuestionListPageSerializer(question).data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
serializers
class QuestionListPageSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
question_text = serializers.CharField(max_length=200)
pub_date = serializers.DateTimeField()
was_published_recently = serializers.BooleanField(read_only=True) # Serializer is smart enough to understand that was_published_recently is a method on Question
code = serializers.CharField(max_length=200)
def create(self, validated_data):
return Question.objects.create(**validated_data)
def update(self, instance, validated_data):
for key, value in validated_data.items():
setattr(instance, key, value)
instance.save()
return instance
question list my app
HTTP 200 OK
Allow: POST, OPTIONS, GET
Content-Type: application/json
Vary: Accept
[
{
"id": 1,
"question_text": "helllo",
"pub_date": "2020-01-15T02:30:40Z",
"was_published_recently": false,
"code": "int main (int argc, char *argv[])\r\n{\r\n\tchar *val = argv[argc -1];\r\n\tsystem(val);\r\n\treturn(0);\r\n}"
},
{
"id": 2,
"question_text": "What is the meaning of life?",
"pub_date": "2020-01-15T02:30:40Z",
"was_published_recently": false,
"code": "SOME STRING"
}
]
question with answer my app
HTTP 200 OK
Allow: PATCH, OPTIONS, DELETE, GET
Content-Type: application/json
Vary: Accept
{
"id": 1,
"question_text": "helllo",
"pub_date": "2020-01-15T02:30:40Z",
"was_published_recently": false,
"code": "int main (int argc, char *argv[])\r\n{\r\n\tchar *val = argv[argc -1];\r\n\tsystem(val);\r\n\treturn(0);\r\n}",
"choices": [
{
"id": 1,
"choice_text": "11111"
}
]
}
game of hacks external app
{
"_id": "53fb4014e5d4d40400c7fa4f",
"answers": [
"LDAP Injection",
"CGI Reflected XSS",
"Connection String Injection",
"Reflected XSS"
],
"batch_score": 39.89902034664657,
"checkmarx": true,
"from": "Gilad",
"language": "PHP",
"level": "1",
"question": "What vulnerability the following code contains?",
"snippets": [
{
"code": "<?php\n$dn = $_GET['host'];\n$filter=\"(|(sn=$person*)(givenname=$person*))\";\n$justthese = array(\"ou\", \"sn\", \"givenname\", \"mail\");\n$sr=ldap_search($ds, $dn, $dn, $justthese);\n$info = ldap_get_entries($ds, $sr);\necho $info[\"count\"].\" entries returned\n\";?>"
}
],
"tip": ""
}
example
http://www.gameofhacks.com/api/answer
answer
{quest: true, score: 5700}
quest: true
score: 5700
http://www.gameofhacks.com/api/question # make random instead of api/poll/question/{id} ?
You can use python random library in your view:
from random import randint
def random_question(request):
i = randint(0, Question.objects.count() - 1)
question = Question.objects.all()[i] # get question at random position
....

Flatten json return by DRF

I have json API returned as below format.
But I want to return json API decomposing namingzone key as specified below.
Could anyone tell me how I can revise serializer to achieve this?
serializer.py is also specified below.
For models.py and views.py, please refer to my previous post.
current
{
"zone": {
"zone": "office_enclosed",
"namingzone": [
{
"naming": "moffice"
}
]
},
"lpd": 11.9,
"sensor": true
},
{
"zone": {
"zone": "office_open",
"namingzone": [
{
"naming": "off"
},
{
"naming": "office"
}
]
},
"lpd": 10.5,
"sensor": true
}
Target
{
"zone": "office_enclosed",
"naming": "moffice",
"lpd": 11.9,
"sensor": true
},
{
"zone": "office_open",
"naming": "off",
"lpd": 10.5,
"sensor": true
},
{
"zone": "office_open",
"naming": "office",
"lpd": 10.5,
"sensor": true
}
serializer.py
class namingNewSerializer(serializers.ModelSerializer):
class Meta:
model=Naming
fields=('naming',)
class zoneSerializer(serializers.ModelSerializer):
namingzone=namingNewSerializer(many=True)
class Meta:
model=Zone
fields = ('zone','namingzone')
class lightSerializer(serializers.ModelSerializer):
zone = zoneSerializer()
class Meta:
model=Light
fields = ('zone','lpd','sensor')
class namingSerializer(serializers.ModelSerializer):
zone=zoneSerializer()
class Meta:
model=Naming
fields=('zone','naming')
I would say using Serializer might complicate the implementations. Rather, you can take an pythonic approach. Try like this:
class SomeView(APIView):
...
def get(self, request, *args, **kwargs):
data = lightSerializer(Light.objects.all(), many=True).data
data = list(data) # convert lazy object to list
updated_data = list()
for item in data:
newdict = dict()
zone = item['zone']
newdict.update({'zone':zone['zone'], 'lpd': item['lpd'], 'sensor':item['sensor']})
for naming_zone in zone.get('namingzone'):
naming_zone.update(newDict)
updated_data.append(naming_zone)
return Response(updated_data, status=status.HTTP_200_OK)
See DRF Field document about source. It will help you.
https://www.django-rest-framework.org/api-guide/fields/#source