Django: Serializer automatically parse json field and add to common fields - json

I call serializer:
serializer = MySerializer(qs, many=True) when qs - QuerySets for myModel
from rest_framework.serializers import Serializer
class MySerializer(Serializer):
param1 = CharField()
param2 = IntegerField(required=False)
custom_fields = JSONField()
class Meta:
pass
Next, I just use the custom_fields and get the values manually.
Is it possible at this stage to get the fields inside this custom_fields and return them through the serializer?
custom_fields contains:
{
'custom_value1': 3,
'custom_value2': 5
}
updated: What i want to get after serializer:
{
'param1': 'value1',
'param2': 'value2',
'custom_value1': 3,
'custom_value2': 5
}

I'm afraid I exactly understand your question... You're asking that you can GET your custom field's data?
Then answer is Yes
If you just call GET request to APIView using MySerializer, you can get all data from your model.
If you want to get data from your Model, you simply using ModelSerializer. (http://www.django-rest-framework.org/api-guide/serializers/#modelserializer). ModelSerializer is much easier but you can use your own serializer, too.
Nowadays I experienced similar situation, and I can get all my extra data (same as you, it's json object)
I attach my serializer and respone. I hope it helps you.
My serialzier (extra field is JsonField)
class JobUserSerializer(serializers.ModelSerializer):
class Meta:
model = JobUser
fields = (
"email",
"is_active",
"is_staff",
"id",
"extra",
)
read_only_fields = (
"id",
)
My response (GET request to viewsets. You can see extra json data)
{
"email": "test5#test.com",
"is_active": true,
"is_staff": false,
"id": 13,
"extra": {
"last_name": "kim",
"first_name": "seul",
"gcf_spouses_name": "test",
"gcf_spouses_position": "test"
}
},
Update
I think you can use get_* method and use it.
this is example code for mine
class JobUserSerializer(serializers.ModelSerializer):
first_name_from_extra = serializers.SerializerMethodField()
class Meta:
model = JobUser
fields = (
"email",
"is_active",
"is_staff",
"id",
# "extra",
"first_name_from_extra"
)
read_only_fields = (
"id",
)
def get_first_name_from_extra(self, obj):
try:
return obj.extra['first_name']
except TypeError:
return ""
Then you can access directly "first_name" (my extra json field's key)
My response
{
"email": "test5#test.com",
"is_active": true,
"is_staff": false,
"id": 13,
"first_name_from_extra": "seul"
},
Be careful using this method: your json field SHOULD have that key. If one of your serializing model don't have the key, it raises NoneType TypeError. Or you can use try/except in get_* method.
Hope helping!

Related

Scala: Json sorting operations on a json file which has different datatypes but same schema

I have a json file which has two types of data for store and online.But datatypes of few columns are different ,but column names and schema are same.
I would like to do some operations on json like sorting,re-arranging using spray joson library in Scala.But I am not able to apply the case class on josn becuase datatype of "storeId" and "amount" are different.
Is there any way we can re-write case class to handle bothe integer and string ?
Example : I will have them in a file, read it line by line and assign it to string
Given inputJosn String :
{
"purchaseType": "1",
"purchaseChannel": "store",
"storeId": 6167,
"paymentType": [{
"type": "CASH",
"category": "Cash",
"amount": 3.91
}]
},
{
"purchaseType": "2",
"purchaseChannel": "online",
"storeId": "6168",
"paymentType": [{
"type": "Card",
"category": "Card",
"amount": "5.04"
}]
}
Code :
import spray.json._
import DefaultJsonProtocol._
case class payType(`type`:String,category:String,amount:String)
case class Transactions(purchaseType:String,purchaseChannel:String,storeId:String,paymentType: payType )
object MyJsonpayType extends DefaultJsonProtocol {
implicit val payTypeFormat: JsonFormat[payType] = jsonFormat3(payType)
}
import MyJsonpayType._
object MyJsonTransactions extends DefaultJsonProtocol {
implicit val TransactionsFormat: JsonFormat[Transactions] = jsonFormat4(Transactions)
}
import MyJsonTransactions._
inputJosn.parseJson.convertTo[Transactions]
It throws errror saying expecing JsString but got 6167.
I know why is it throwing error, but is these any way to re-writw case class in bettwer way to handle both integer and string.
You can get away with using Either, like
case class payType(`type`:String,category:String,amount:Either[Double,String])
But then you are simply kicking the can down the road. If you have to do any operation on the amount field like- sort, aggregation you would need to normalise the data.
If that's case I would better write custom de-serialization for the object with having a single type.
In these situations I like to use the Jackson library, but in this situation you could just call the storeId:string. After that you can parse it to long by using a companion object.
Try this:
case class Transactions (
purchaseType:String,
purchaseChannel:String,
storeId:String,
paymentType: payType ) {
def storeIdToLong(t: Transaction) = TransactionsAsLong(
purchaseType = t.purchaseType,
purchaseChannel = t.purchaseChannel,
storeId = t.storeId.toLong,
paymentType = t.paymentType
)
}
case class TransactionsAsLong (
purchaseType:String,
purchaseChannel:String,
storeId:Long,
paymentType: payType )
object Transactions {
implicit def transactions_to_long(t: Transactions): TransactionsAsLong = t.storeIdToLong()
}
Now you can write val test: Transaction = TransactionsAsLong(...) if you need that.
I would suggest to write a custom deserializer instead :-)

Django RestFramework returning a precomputed json

Currently I have this:
class MySerializer(serializers.ModelSerializer):
class Meta:
model = MyModel
fields = (
'id', 'f0', 'f1', 'f2')
And it returns something like this:
{
"count": 6242,
"previous": null,
"total_pages": 209,
"results": [
{
"id": 63915,
"f0": "Some stuff"
.....
},
{
"id": 63916,
"f0": "Some other stuff"
.....
}....
]
}
And this is good, but I noticed that serializing the data is actually quite expensive to do on the fly, so I would like to precompute it. So far I've managed to precompute it and store it in a jsonfield for my model, the problem is my API is now returning {'json_repersentation':{myold_response}}
class MySerializer(serializers.ModelSerializer):
class Meta:
model = MyModel
fields = ('json_representation',)
My question is, is it possible to change it so that it simply returns the json contained within json_representation field without the "overhead" of {'json_representation':{id:0, f0:label...}} and instead just simply {id:0, f0:label...}
You can override the serializer to_representation method:
def to_representation(self, instance):
data = super(MySerializer, self).to_representation(instance)
return data['json_representation']

Change structure of serialized object in Django Rest Framework (pull ID field outside of model content)

Say, you have a model with name and e-mail and used Model Serializer to serialize your data with Django Rest Framework. The output would be something like this:
[{
"id": "1",
"name": "Alex",
"email": "alex#host.com"
}, {
"id": "2",
"name": "Henry",
"email": "henry#host.com"
}]
Is there a way to "pull" a unique field outside the rest of the model content to get this?:
{
"1": {
"name": "Alex",
"email": "alex#host.com"
},
"2": {
"name": "Henry",
"email": "henry#host.com"
}
}
I am able to "externalize" the ID by overriding serializer's to_representation() method (code sample below) but I can't get rid of the rudimental wrappers - currently JRF will return everything as list[] of dict(instance) similar to this:
data = list
for item in instance:
list.append(item.as_dict())
I don't need this to be a list of dicts, I want them to do be dict of dict?
data = {}
for item in instance:
data[item.id] = item.as_dict()
here's my to_representation() code, I can only manipulate what's returned for individual instance, so it has to be a dict, but I need this dict to be merged, rather than stacked at the output when serializer.data is fully cooked.
def to_representation(self, instance):
rep = OrderedDict()
fields = self._readable_fields
id_field = filter(lambda i: i.label == "ID", fields)[0]
if id_field:
id_atr = id_field.get_attribute(instance).__str__()
else:
raise Exception('cannot serialize models without ID field')
rep[id_atr] = {}
for field in fields:
try:
attribute = field.get_attribute(instance)
except SkipField:
continue
if attribute is None:
rep[id_atr][field.field_name] = None
else:
rep[id_atr][field.field_name] = field.to_representation(attribute)
return rep
I was able to make it work by reformatting the original serializer output, but it's surely not a clean solution with a lot of performance impact:
def list(self, request, *args, **kwargs):
_data = super(ChatMessageViewSet, self).list(request, *args, **kwargs)
_resp = {}
for item in _data.data:
_id = item.get('id', None)
if not _id:
raise Exception('cannot serialize data without id field')
_resp[_id] = {}
for element in item:
_resp[_id][element] = item[element]
_data.data = _resp
return _data
as per #zaphod100-10 answer, I have created a custom ListSerializer class:
class ChatMessageListSerializer(serializers.ListSerializer):
def to_representation(self, data):
iterable = data.all() if isinstance(data, models.Manager) else data
_data = {}
for item in iterable:
_data[item.id] = self.child.to_representation(item)
return _data
and it formats the data the way I need this:
but my data does not survive 'ListModelMixin':
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
it returns a list[] of just the entity ids: [1L, 2L, 3L...]
Create a custom ListSerializer for your model and override the to_representation method.
def to_representation(self, data):
# convert your data which is a queryset or a
# list of objects to dict of dicts
.....
return dict_of_dicts
After creating the ListSerializer in your main serializer class add list_serializer_class meta property.
read more about list serializer here:
http://www.django-rest-framework.org/api-guide/serializers/#listserializer

django rest framework view with merged results from different object serializers

having the following models
class TreeLifephase(DBordered):
name = models.CharField(max_length=200)
def __unicode__(self):
return self.name
class TreeWidth(DBordered):
name = models.CharField(max_length=200)
def __unicode__(self):
return self.name
and many more like this, that contain editable attributes of my Tree objects. For a select field on the UI I want to have all available treelifephases and treewidths with one query - to have a json result that looks something like
{
"treelifephases": [
{
"id": 1,
"name": "young"
},
{
"id": 2,
"name": "medium"
},
{
"id": 3,
"name": "old"
}
],
"treewidths": [
{
"id": 1,
"name": "10-20cm"
},
{
"id": 2,
"name": "21-30cm"
},
{
"id": 3,
"name": "31-40cm"
}
]
}
I have serializers for the Models at hand and it would be awesome to have a view that could just get a list of serializers to return a resultset like the above.
You can use SerializerMethodField and you need to create a model, in order to use model serializer.
class FullTree(serializers.ModelSerializer):
full_tree = serializers.SerializerMethodField()
class Meta:
model = FullTreeModel
fields = ('treewidth','lifephase')
get_full_tree(obj):
treewidth = obj.treewidths # get all your treewidth
lifephase = obj.lifephases# get all your lifephases
//build your tree with your serializers
return your_full_tree
I came up with a good solution for my use case which I claryfied in the question above:
1st: I wrote a Serializer that only gives me the id and the name
class IDNameSerializer(serializers.BaseSerializer):
def to_representation(self, obj):
return {
'id': obj.id,
'name': obj.name
}
2nd: I wrote a view that gives me the required json response with the help of the #api-view decorator from django-rest-framework
from rest_framework.decorators import api_view
from rest_framework.response import Response
#api_view(['GET'])
def tree_select(request):
result = {}
result['treelifephases'] = IDNameSerializer(TreeLifephase.objects.all(),many=True).data
result['treewidths'] = IDNameSerializer(TreeWidth.objects.all(),many=True).data
return Response(result)
Why I chose this as an answer:
Through use of #api_view decorator, we can still use permission classes and other sugar
It gives the json response in the required format
It is easy to alter when new fields might come there is only the view to be changed
I don't need a django model only for serialization
It is simple!

How to add "data" and "paging" section on JSON marshalling

I know i can customize the JSON response registering JSON marshallers to Domain entities, even i can create profiles with names for different responses.
This is done filling an array that later will be marshalled like:
JSON.registerObjectMarshaller(myDomain) {
def returnArray = [:]
returnArray['id'] = it.id
returnArray['name'] = it.name
returnArray['price'] = it.price
return returnArray
}
What i want is to alter the way it gets marshalled to have two sections like
{
"paging": {
"total": 100
},
"data": [
{
"id": 1,
"description": "description 1",
}
},
...
]
}
I assume i have to implemetn a custom JSON Marshaller but i don't know how to use it for a specific response instead of wide application.
EDIT: I assume i'll need a custom RENDERER apart from the marshaller. Is this one that i don't know how to use for specific response.
What about a simple:
def json = new JSON([ paging: [ total: myArray.totalCount ], data: myArray ])
Your domain objects will be converted with the marshaller you have set up while your paging data will simply be transformed into JSON.