Adding additional data to django rest framework viewset response - python

Curently I have a viewset of an example Warehouse and I want to pass additional 'filter' list to each dictionary that is returned.
My WarehouseViewSet:
class WarehouseViewSet(viewsets.ReadOnlyModelViewSet):
filters = [{'date': 'Date/Time'}]
queryset = Warehouse.objects.all()
serializer_class = WarehouseSerializer
WarehouseSerializer:
class WarehouseSerializer(serializers.ModelSerializer):
class Meta:
model = Warehouse
field = ('name', 'address', 'action_list')
Currently I get an json list response like:
[
{
"id": 1,
"name": "Brameda Warehouse",
"address": "Bergijk"
},
{
"id": 2,
"name": "Amazon",
"address": "UK"
}
]
I would like to get:
[
{
"id": 1,
"name": "Brameda Warehouse",
"address": "Bergijk"
"filters": [
{'date': 'dateTime'}, {'actove': 'ActiveObject'}
]
},
{
"id": 2,
"name": "Amazon",
"address": "UK",
"filters": [
{'date': 'dateTime'}, {'actove': 'ActiveObject'}
]
}
]
I understand that having one filter is enough outside the objects dictionary, but I would like to know how to pass lists inside objects.
Any ideas how I can pass additional lists that would be returned as json object would be appreaciated.

I feel a bit unclear as to what you want, but if you just want to add some read-only computed field to the output, you can use SerializerMethodField:
class WarehouseSerializer(serializers.ModelSerializer):
# your other declared fields here
filters = serializers.SerializerMethodField()
# your Meta options here
def get_filters(self, obj):
return ['some', 'stuff', 'here', {'and': 'more'}]
The method has to be named get_field_name (there is an option to change it but I don't really see any point using it).
You get the instance being serialized as obj.
You may return anything that is made of regular types (numbers, strings, dicts, lists, tuples, booleans, None).
If the data has to come from outside, you should have the caller add it to the context, and it will be available on self.context['foobar'].

Related

marshmallow - choose nested schema based on field value and handle fields 'starting with'

I am parsing a file and end up with a dictionary like this:
user_data = {
"title": "TestA",
"sects": [
{
"type": "cmd",
"cmd0": {
"moveX": 12,
"moveY": 34,
"action": "fire"
},
"session": 9999,
"cmd1": {
"moveX": 56,
"moveY": 78,
"action": "stop"
},
"endType": 0,
},
{
"type": "addUsers",
"user1": {
"name": "John",
"city": "London"
},
"user2": {
"name": "Mary",
"city": "New York"
},
post = "yes"
}
]
}
I am attempting to validate it using marshmallow but not sure how to handle these two things:
With sects the content of each nested dict is dependent on the type (cmd, addUser, etc). Is there a way to pick a schema based on the value of a field?
Is there such a thing as field 'startsWith' to handle the fact that I may have cmd0, cmd1...cmdN?
So, something like the following:
class CmdSchema(schema):
type = fields.Str()
cmdN = fields.Dict(...) # Allow cmd1, cmd2, etc
session = fields.Int()
endType = fields.Int()
class AddUsersSchema(schema):
type = fields.Str()
userN = fields.Dict(...) # Allow user1, user2, etc
post = fields.Str()
class ParsedFileSchema(schema):
title = fields.Str()
sects = fields.Nested(...) # Choose nested schema based on sects[i]['type']?
Note: I cannot change the format/content of the file I'm parsing and order matters (so I need to retain the order of cmd1, cmd2, etc so they can be processed in the correct order).
1/ What you want to achieve is polymorphism. You may want to check out marshmallow-oneofschema.
2/ This does not exist to my knowledge. It shouldn't be too hard to create as a custom validator, though. It might even make it to the core.

Django rest - convert field into a list?

I have a Textfield in my model which stores dictionaries, I would like to convert this field into a dictionary if possible in a rest serializer.
at the moment the field returns the dictionary but backslashes all the quotes, it is possible to convert the strings into a nested list of dicts?
Thanks
api currently returns below:
{
"id": 3,
"hostname": "WAN-EDGE",
"timestamp": "2019-04-12T11:34:36.654521",
"routing_table": "[{\"route\": \"0.0.0.0\", \"subnet_mask\": \"0.0.0.0\", \"next_hop\": \"172.16.66.193\"}, {\"route\": \"10.10.21.0\", \"subnet_mask\": \"255.255.255.0\", \"next_hop\": \"172.16.67.146\"}, {\"route\": \"10.22.0.0\", \"subnet_mask\": \"255.255.0.0\", \"next_hop\": \"172.18.1.5\"}, {\"route\": \"10.31.0.0\", \"subnet_mask\": \"255.255.0.0\", \"next_hop\": \"172.16.67.146\"},...]"
},...
}
desired result a nested list of dicts
{
"id": 3,
"hostname": "WAN-EDGE",
"timestamp": "2019-04-12T11:34:36.654521",
"routing_table": [
{
"route": "0.0.0.0",
"subnet_mask": "0.0.0.0",
"next_hop": "172.16.66.193"
},
{
"route": "10.10.21.0",
"subnet_mask": "255.255.255.0",
"next_hop": "172.16.67.146"
},
{
"route": "10.22.0.0",
"subnet_mask": "255.255.0.0",
"next_hop": "172.18.1.5"
},
{
"route": "10.31.0.0",
"subnet_mask": "255.255.0.0",
"next_hop": "172.16.67.146"
},...
]
},...
}
Current serialiser:
class RoutingTableSerializer(serializers.ModelSerializer):
hostname = serializers.ReadOnlyField(
source='device.hostname',
)
rt = serializers.JSONField(
source='routing_table'
)
class Meta:
model = DeviceData
fields = ('id','hostname','timestamp','rt')
You may need serializers.JSONField()
Update-1
You could also try with SerializerMethodField() as
import json
class RoutingTableSerializer(serializers.ModelSerializer):
hostname = serializers.ReadOnlyField(source='device.hostname', )
rt = serializers.SerializerMethodField(source='routing_table', read_only=True)
def get_routing_table(self, instance):
return json.loads(instance.routing_table)
class Meta:
model = DeviceData
fields = ('id', 'hostname', 'timestamp', 'rt')

