I have a nested writable serializer.
class GamerSerializer(serializers.ModelSerializer):
account= AccountSerializer()
document = DocumentSerializer()
class Meta:
model = Gamer
fields = [
'chosen_game',
'gamer_experience',
'account',
'document'
]
This serializers is supposed to create Account object, a Gamer object related to Account as well as Document object related to Gamer.
By default the nested serializer always accepts nested objects as data like this:
serializer = self.get_serializer(data= { account: {...acount}, document: {...document}, chosen_game: "Minecraft", "gamer_experience": "1 year" } )
but I want the serializer to accept normalized flattened data( we assume that names of the model attributes do not overlap). Like this :
serializer = self.get_serializer(data= { account_name: '', account_type:'', document_name: '', document_file: '', chosen_game: "Minecraft", "gamer_experience": "1 year" })
How can I achieve this result ?
Related
I'm starting with DRF and i would like to serialize both ID and Hyperlinked URL at the same time.
Let me define a simple example model:
class Account(model.Models):
name = models.CharField(max_length=100)
active = models.BooleanField()
I know that there is a ModelSerializer which represents the object as follows:
{
"id": 1,
"name": "library",
"active": true
}
And there is as well an HyperlinkedModelSerializer which represents the object as follows:
{
"url": "http://127.0.0.1:8000/core/accounts/1/",
"name": "library",
"active": true
}
Intrinsically in the HyperlinkedModelSerializer we can retrieve the row's ID, but what I'm looking for is to get something like this:
{
"id": 1,
"url": "http://127.0.0.1:8000/core/accounts/1/",
"name": "library",
"active": true
}
I checked the docs, you can explicitly add the field 'id' by including it in fields.
class AccountSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Account
fields = ['url', 'id', 'name', 'active']
I got the answer from here it works well for me.
Doing this you can avoid to define model's fields and after that define them again in the serializer with the id and url fields like ['url', 'id', 'name', 'active']
With the example it seems dummy but this can save you a lot of time when you deal with models which have much more fields ...
class AccountSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Account
fields = [field.name for field in model._meta.fields]
fields.extend(['id', 'url'])
I have this serializer that represents content abstract where I would like to add an hyperlink field that is not in the model, but calculated by the framework linked to the ContentsSerializer.
class ContentsAbstractSerializer(serializers.HyperlinkedModelSerializer):
content_url = ???
class Meta:
model = Contents
fields = ('content_url','content_id','content_title', 'content_abstract','start_date','stop_date','last_date','content_status','version')
class ContentsSerializer(serializers.HyperlinkedModelSerializer):
categories = CategoriesContentsSerializer(read_only=True, many=True)
class Meta:
model = Contents
fields = ('content_id','content_title', 'content_abstract', 'content_body','start_date','stop_date','last_date','content_status','version','sections', 'images','attaches','categories')
I would like to have a result like this:
{
"content_url":"http://mysite/Content/125",
"content_id": 125,
"content_title": "this is the title",
"content_abstract": "This is the abstract",
"start_date": "2005-01-12",
"stop_date": "3000-01-12",
"last_date": "2019-02-27T09:40:38Z",
"content_status": "PUBLISHED",
"version": 0
},
I think that instead of define your own custom field you should use the manually specify the view_name for each nested resource as part of extra_kwargs.
I think that you simply could do something like that:
class ContentsAbstractSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Contents
fields = ('content_url','content_id','content_title', 'content_abstract','start_date','stop_date','last_date','content_status','version')
extra_kwargs = {
'content_url': {'view_name': 'name_of_your_detail_view'},
}
Output:
{
"content_url":"http://mysite/Content/125",
"content_id": 125,
....
},
The Resource: 'HyperlinkedModelSerializer' with custom nested
'view_name's does not work in combination with
'depth'
Oficial Resource: How hyperlinked views are determined
I'm trying to display a 'nested' model in my API response and having trouble shaping the data.
I have the model the API is called from:
something like
class Rules(Model):
conditions = models.ManyToManyField(RulesPoliciesConditions)
...
...
class RulesPoliciesConditions(Model):
rules = models.ForeignKey(Rules, ...)
policies = models.ForeignKey(Policy, ...)
Rules and Policies are their own models with a few TextFields (name, nickname, timestamp, etc)
My problem is that when I use the Rules model to call a field called conditions, only the rules and policies PK display. I want to reach the other attributes like name, timestamp, nickname, etc.
I tried making my fields (in my Serializer) try to call specifically like "conditions__rules__name" but it's invalid, I also tried "conditions.rules.name" which is also invalid. Maybe I'm using the wrong field in my serializer, I'm trying out conditions = serializers.SlugRelatedField(many=True, queryset=q, slug_field="id")
My intention is to display something like:
conditions: [
{
rules: {id: rulesId, name: rulesName, ...},
policies: {id: policiesId, name: policiesName, ...}
}, ...
]
or just even:
conditions: [
{
rules: rulesName,
policies: policiesName
}, ...
]
since right now it just returns the rulesId and policiesId and it doesn't "know" about the other fields
EDIT: I found a relevant question on SO but couldn't get a relevant answer
Django REST Framework: Add field from related object to ModelSerializer
This can be achieved by using nested serializers. The level of nesting can be controlled/customized by various methods
class RulesPoliciesConditionsSerializer(serializers.ModelSerializer):
class Meta:
fields = '__all__'
model = RulesPoliciesConditions
depth = 1
class RulesSerializer(serializers.ModelSerializer):
conditions = RulesPoliciesConditionsSerializer(many=True)
class Meta:
fields = '__all__'
model = Rules
Pass your Rules queryset to the RulesSerializer serializer to
get the desired output
Example
rules_qs = Rules.objects.all()
rules_serializer = RulesSerializer(rules_qs, many=True)
data = rules_serializer.data
References
1. serializer depth
2. Nested serializer
You can use nested serializers for the purpose.
class RuleSerializer(serializers.ModelSerializer):
...
class Meta:
model = Rules(rulesId, rulesName)
fields = ('id', 'email', 'country')
class RulesPoliciesConditionsSerializer(serializers.ModelSerializer):
rules = RuleSerializer()
policies = PolicySerializer()
...
I have a model is pretty straight forward and I've created a property on the model to essentially return child data as JSON. The property is simple:
#property
def question_data(self):
from api.models import TemplateQuestion
questions = TemplateQuestion.objects.filter(template__id=self.template.id)
question_dict = [obj.as_dict() for obj in questions]
return(json.dumps(question_dict, separators=(',', ': ')))
Which does its job and outputs valid JSON. That said I'm at a total loss of how to add that property to the Serializer as JSON and not a string like
{
"questions": "[{\"sequence\": 2,\"next_question\": \"\",\"modified_at\": \"2016-01-27T19:59:07.531872+00:00\",\"id\": \"7b64784e-a41d-4019-ba6e-ed8b31f99480\",\"validators\": []},{\"sequence\": 1,\"next_question\": null,\"modified_at\": \"2016-01-27T19:58:56.587856+00:00\",\"id\": \"99841d91-c459-45ff-9f92-4f75c904fe1e\",\"validators\": []}]"
}
It's stringifying the JSON and which I need as proper JSON.
Serializer is probably too basic but I haven't worked with DRF in a while and have never tried to append JSON to the serialized output.
class BaseSerializer(serializers.ModelSerializer):
class Meta:
abstract = True
class SurveySerializer(BaseSerializer):
included_serializers = {
'landing_page': 'api.serializers.LandingPageSerializer',
'trigger': 'api.serializers.TriggerSerializer',
'template': 'api.serializers.TemplateSerializer'
}
questions = serializers.ReadOnlyField(source='question_data')
class Meta:
model = Survey
fields = ('id',
'name',
'slug',
'template',
'landing_page',
'trigger',
'trigger_active',
'start_date',
'end_date',
'description',
'fatigue_limit',
'url',
'questions',)
meta_fields = ('created_at', 'modified_at')
I'll add that I'm also bolting on the Django Rest Framework JSON API formatting but I think that in the end I'm just not getting how to append JSON to a model's serialization without it being returned as a string.
You should not be dumping the results of the method to JSON. Just return the dict; DRF's serializer will take care of converting it.
I have two ModelSerializers:
class VariantSerializer(serializers.ModelSerializer):
class Meta:
model = Variant
fields = (
'id',
'name',
... and so on)
and
class PictureItemSerializer(serializers.ModelSerializer):
class Meta:
model = PictureItem
fields = (
'id',
... and so forth)
What I want to do is to return a JSON including a single instance of the VariantSerializer, and multiple instances of the PictureItemSerializer.
I created another serializer as such:
class PictureItemUploadSerializer(serializers.Serializer):
variant = VariantSerializer()
pictureItems = PictureItemSerializer(many=True)
But I'm having difficulties in instantiating this "combination" of a serializer if you will.
This does not work:
p = PictureItemUploadSerializer()
p.variant = variant_serializer
p.pictureItems = picture_item_serializer
return Response(p.data, status=status.HTTP_201_CREATED)
as it yields empty data:
{
"variant": {
"name": ""
},
"pictureItems": []
}
You need to pass the data dictionary to the serializer when you are creating an instance of the serializer.
Lets say variant_data contains data for Variant model and picture_items_data contains the list of PictureItem model data.
Create a data dictionary containing them.
data = {'variant': variant_data, 'pictureItems': picture_items_data}
Then pass this data to the serializer.
p = PictureItemUploadSerializer(data=data) # pass the data dictionary
p.is_valid(raise_exception=True) # check if serializer is valid
return Response(p.data, status=status.HTTP_201_CREATED) # return response