JSON Django Model queryset and it's related models - json

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.

Related

Serialize models like with .values(), but also include ManyToMany related field

class Book(models.Model):
user = models.ForeignKey(Profile, on_delete=models.CASCADE)
tags = models.ManyToManyField(Tag, related_name="books")
I'm have pre-existing JavaScript code that works on input that I feed from django like this:
json.dumps(list(Book.objects.all().values()), cls=DjangoJSONEncoder)
list(Book.objects.all().values()) gives me an array of dictionaries upon which my entire frontend code is based on.
[{ "user": 1 }, { ... }, ... ]
However, now I've added the tags property. I was expecting to find the tags property in my dictionary but there's not: apparently, Django doesn't serialize ManyToMany managers by default.
The offered solution is this:
from django.core import serializers
serializers.serialize("json", Book.objects.all())
that, however, outputs a dictionary that it's in a completely different form, where all my model properties are inside of a fields parameter.
[{"model": "Main.book", "pk": 1, "fields": { "user": ... }]
How can I have a tags field like with serializers.serialize while maintaining the .values() form? Do I have to re-write my frontend code entirely to use the format of serializers.serialize or is there a simpler solution?

How to write DRF serializer to handle JSON wrappers enveloping data fields defined in my models?

I am writing a REST API using Django Rest Framework and need to know how to write a serializer to handle this json request
{
"user_form": {
"fields": [
{"email": "tom.finet#hotmail.co.uk"},
{"password": "password"},
{"profile": {
"username": "Tom Finet",
"bio": "I like running, a lot.",
"location": "Switzerland"
}}
]
}
}
Models exist for both User and Profile objects, therefore I am using a ModelSerializer to make serialization easier. However, the relevant user and profile data is wrapped in a user_form and fields envelop. Therefore, when I make a POST request to create a user the server spits back status code 400 with a BadRequest.
Here are the User and Profile serializers
class ProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = '__all__'
class UserSerializer(serializers.ModelSerializer):
profile = ProfileSerializer()
class Meta:
model = User
fields = ('email', 'password', 'profile')
def create(self, validated_data):
email_data = validated_data.pop('email')
password_data = validated_data.pop('password')
created, user = User.objects.get_or_create_user(
email=email_data,
password=password_data
)
return user
Here is the api create view:
def create(self, request):
user_serializer = UserSerializer(data=request.data)
if user_serializer.is_valid(raise_exception=True):
user_serializer.save()
return Response(
user_serializer.data,
status=status.HTTP_201_CREATED
)
What I want to happen is for the serializers to create a user from the json request specified, but I am unaware of how to go about handling the envelops wrapping the user and profile data.
Following from my comment,
Consider modifying your post payload (client side) as follows:
{
"email": "tom.finet#hotmail.co.uk",
"password": "password",
"profile": {
"username": "Tom Finet",
"bio": "I like running, a lot.",
"location": "Switzerland"
}
}
Following this your current serializer classes should suffice.
If it's not possible to change your post payload on the client, consider extrapolating it using the following comprehension to instantiate your serializer manually within your view:
serializer = UserSerializer(data={
k: v
for d in request.data.get('user_form').get('fields')
for k, v in d.items()
})
if not serializer.is_valid():
# handle invalid serializer error
pass
# save the new model
serializer.save()

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

Using nested serializer as related field in Django Rest Framework

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.

Bootstrap Backbone.js models from django model with TastyPie

I am using Django + TastyPie + Backbone.js. I am trying to figure out how to bootstrap the initial data for the models on the first request, instead of fetching them after the initial page load as recommended here: http://backbonejs.org/#FAQ-bootstrap
There are two issues--one is trying to load a single Django model and the other is trying to serialize a queryset.
As an example, I have a variable called user in the context which is a Django User and represents the current user who is logged in.
I do not want to do this:
var curUser = new App.Models.User({id: 1});
curUser.fetch();
and cause another request to the server, since I already have the user model loaded.
I want to boostrap this data into a Backbone model like:
var curUser = new App.Models.User({{user|json}});
(similar to How to load bootstrapped models of backbone in Django, but I do not want to do a special case on each view converting everything to json)
where I have created a custom template filter to convert to json
def json(object):
"""Return json string for object or queryset.
"""
if isinstance(object, QuerySet):
return mark_safe(serialize('json', object))
if isinstance(object, Model):
object = object.to_dict()
return mark_safe(simplejson.dumps(object))
register.filter('json', json)
The issue is if I seralize a django model, I get something that looks like this:
[{"pk": 1, "model": "auth.user", "fields": {"username": "user#gmail.com", "first_name": "Jeremy", "last_name": "Keeshin", "is_active": true, "is_superuser": false, "is_staff": false, "last_login": "2013-07-15T22:31:02", "groups": [], "user_permissions": [], "password": "passwordhash", "email": "user#gmail.com", "date_joined": "2012-06-14T00:59:18"}}]'
What I really want is the json representation to match up with the api that I've defined using TastyPie:
class UserResource(ModelResource):
"""A resource for the User model."""
class Meta:
queryset = User.objects.all()
resource_name = 'user'
authorization = Authorization()
fields = ['username', 'first_name', 'last_name', 'id']
filtering = {
'username': ALL,
'email': ALL,
'first_name': ALL,
'last_name': ALL
}
Here I only get a few fields passed back, not all of them when I serialize the model. I know Django lets you serialize only certain fields, but those fields are set on a per model basis, and I wouldn't want to include that call on every view.
I see a similar answer here Django Serialize Queryset to JSON to construct RESTful response with only field information and id, but this requires writing this call on every view.
My current solution is adding a method to_dict monkeypatched onto the User model
def to_dict(self):
"""Return a subset of fields as a dictionary."""
return {
'id': self.id,
'first_name': self.first_name,
'last_name': self.last_name,
'email': self.email
}
because this is easy to serialize. Additionally a django model cannot be serialized (not in a list of one) by itself, only querysets can be serialized.
I imagine lots of people are figuring out good ways to bootstrap their django model data into backbone models upon the initial page load (especially when working with TastyPie), but I haven't figured out a reasonable way to do this.
With querysets, there is additionally much more django info passed along, and I am trying to figure out a way to have the serialization match the output from the TastyPie api.
Does anyone have best practices to boostrap django models and querysets into backbone models?
Update:
I have changed my json filter to use TastyPie bundles like
from core.api import UserResource
from core.api import UserProfileResource
from tastypie.serializers import Serializer
def json(object):
"""Return json string for object or queryset."""
TYPE_TO_RESOURCE = {
'User': UserResource,
'UserProfile': UserProfileResource
}
Resource = TYPE_TO_RESOURCE[object.__class__.__name__]
r = Resource()
bundle = r.build_bundle(object)
r.full_dehydrate(bundle)
s = Serializer()
return mark_safe(s.serialize(bundle, 'application/json'))
Source: http://django-tastypie.readthedocs.org/en/latest/cookbook.html#using-your-resource-in-regular-views
This seems to be closer, work with single models, stay DRY with which fields I want that I have listed in TastyPie, but does not handle multiple models yet. I'm also not sure if it is problematic to have this in a filter.
Update
Also use this: https://gist.github.com/1568294/4d4007edfd98ef2536db3e02c1552fd59f059ad8
def json(object):
"""Return json string for object or queryset."""
TYPE_TO_RESOURCE = {
'User': UserResource,
'UserProfile': UserProfileResource,
}
if isinstance(object, QuerySet):
Resource = TYPE_TO_RESOURCE[object[0].__class__.__name__]
r = Resource()
bundles = [r.build_bundle(model) for model in object]
bundle = [r.full_dehydrate(b) for b in bundles]
elif isinstance(object, Model):
Resource = TYPE_TO_RESOURCE[object.__class__.__name__]
r = Resource()
bundle = r.build_bundle(object)
r.full_dehydrate(bundle)
else:
mark_safe(simplejson.dumps(object))
s = Serializer()
return mark_safe(s.serialize(bundle, 'application/json'))