DRF3 M2M nested serializer removing nested 'id' fields on update()

I am trying to create a full-featured M2M (through table) nested serializer, which works perfect on create(). However, when I take the JSON returned by the GET version of serializer which contains the id's of the nested records and perform a PUT against the same serializer, the 'id' fields are removed from the nested record validated_data by the time it gets to the update() method.
{
"id": 1,
"addresses": [
{
"id": 1, # This is ripped out
"city": "Oakville",
"addr": "13 Main St",
"postal_code": "01101"
},
{
"id": 2, # This is ripped out
"city": "Watertown",
"addr": "88 Main St",
"postal_code": "01101"
},
"customer_number": 1234,
"customer_type": 1,
"pricing_sequence": 2,
"name": "Customer number 1234"
}
Any ideas?
Yes this is a duplicate of django-rest-framework: serializer from DATA don't update model ID
I think I figured it out. It seems the ModelSerializer by default makes the 'id' field read-only. Solution is to add an explicit 'id' field to the serializer. See tomchristie's comment https://github.com/tomchristie/django-rest-framework/issues/2114

Mongokit validate dict inside list

How can I validate that my desc field is required and my category field is optional?
class Mydoc(Document):
structure = {
"name": unicode,
"items": [{
"category": int,
"desc": unicode
}]
}
required_fields = ["name", "items", "items.desc"] # Error: items has no attribute
# desc, it is a list not a dict.
How can I validate the categories inside the list?
UPDATE
https://groups.google.com/forum/?fromgroups=#!topic/mongokit/GP5AgaMG6T4
The tricky point here is that we do not know how many items there are. Mongokit doesn't allow you to specify a nested object as required because it could potentially be very slow if you have many items.
So, in short, mongokit doesn't allow required_fields and default_values in nested objects.
However, Mongokit is very light and can be customized very easily if needed:
class MyDoc(Document):
structure = {
"name": unicode,
"items": [{
"category": int,
"desc": unicode
}]
}
def validate(self, *args, **kwargs):
super(MyDoc, self).validate(*args, **kwars)
for item in self["items"]:
assert item["desc"], "desc is required: %s" % item

django tastypie get related column total amount

Given the below JSON result from tastypie, I would like to create a new value at check.payments_total which is equal to the total amount of the payments (in this case, 44.00). Any clue on how to do this? I'm completely stumped. payments is a joined foreign key to the check table.
{
"objects": [
{
"check": {
"id": "58a81b36-1ea6-403b-9902-a50cbd13cf2e",
"number": 2,
"payments": [
{
"amount": "5.00",
},
{
"amount": "39.00",
}
]
}
}
]
}
If for the response, then you could override the following method in your resource (the snippet is from tastypie.resources.Resource):
def alter_list_data_to_serialize(self, request, data):
"""
A hook to alter list data just before it gets serialized & sent to the user.
Useful for restructuring/renaming aspects of the what's going to be
sent.
Should accommodate for a list of objects, generally also including
meta data.
"""
return data
just include something like (not tested, consider to be pseudo-code):
total_amount = 0.0
for object in data[ 'objects' ]:
total_amount += object[ 'amount' ]
return { 'objects' : data[ 'objects' ], 'total_amount' : total_amount }
and you should be done.

Categories