Using nested serializer as related field in Django Rest Framework - json

I have two simple serializers:
class ZoneSerializer(serializers.ModelSerializer):
class Meta:
model = Zone
fields = ('id', 'name')
class CitySerializer(serializers.ModelSerializer):
zone = ZoneSerializer(source='zone')
class Meta:
model = City
fields = ('id', 'name', 'zone')
So client-side receives JSON objects like:
{
"id": 11,
"name": "City1",
"zone": {
"id": 2,
"name": "Zone 2"
}
}
Now, when I receive JSON from client-side as...
{
"name": "NewCity",
"zone": {
"id": 2,
"name": "Zone 2"
}
}
... and want to POST (create) it as a new "city", I want my ModelSerializer to know that "zone" in JSON is a foreign key to Zone model, and it shouldn't be inserted to db as a new "zone".
Is there a way to achieve that? Or must I use RelatedField instead, although I want to pass and receive full detailed zone rather than just primary keys?

According to this answer by the author of django-rest-framework, the answer is no, it is not possible ("nested serializers do not currently support write operations") as of January 2013.

Related

how to be able to add records using Http Methods on django

I'm creating a Django rest API authentication system and I want to return data in JSON format,the main purpose of this question is : I want to add or delete users only by adding fields on json format , not in the inputs that've created :
this is my views.py :
# Rendering plant data as json :
def plant_json(request):
data=list(new_Plant.objects.values())
return JsonResponse(data,safe=False)
this is my output ( don't mind the values ) :
[{"id": 5, "name": "dqsd", "adress": "sdq", "type": "PV", "location": "dqd", "list_gateway": "gggdds", "status": "etude"}, {"id": 6, "name": "fdsfds", "adress": "fsdfds", "type": "PV", "location": "fdsfds", "list_gateway": "fdsfs", "status": "etude"}, {"id": 7, "name": "sdqdssd", "adress": "dsdsq", "type": "HYBRID", "location": "dqsdqs", "list_gateway": "dsdqss", "status": "online"}]
normally in order to add a new plant I've created a HTML template that has inputs where the user can add a plant , but what I want is that I can add a plant only by adding fields directly using json format ( using postman for example ) no need for the inputs. Thank you
What you want to use is a CreateAPIView (see docs here). All you need to do is to define the serializer you need and use it in the view.
# serializers.py
from models import Plant
from rest_framework import serializers
class PlantSerialzer(serializers.ModelSerializer):
class Meta:
model = Plant
fields = ('id', 'name', 'address', 'type', 'location', 'list_gateway', 'status')
# views.py
from my_plant_module.serializers import PlantSerializer
from rest_framework.generics import CreateAPIView
class PlantCreateView(CreateAPIView):
queryset = Plant.objects.all()
serializer_class = PlantSerializer

JSON Django Model queryset and it's related models

I have two models on Django 1.9, one has a relationship of FK with the other. I want to generate a JSON of the first model and the second as well, but the queryset only loads the first model.
models.py
class Header(models.Model):
title=models.CharField(max_length=60)
description=models.TextField()
createdBy=models.ForeignKey(User)
def __unicode__(self):
return self.title
class Detail(models.Model):
header=models.ForeignKey(Disaster)
country=models.CharField(max_length=3)
On view.py
def getHeaders(request):
header_rs = Header.objects.all()
response_data = serializers.serialize("json", header_rs)
return HttpResponse(response_data, content_type="application/json")
The return I got:
[
{
"model": "DH_BASE.header",
"pk": 1,
"fields": {
"title": "Teste",
"description": "Teste",
"createdBy": 1
}
}]
I would like the "Detail" to appear in this result as well.
How could I do that?
Thanks
Serialization built into django is flat. That means it will only reference related models. You can use natural keys to overcome this, but this is a bit hackish.
Better approach is to use serializers from Django REST Framework.

Get rid of Mongo $ signs in JSON

I am building python backend for SPA (Angular) using MongoDB.
Here is what I use: Python 3.4, MongoDB 3, Flask, flask-mongoengine and flask-restful
Now I receive the following JSON from my backend:
[
{
"_id": {
"$oid": "55c737029380f82fbf52eec3"
},
"created_at": {
"$date": 1439129906376
},
"desc": "Description.....",
"title": "This is title"
},
etc...
]
And I want to receive something like that:
[
{
"_id": "55c737029380f82fbf52eec3",
"created_at": 1439129906376,
"desc": "Description.....",
"title": "This is title"
},
etc...
]
My code for now:
from flask import json
from vinnie import app
from flask_restful import Resource, Api
from vinnie.models.movie import Movie
api = Api(app)
class Movies(Resource):
def get(self):
movies = json.loads(Movie.objects().all().to_json())
return movies
api.add_resource(Movies, '/movies')
Model:
import datetime
from vinnie import db
class Movie(db.Document):
created_at = db.DateTimeField(default=datetime.datetime.now, required=True)
title = db.StringField(max_length=255, required=True)
desc = db.StringField(required=True)
def __unicode__(self):
return self.title
What is the best way to format convenient JSON for front-end?
If you are confident you want to get rid of all the similar cases, then you can certainly write code that matches that pattern. For example:
info = [
{
"_id": {
"$oid": "55c737029380f82fbf52eec3"
},
"created_at": {
"$date": 1439129906376
},
"desc": "Description.....",
"title": "This is title"
},
#etc...
]
def fix_array(info):
''' Change out dict items in the following case:
- dict value is another dict
- the sub-dictionary only has one entry
- the key in the subdictionary starts with '$'
In this specific case, one level of indirection
is removed, and the dict value is replaced with
the sub-dict value.
'''
for item in info:
for key, value in item.items():
if not isinstance(value, dict) or len(value) != 1:
continue
(subkey, subvalue), = value.items()
if not subkey.startswith('$'):
continue
item[key] = subvalue
fix_array(info)
print(info)
This will return this:
[{'title': 'This is title', 'created_at': 1439129906376, 'desc': 'Description.....', '_id': '55c737029380f82fbf52eec3'}]
Obviously, reformatting that with JSON is trivial.
I found a neat solution to my problem in flask-restful extension which I use.
It provides fields module.
Flask-RESTful provides an easy way to control what data you actually render in your response. With the fields module, you can use whatever objects (ORM models/custom classes/etc.) you want in your resource. fields also lets you format and filter the response so you don’t have to worry about exposing internal data structures.
It’s also very clear when looking at your code what data will be rendered and how it will be formatted.
Example:
from flask_restful import Resource, fields, marshal_with
resource_fields = {
'name': fields.String,
'address': fields.String,
'date_updated': fields.DateTime(dt_format='rfc822'),
}
class Todo(Resource):
#marshal_with(resource_fields, envelope='resource')
def get(self, **kwargs):
return db_get_todo() # Some function that queries the db
Flask-RESTful Output Fields Documentation

django restframework: How to add an array name

Lets say I have a simple django model:
class Snippet(models.Model):
created = models.DateTimeField(auto_now_add=True)
title = models.CharField(max_length=100, blank=True, default='')
When I display info from it as JSON through the django web framework I get this:
[{"id": 1, "title": "hello"}, {"id": 2, "title": "world"}]
How would I add an array title to the generated JSON? Like so:
["books" :{"id": 1, "title": "hello"}, {"id": 2, "title": "world"}]
So your client API requires the JSON to be an object instead of an array (there was a security rationale for it when using the browser built-in javascript parser to parse JSON but I forgot the reason)...
If your client API does not mind the extra fields added by PaginationSerializer, you can do:
class BookSerializer(pagination.BasePaginationSerializer):
results_field = "books"
class BookListView(generics.ListAPIView):
model = Book
pagination_serializer_class = BookSerializer
paginate_by = 9999
This will result:
{
'count': 2,
'next': null,
'previous': null,
'books': [
{"id": 1, "title": "hello"},
{"id": 2, "title": "world"}
]
}
[update]
The security reason for avoiding an array as the JSON root is JSON Hijacking. Basically, a clever hacker could override the array constructor in order to do nasty things. Only relevant if your API are answering GET requests and using cookies for authentication.

Displaying Nested Models in Marionette

I'm sending a nested model as JSON to a Marionette app. It looks something like this:
{
"Course1": [
{
"id": 51,
"title": "Assignment1",
"created_at": "2013-09-01T08:47:37.908+09:00",
"updated_at": "2013-09-09T20:53:00.193+09:00",
},
{
"id": 52,
"title": "Assignment2",
"created_at": "2013-09-01T09:11:40.547+09:00",
"updated_at": "2013-09-09T20:52:37.630+09:00",
}
],
"Course2": [
{
"id": 19,
"title": "Assignment1",
"created_at": "2013-08-08T22:49:26.621+09:00",
"updated_at": "2013-09-09T20:48:20.015+09:00",
},
{
"id": 20,
"title": "Assignment2",
"created_at": "2013-08-08T23:03:58.131+09:00",
"updated_at": "2013-09-09T20:47:53.997+09:00",
}
],
"Course3": [
{
"id": 29,
"title": "Assignment1",
"created_at": "2013-08-18T09:22:32.299+09:00",
"updated_at": "2013-09-09T20:47:32.971+09:00",
},
{
"id": 30,
"title": "Assignment2",
"created_at": "2013-08-18T09:33:16.882+09:00",
"updated_at": "2013-09-09T20:02:08.731+09:00",
}
]
}
I'm wondering if there is some way to display each "course" and the data nested within the courses as a table in a Marionette view. I don't know how many courses I'll be sending to Marionette on any given request.
Is there some way to iterate over the data above (as a collection in the Marionette app) and dynamically make a new CompositeView for each course?
You could use Underscore each function. Something like this:
var data = <your JSON data here>;
_.each(data, function(value, key, list) {
var compView = new <yourCustomCompositeView>({ collection: value });
// render the view in whatever region you need to
});
That would create a new CompositeView for each entry in your JSON. Here's the documenation for that function: http://underscorejs.org/#each
Update
Take a look at http://jsfiddle.net/craigjennings11/D2qAC/ on a way to solve this. It might not be the cleanest, but it gets the job done. Note that I specify a custom region that overloads the open method for the layout, and then call open to attach the views to the region instead of show. show closes the region before calling open again, which would remove your previous view.
I realize now how foolish I was! What I was trying to do is part of the core Backbone.Marionette functionality.
I needed only to nest the List.Assignments CompositeView inside the List.Courses CompositeView.
#ControlPanel.module "AssignmentsApp.List", (List, App, Backbone, Marionette, $, _) ->
class List.Controller extends Marionette.Controller
initialize: ->
# This is a collection of courses with nested assignments
# as described in the original question.
App.request "courses:entities", (courses) =>
#coursesView = #getCoursesView courses
App.mainRegion.show #coursesView
getCoursesView: (courses) ->
new List.Courses
collection: courses
class List.Assignment extends Marionette.ItemView
template: "assignments/list/templates/assignment"
tagName: "li"
class List.Assignments extends Marionette.CompositeView
template: "assignments/list/templates/assignments"
tagName: "li"
itemView: List.Assignment
itemViewContainer: "ul"
# This is the important part...
initialize: ->
# Get the nested assignments for this particular course
assignments = #model.get("assignments")
# At this point assignments is a regular Javascript object.
# For this to work assignments must be a valid Backbone collection
#collection = new Backbone.collection assignments
class List.Courses extends Marionette.CompositeView
template: "assignments/list/templates/courses"
itemView: List.Assignments
itemViewContainer: "ul"