Serialize a string without changes in Django Rest Framework? - python

I'm using Python's json.dumps() to convert an array to a string and then store it in a Django Model. I'm trying to figure out how I can get Django's REST framework to ignore this field and send it 'as is' without serializing it a second time.
For example, if the model looks like this(Both fields are CharFields):
name = "E:\"
path_with_ids= "[{"name": "E:\", "id": 525}]"
I want the REST framework to ignore 'path_with_ids' when serializing so the JSON output will look like this:
{ "name": "E:\", "path_with_ids":
[ {"name": "E:\", "id": 525} ] }
and not like this:
{
"name": "E:\",
"path_with_ids": "[{\"name\": \"E:\\\", \"id\": 525}]" }
I've tried to make another serializer class that spits out the input it gets 'as is' without success:
Serializers.py:
class PathWithIds(serializers.CharField):
def to_representation(self, value):
return value.path_with_ids
class FolderSerializer(serializers.ModelSerializer):
field_to_ignore = PathWithIds(source='path_with_ids')
class Meta:
model = Folder
fields = ['id', 'field_to_ignore']
Please help!

I ended up using a wasteful and sickening method of deserializing the array before serializing it again with the REST framework:
Serializers.py:
import json
class PathWithIds(serializers.CharField):
def to_representation(self, value):
x = json.loads(value)
return x
class FolderSerializer(serializers.ModelSerializer):
array_output = PathWithIds(source='field_to_ignore')
class Meta:
model = Folder
fields = ['id', 'array_output']
Output in the rest API:
{
"name": "E:\",
"array_output": [
{
"name": "E:\",
"id": 525
}
] }

Related

django rest framework returning list instead of dict after serializering data

I am returning some data but instead of dict I am getting list in return
my serializer.py file
class CommitteeDetailsSerializer(serializers.ModelSerializer):
class Meta:
model = Committee
fields = ['id','com_name', 'com_members_limit', 'com_amount_per_month',
'com_starting_date', 'com_join_id', 'com_status']
my views.py
serializer = CommitteeDetailsSerializer(get_committee, many=True)
I am getting this data list
{
"status": "success",
"msg": "Done.",
"data": [
{
"id": 19,
"com_name": "My second Committee",
"com_members_limit": 4,
"com_amount_per_month": 1000,
"com_starting_date": "2022-05-04",
"com_join_id": "GR9PKKOX65",
"com_status": "pending"
}
]
}
what I want is dict here in data {}
In your views.py in the serializer you passed many=True, hence its returning a list. Remove that and you should get one single object serialized instead of a list.

Django REST Framework updated nested object not in Model

I only want to update all order in my_stage by using PUT, something like:
"Payload":
{
"stages": [
{
"stage_id": 1,
"position": 2
},
{
"stage_id": 2,
"position": 3
},
{
"stage_id": 3,
"position": 4
}
]
}
and "Response":
{
"stages": [
{
"stage_id": 1,
"position": 2
},
{
"stage_id": 2,
"position": 3
},
{
"stage_id": 3,
"position": 4
}
]
}
But I don't have "stages" in my model so I cannot use ModelSerializer. But I have to implement create() first.
So what should I do to implement update? Thank you.
My stage model is:
class Stage(models.Model):
class Meta:
db_table = 'stage'
position = models.IntegerField()
Here are my serialzier.py
class StagePositionSerializer(ModelSerializer):
"""Serialize order"""
# company_id = IntegerField(read_only=True)
stage_id = IntegerField(source='id', read_only=True)
position = IntegerField(min_value=0)
class Meta:
model = Stage
fields = [
'stage_id',
'position'
]
class PositionSerializer(Serializer):
stages = StagePositionSerializer(many=True)
and my view.py
class StagePositionListView(APIView):
serializer_class = PositionSerializer
If you only want to have "stages:" prepended to your data you can create a custom endpoint in the viewset and specify the formatting you want returned.
I'm not sure why you have a model serializer as well as a non-model serializer. What does that buy you?
Here is some sample code which would add 'get_stages' to your api url.
views.py:
class StagePositionViewSet(viewsets.ModelViewSet):
queryset = Stage.objects.all()
serializer_class = StagePositionSerializer
#list_route(methods=['GET'])
def get_stages(self, request, **kwargs):
try:
stage_list = Stage.objects.all()
serializer = StagePositionSerializer(stage_list , many=True)
results = dict()
#this is where you add your prepended info
results['stages'] = serializer.data
return Response(results, status=status.HTTP_200_OK)
except Exception as e:
return Response(e, status=status.HTTP_400_BAD_REQUEST)
Then if you perform a GET at the URL whatever_your_url/is/get_stages you will get back the payload format you want.
You could easily take advantage of serializer(many=True), which will match a list of serializers. Your serializer would be:
class StagePositionSerializer(ModelSerializer):
class Meta:
model = Stage
fields = [
'id',
'position'
]
class PositionSerializer(Serializer):
stages = StagePositionSerializer(many=True)

Django Rest Framework - OPTIONS request - Get foreign key choices

Lets say I have two models in Django, and I'm using standard Viewsets and Serializers to expose endpoints.
ModelA has a ForeignKey field to ModelB. When I do an OPTIONS request to the ModelA endpoint it just lists it as a field, like below:
"actions": {
"POST": {
"pk": {
"type": "integer",
"required": false,
"read_only": true,
"label": "ID"
},
"created": {
"type": "datetime",
"required": false,
"read_only": true,
"label": "Created"
},
"foreign_key_field": {
"type": "field",
"required": true,
"read_only": false,
"label": "Wcp"
},
}
}
I would like it to have a 'choices' property which pulls down every single instance of Model B. This way in my Angular frontend I can create a dropdown of all the choice.
I think I need to make a new MetaData class or somthing. But I can't find any solid way of doing this. I also think this functionality has changed in 3.X of DRF so old answers around the internet no longer help.
Cheers,
Dean
Made some improvements to #ralfzen his answer to improve serialization.
Also needed to use RelatedField instead of just ManyRelatedField
from django.utils.encoding import force_text
from rest_framework.views import APIView
from rest_framework.metadata import SimpleMetadata
from rest_framework.relations import ManyRelatedField, RelatedField
class MyMetaData(SimpleMetadata):
def get_field_info(self, field):
field_info = super(MyMetaData, self).get_field_info(field)
if isinstance(field, (RelatedField, ManyRelatedField)):
field_info['choices'] = [
{
'value': choice_value,
'display_name': force_text(choice_name, strings_only=True)
}
for choice_value, choice_name in field.get_choices().items()
]
return field_info
class MyView(APIView):
metadata_class = MyMetaData
I don't have a perfect answer as well, but my way of achieving this was mapping json items to their ids and storing such a collection like this (in angular):
$scope.nodes = response.data; //original data retrieved from django
$scope.nodesIds = response.data.map(function (v) { return v.id; }); //particular ids
doing this, you're able to use that map in ng-options in such a way:
<select ng-model="node_start" ng-options="node for node in nodesIds" />
Any better ideas?
//After thorough consideration (it turns out that this is a 'manual' approach)
from rest_framework.metadata import SimpleMetadata
class MyMetadata(SimpleMetadata):
def get_serializer_info(self, serializer):
orderedDict = super().get_serializer_info(serializer)
orderedDict['foreign_key_field']['choices'] = [x.id for x in Node.objects.all()]
return orderedDict
class ViewSet(ModelViewSet):
metadata_class = MyMetadata
Please let me know if it works for you :)
//Finally (smart approach with changes in default DRF code)
If you want it fully automatically, all you have to do is adding three lines of code to metadata.py:
on top:
from rest_framework.relations import PrimaryKeyRelatedField
in get_field_info method:
if type(field) is PrimaryKeyRelatedField:
field_info['choices'] = [x.id for x in field.queryset.all()]
I got this Issue solved thanks #detoix hint like this:
from rest_framework.relations import ManyRelatedField
from rest_framework.metadata import SimpleMetadata
class MyMetaData(SimpleMetadata):
def get_field_info(self, field):
field_info = super(MyMetaData, self).get_field_info(field)
if isinstance(field, ManyRelatedField):
field_info['choices'] = field.get_choices()
return field_info
And in views.py:
class ViewSet(ModelViewSet):
metadata_class = MyMetadata

Serializing nested object to string in Django Rest Framework

I am trying to connect my Django application to the mailchimps API with help from the Django Rest Framework, if I want to create a batch operation, I need to send the following call:
{
"operations": [
{
"method": "PUT",
"path": "lists/abc123/members/817f1571000f0b843c2b8a6982813db2",
"body": "{\"email_address\":\"hall#hallandoates.com\", \"status_if_new\":\"subscribed\"}"
},...
]
}
As you can see, the body object should be a json string. To create these calls, I created a model operation:
models.py
class Operation(object):
def __init__(self, method, list, contact):
email_clean = contact.email_address.lower().encode('utf-8')
subscriber_hash = hashlib.md5(email_clean).hexdigest()
serializer = ContactSerializer(contact)
body = serializer.data
self.method = method
self.path = "lists/%s/members/%s" % (list, subscriber_hash)
self.body = body
And the following serializer:
serializer.py
class OperationSerializer(serializers.Serializer):
method = serializers.CharField()
path = serializers.CharField()
body = serializers.CharField(allow_blank=True, required=False)
When I use my serializer to generate JSON, and parse the data with JSONRenderer(), the following call is returned:
{
"operations": [
{
"method": "PUT",
"path": "lists/abc123/members/817f1571000f0b843c2b8a6982813db2",
"body": "{\'email_address\':\'hall#hallandoates.com\', \'status_if_new\':\'subscribed\'}"
},...
]
}
This call breaks because of the single quotes, can anyone help me a hand in solving this?

How to access ForeignKey data without making extra queries in Wagtail(django)

I have the following two classes in my app.models and i'm using the wagtail APIs to get the data as json
class AuthorMeta(Page):
author=models.OneToOneField(User)
city = models.ForeignKey('Cities', related_name='related_author')
class Cities(Page):
name = models.CharField(max_length=30)
So, when I try /api/v1/pages/?type=dashboard.AuthorMeta&fields=title,city, it returns the following data:
{
"meta": {
"total_count": 1
},
"pages": [
{
"id": 11,
"meta": {
"type": "dashboard.AuthorMeta",
"detail_url": "http://localhost:8000/api/v1/pages/11/"
},
"title": "Suneet Choudhary",
"city": {
"id": 10,
"meta": {
"type": "dashboard.Cities",
"detail_url": "http://localhost:8000/api/v1/pages/10/"
}
}
}
]
}
In the city field, it returns the id and meta of the city. How can I get the name of the city in the response here, without making an extra query? :/
I couldn't find any solution in the Documentation. Am I missing something?
Use Django model property to return through the ForeignKey:
class AuthorMeta(Page):
author=models.OneToOneField(User)
city = models.ForeignKey('Cities', related_name='related_author')
city_name = property(get_city_name)
def get_city_name(self):
return self.city.name
Check Term Property to better understand the concept
In case you have the foreign key in a Streamfield, e.g. a PageChooserBlock, you can customize the api response by overwriting the get_api_representation of a block, as described in the example as provided here:
class CustomPageChooserBlock(blocks.PageChooserBlock):
""" Customize the api response. """
def get_api_representation(self, value, context=None):
""" Return the url path instead of the id. """
return value.url_path

Categories

Resources