a third-party application I am using is storing json in a textfield.
I would like to serialize this data to json, and I only need to be able to read from this serializer, not write to it. I don't want to have to manipulate the data on the frontend, so I want it to come out at clean json from my api.
class SomeSerializer(serializers.ModelSerializer):
details = serializers.CharField()
class Meta:
model = SomeModel
fields = ( 'id', 'details')
right now this is returning:
[{"id":"someID",
"details":"{\"address\": {\"city\": null}"}"}]
I can't figure out how to use json.loads in a serializer, which would seem the cleanest option.
You can use SerializerMethodField.
import json
from rest_framework import serializers
class SomeSerializer(serializers.ModelSerializer):
details = serializers.SerializerMethodField()
class Meta:
model = SomeModel
fields = ('id', 'details')
def get_details(self, obj):
return json.loads(obj.details)
Note that SerializerMethodField is read_only, cannot for write.
Related
For what purpose is the class Meta: used in the class inside the Django serializers.py file?
Serializer classes can also include reusable validators that are
applied to the complete set of field data. These validators are
included by declaring them on an inner Meta class. Also when
you are defining a serializer then meta tags will help the serializer
to bind that object in the specified format
Below are some of the examples :
While validating request data in specified format:
class EventSerializer(serializers.Serializer):
name = serializers.CharField()
room_number = serializers.IntegerField(choices=[101, 102, 103, 201])
date = serializers.DateField()
class Meta:
# Each room only has one event per day.
validators = UniqueTogetherValidator(
queryset=Event.objects.all(),
fields=['room_number', 'date']
)
While getting data from db
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = ['id', 'account_name', 'users', 'created']
More you can read here
The Meta class can be used to define various things about the model such as the permissions, database name, singular and plural names
I want to serialize the data
class Main(models.Model):
title = models.CharField()
class ForeignKey(models.Model):
main = models.ForeignKey(Main,on_related='foreign_key')
I want to get the result like
{'title':'hello', 'foreign_key':'['foreign_key1','foreign_key2','foreign_key3']'
I first thought I could make it work in the 'views.py', but some documents told me that there is a thing like 'listField' in serializer, which has too little examples..
What would be the way here?
Write your serializer as ,
#serializers.py
from rest_framework import serializers
class ForeignKeySerializer(serializers.ModelSerializer):
class Meta:
model = ForeignKey
fields = '__all__'
class MainSerializer(serializers.ModelSerializer):
foreign_key = ForeignKeySerializer(source='foreignkey_set', many=True)
class Meta:
model = Main
fields = ("title", "foreign_key")
then write your views as,
#views.py
from rest_framework.decorators import api_view
from rest_framework.response import Response
#api_view()
def sample_view(request):
queryset = Main.objects.all()
serializer = MainSerializer(queryset, many=True)
return Response(data=serializer.data)
References
DRF-Serializer
DRF-views
I am trying to post to my API with foreign key relationships. It's throwing me back an error saying it's expecting a dictionary as opposed to int for character, character_opponent and stage. This is because the way my models are set up. They have foreign key relationships. The model in question looks like this:
import uuid
from django.db import models
from django.utils import timezone
from analysis.models import Analysis
from characters.models import Character
from stages.models import Stage
class Match(models.Model):
analysis = models.ForeignKey(Analysis, on_delete=models.CASCADE)
character = models.ForeignKey(Character, on_delete=models.CASCADE, related_name='character')
character_won = models.BooleanField()
character_opponent = models.ForeignKey(Character, on_delete=models.CASCADE, related_name='character_opponent')
character_opponent_won = models.BooleanField()
created_at = models.DateTimeField(editable=False)
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
updated_at = models.DateTimeField(editable=False)
stage = models.ForeignKey(Stage, on_delete=models.CASCADE)
def __str__(self):
return '%s vs. %s on %s' % (self.character, self.character_opponent, self.stage)
def save(self, *args, **kwargs):
''' On save, update timestamps '''
if not self.created_at:
self.created_at = timezone.now()
self.updated_at = timezone.now()
return super(Match, self).save(*args, **kwargs)
class Meta:
db_table = "matches"
And here is my serializer:
from rest_framework import serializers
from matches.models import Match
from characters.serializers import CharacterSerializer
from stages.serializers import StageSerializer
class MatchSerializer(serializers.ModelSerializer):
character = CharacterSerializer()
character_opponent = CharacterSerializer()
stage = StageSerializer()
class Meta:
model = Match
fields = ('id', 'analysis', 'character', 'character_won', 'character_opponent', 'character_opponent_won', 'stage')
Is there some option I am missing here to be able to post properly? Clearly I shouldn't have to pass the entire character object each time I want to post something, right? I should just be able to pass the primary key.
From your few comments I understood that you need nested serializer in GET method. What I suggest is, use two[or more] serializers for your API class.
Assuming you are using ModelViewSet API class is using,then you could override get_serializer_class() method as below,
from rest_framework.viewsets import ModelViewSet
class MatchAPI(ModelViewSet):
queryset = Match.objects.all()
def get_serializer_class(self):
if self.action == 'create':
return MatchCreateSerializer
return MatchSerializer
And your MatchCreateSerializer will be like this,
class MatchCreateSerializer(serializers.ModelSerializer):
class Meta:
fields = '__all__'
model = Match
Thus, you only need to provide the PKs of analysis,character etc while creation of Match instance
It will come down to your CharacterSerializer and StageSerializer. If you want to input 1 format (using serialisers.PrimaryKeyRelatedField()), but output another (CharacterSerializer, StageSerializer), you might be best served using 2 serialisers and switching in your view.
In your view you can override get_serializer_class and check your request method, or in the case of a viewset you can check the method being invoked.
When you declare a serializer related field using another serializer, like this
character = CharacterSerializer()
you are telling django-rest-framework that you want a nested serializer. What you want is something like this instead
character = serializers.PrimaryKeyRelatedField()
or you can actually just leave the explicit field declaration out of the serializer (since this is the default), see the doc on serializer relations.
I have the following model:
def get_file_path(instance, filename):
return 'files/{0}/{1}'.format(instance.user.username, filename)
class File(models.Model):
fid = models.UUIDField(primary_key=True, default=uuid.uuid4)
user = models.ForeignKey(settings.AUTH_USER_MODEL)
file = models.FileField(upload_to=get_file_path)
when I try to serialize this model using for example serializers.serialize('json', File.objects.all()) I get:
u'[{"model": "app_name.file", "pk": "45f616b4-d028-488e-83c5-878ce1028018", "fields": {"user": 1, "file": "files/username/filename.txt"}}]'
I want the serialized json to also include the file field's url which file_obj.file.url returns so I added a property field like so:
class File(models.Model):
fid = models.UUIDField(primary_key=True, default=uuid.uuid4)
user = models.ForeignKey(settings.AUTH_USER_MODEL)
file = models.FileField(upload_to=get_file_path)
def _get_file_url(self):
"Returns file's url"
return self.file.url
url = property(_get_file_url)
However property field url is still not serialized. So I tried to write a custom serializer for the model by extending serializers.ModelSerializer like so:
class FileSerializer(serializers.ModelSerializer):
class Meta:
model = File
fields = ('fid', 'user', 'file', 'url')
which doesn't work and I get 'module' object has no attribute 'ModelSerializer'. is it because django doesn't have ModelSerializer class and only django rest_framwork does ? if so how do I solve this if I am using django only and not django rest framework ?
Thanks.
when not using DRF - what is a poor idea...
you have to build manually in your view the dict with the data you wish to return and then use from django.http.response import JsonResponse for serializing the result.
def single_object_representation(obj):
return {
'fid': obj.fid,
'user': obj.user.username,
'url': obj._get_file_url()
}
def demo_view(request):
objects_to_serialize = File.objects.all()
result = [single_object_representation(obj) for obj in objects_to_serialize]
return JsonResponse(result, safe=False)
using django.core.serializers
see here: https://docs.djangoproject.com/en/2.0/topics/serialization/#subset-of-fields
but make sure it will work with your property. For some fields like User you should use duble underscore notation __ e.g user__username
otherwise (with DRF) - what I strongly recommend to you
First of all install drf:
pip install djangorestframework
Then if there is still problem with your serializer give a try to the SerializerMethodField (if in doubt see the docs: http://www.django-rest-framework.org/api-guide/fields/#serializermethodfield)
from rest_framework import serializers
class FileSerializer(serializers.ModelSerializer):
url = serializers.SerializerMethodField()
class Meta:
model = File
fields = ('fid', 'user', 'file', 'url')
def get_url(self):
return self. _get_file_url()
I would like to serialize a polymorphic model but only his base type fields are serialized, not those of the polymorphic.
models.py
class Folder(PolymorphicMPTTModel):
parent = PolymorphicTreeForeignKey('self', null=True, blank=True, related_name='children')
name = models.CharField(max_length=50)
class File(Folder):
srs_wkt = models.CharField(max_length=1000, blank=True, null=True)
views.py
from django.core import serializers
from django.core.serializers.json import DjangoJSONEncoder
file = File.objects.get(pk=1)
serialized = serializers.serialize('python', [file,])
response = json.dumps({'file':file}, cls=DjanJSONEncode)
return HttpResponse(response, content_type="application/json")
That's how I usually do to serialize my model object and send it as JSON, but here, the JSON object has only the srs_wkt field, not the name.
Is there a way to serialize polymorphic model?
the reason is that Folder is not an abstract model, so you have:
https://docs.djangoproject.com/en/dev/topics/db/models/#multi-table-inheritance
in most places Django hides the underlying OneToOneField that binds the two models together, but the serializer does not, see here:
https://docs.djangoproject.com/en/dev/topics/serialization/#inherited-models
They provide in the docs above a recipe for your situation, but it's not very elegant so I'd suggest trying an alternative such as:
from django.core.serializers.json import DjangoJSONEncoder
def myview(request):
file_dict = File.objects.filter(pk=1).values()[0]
folder_dict = Folder.objects.filter(pk=file.folder.pk).values()[0]
folder_dict.update(file_dict)
response = json.dumps({'file': folder_dict}, cls=DjangoJSONEncoder)
return HttpResponse(response, content_type="application/json")