Django Rest Framework Dictionary Field - django

I'm using Mongodb with mongoengine as a backend for a API in Django.
The framework I'm using to create the api is Django Rest Framework.
I need to store a dictionary in a field in Mongo and the best I've done when the method post is called is to use a charfield and parse the dictionary in the function restore_object.
There is a better way to achieve this goal?
It's better to create a dict field? I don't know how hard this could be.
Thank you.
edited to show some code, notice that I store the dictionary as a dict (DictField) and it's content could change from one object to other.
my mongoengine model is something like:
class MyDoc(mongoengine.Document):
name = mongoengine.StringField(max_length=200)
context = mongoengine.DictField()
and my serializer something like:
class MyDocSerializer(serializers.Serializer):
name = serializers.CharField(max_length=200)
context = serializers.CharField()
url = serializers.HyperlinkedIdentityField(
view_name="drf:mydoc-detail",)
def __init__(self,*args,**kwargs):
super(MyDocSerializer,self).__init__(*args,**kwargs)
def restore_object(self, attrs, instance=None):
# Parse string to dict
# this is so ugly, notice I had to repace ' for " to
# avoid an error parsing the json
context = JSONParser().parse(
StringIO.StringIO(
attrs['context'].replace("'","\"")
)
)
attrs['context'] = context
if instance is not None:
instance.name = attrs['name']
instance.context = context
return instance
return MyDoc(**attrs)

Rather than deal with the dictionary field in the Serializer's restore_object, you'll probably end up with something slightly cleaner, if instead you use a custom field for the dictionary field, that manages converting between the dictionary representation and internal char based storage.
You'll want to subclass serializers.WritableField and override the to_native() and from_native methods.
Relevant docs here.
Note: WritableField class that was present in version 2.x no longer exists. You should subclass Field and override to_internal_value() if the field supports data input.
Update: As of 3.0.4 you can now use serializers.DictField... http://www.django-rest-framework.org/api-guide/fields/#dictfield

Related

How can I pass dictionary in Django Rest Framework?

In django, I was able to pass data using dictionary. Like I set the objects in my dictionary and pass it in return render and call the object in frontend (return render(request, 'c.html', context) right? so How can I do this in django rest?
You may return Response in rest framework like this if you are using django rest framework.
context = {'key':'value'}
return Response(context)
Or if you are using a serializer then
return Response(serializer.data)
In Django REST Framework the concept of Serializing is to convert DB data to a datatype that can be used by javascript. Every serializer comes with some field that is going to be processed. For example, if you have a class with the name Employee and its fields as Employee_id, Employee_name, is_admin, etc. Then, you would need AutoField, CharField, and BooleanField for storing and manipulating data through Django. Similarly, serializer also works with the same principle and has fields that are used to create a serializer.
DictField is basically a dictionary field that validates the input against a dictionary of objects. It has the following arguments:
child and allow_empty like this>>>
field_name = serializers.DictField(*args, **kwargs)
for example document = DictField(child=CharField())
you can use serializer like below>>>
from rest_framework import serializer
class Any(object):
def __init__(self, dictonary):
self.dict = dictionary
class AnySerializer(serializers.Serializer):
dictionary = serializers.DictField(
child = serializers.CharField())
you can visit similar problem for understanding through the real problem.
And this link is the complete documentation of your problem. You can check this out.

How to replace foreign key by its associated value in django queryset

I'm fairly new to django and i would need your help!
I wrote an api/route view that query the database and return a JSON to my fetch function in my javascript.
Is there a way to query the database and got back a queryset with foreign key replaced by its associated value ?
I though i could use ModelName.objects.select_related( 'field ') but i didn't understand how it works.
If you have a solution or advices on better way to achieve the goal, Thanks in advance!
Context in pseudo-code:
// HTML //
Button onclick function get_list_of_data
// JS //
function get_list_of_data:
Fetch URL of route django
Convert response to JSON
iterating through JSON to fill HTLM div
// Django //
use ModelName.objects.filter( name = name ) to get list of data in Queryset
use serializers.serialize(json, ...") to get JSON from Queryset
return JsonResponse (json_query)
If I understood the problem well, when you serialize a model that has a ForeignKey field defined you get only the id value in JSON response but you would like to get the whole object (not only a number) returned.
The way to do that is to specifically write serializer for that ForeignKey model and then use it within the serializer od the model that you are trying to fetch.
You haven't provided any code, but here is some example that might help you:
class SecondModelSerializer(serializers.ModelSerializer):
class Meta:
model = SecondModel
fields = '__all__'
class FirstModelSerializer(serializers.ModelSerializer):
foreign_key_field = SecondModelSerializer()
class Meta:
model = FirstModel
fields = ('id', 'foreign_key_field', 'field1', 'field2')
Here in your FirstModelSerializer you specifically told Django to use SecondModelSerializer for your ForeignKey field (I named it foreign_key_field). This way Django will know how to serialize that field instead of returning only the id value.

How to modify Django Rest Framework incoming request?

I am building a web app using Django that is pretty much only serving as the API server. I have a single-page application that connects to it as well as an Android client. I have a need to modify some of the incoming POST requests that are coming through.
My two use cases:
If during the registration process the user does not select an avatar image to upload (which is a simple TextField that is the URL to the image), I should be able to insert the default avatar URL. So something like if request.data["avatar"] is None: <use default>
The incoming "timestamp" requests from the Android client are all unix timestamps. I would like to convert this to Django's datetime on the fly - so, current request comes in with date_time = 1473387225, I'd like to convert that to a DateTime object.
Now, I'm already doing something similar for certain POST parameters. The way I do it right now is in the post() function of my generic ListCreateApiView I would directly modify the request object and then call the self.create() with that new request object. Is this the right way, or is there a much better way to do it?
Thanks!
If you are using django-rest-framework these things can be done by serializers.
For avatar use an URLField with default value.
For the timestamp you should probably create a custom field.
Check out this site: http://www.cdrf.co It is an easily navigable display of all the methods available on a given class. You can simply use this to overwrite the View you are using. If a model ViewSet, you likely want perform_create and perform_update.
I often do something like this:
class SomeViewSet(viewsets.ModelViewSet):
queryset = SomeModel.objects.all()
serializer_class = SomeModelSerializer
def perform_create(self, serializer):
data = self.request.data
# make some changes to self.request here
serializer.save(
#change some things here
field='some new value'
)
You can do this in a number of ways. As a part of your validation or in the to_internal_value of the request serializer or in a custom field serializer.
Heres an example of doing this as a part of a custom field serializer.
class AccountCreationSerializer(serializers.Serializer):
avatar = AvatarField(
required=False
allow_files=True
)
# Custom Field Serializer
class AvatarField(serializers.FilePathField):
def to_internal_value(self, value):
user_defined_path = super(AvatarField, self).to_internal_value(value)
if user_defined_path:
return user_defined_path
return default_path

Changing the field name when using Django rest framework

I'm feeding serialized data from the Django rest framework to a Javascript pivot table on my site. If I have a variable called 'created_on', the DRF uses that as the field name. What I want to display in my pivot table is the label which will be converted to 'Created On'.
As an example, my output from DRF is the following:
[{"created_on": "2016-04-23"}, {"created_on": "2016-05-23"}]
What I want is:
[{"Created on": "2016-04-23"}, {"Created on": "2016-05-23"}]
Is this possible without me overriding the serialization process?
No, its not possible (currently) without overriding the serialization process.
Why it is not possible?
This is because the alternate name which you want to use for created_on contains whitespace and its not possible to define a field in your serializer having whitespaces in it. Also, there is no functionality currently to provide alternate name for a field to be used in serialization process.
Possible Solution:
You can override the to_representation() method of your serializer and there add a Created On key having value equal to the value of created_on key. Then all the serialized objects will contain a key Created On.
class MySerializer(serializers.ModelSerializer):
...
def to_representation(self, obj):
primitive_repr = super(MySerializer, self).to_representation(obj)
primitive_repr['Created On'] = primitive_repr['created_on']
return primitive_repr
What if the alternate name did not contain any whitespaces?
Had the alternate name did not contain any spaces between them, you could then have used SerializerMethodField() with source argument.
You could have done something like:
class MySerializer(serializers.ModelSerializer):
alternate_name = serializers.SerializerMethodField(source='created_on')
class Meta:
model = MyModel
fields = (.., 'alternate_name')

Django (JSONField) and tastypie

I have a table into mysql that is the type TextField (django) by using the JSONField.
This is how my model looks
from django.db import models
from json_field import JSONField
class Model(models.Model):
obj = JSONField()
The value I send via tastypie is
json_string = '{"data":"value"}'
Into the database I can see
{"data":"value"}
But when retrive the data with curl I get something like this
"{u'data': u'value'}"
What I can do to not have the python u'field' representation into the tastypie's output ?
thanks!
I fixed this issue like so:
def dehydrate_user_inputs(self, bundle):
requirement = Requirement.objects.get(pk = bundle.obj.pk)
user_inputs = json.dumps(requirement.user_inputs)
return user_inputs
My JSONField is named user_inputs. Requirement is the model that it belongs to.
I feel weird doing a Query here when Tastypie has already done so for me, but, this works. I'd love if there are better solutions.
The error you're seeing is caused by Tastypie treating the JSONField like a TextArea and calling str() on the object JSONField returns before returning it to the caller.
Another approach is to use fields.ApiFields for the JSONField. This works because fields.ApiFields does not perform any conversions on either the way in (hydrate()) or way out (convert()). This is exactly what we want - the underlying JSONField will convert the JSON object to a string for persistence on the way in and recreate the object from the string on the way out. Thus, tastypie does not need to do anything. My code looks a bit like this (class/variable names based on OP's example) -
class JSONField(fields.apiField):
""" Wrapper over fields.apiField to make what we're doing here clear """
pass
class MyModelResource(ModelResource):
obj = JSONField('obj')
Use DictField:
obj = fields.DictField(attribute='obj')
I was running into similar problems where my unicode strings would be returned in a weird format in the API (I think the raw encoded strings were returned as opposed to the actual utf-8 characters).
Anyway instead of using the dehydrate method and re-doing the query, you are better off with a custom serializer in your resources.
This is what I used:
class JSONSerializer(Serializer):
'''using the standard json library for better unicode support,
also note django.utils.simplejson, used in the standard Tastypie serializer,
is set for depreciation'''
def to_json(self, data, options=None):
options = options or {}
data = self.to_simple(data, options)
return json.dumps(data)
then in your resources:
class PlaceResource(ModelResource):
class Meta:
queryset = Place.objects.all()
resource_name = 'place'
serializer = JSONSerializer()