Reputation: 2256
I am new to Python and Django, and I just followed the tutorial on Django Book, and I created three Models according to the tutorial - Publisher, Author, Book. Now I want to get all the books, and encoded them to a JSON string. At first, I just use the method I found on djangoproject.com. Here is the code:
from django.core import serializers
def getAllBooks(request):
book_list = Book.objects.all()
return HttpResponse(serializers.serialize("json", book_list), content_type="application/json")
It works fine, but the result is like this:
[
{
"pk": 1,
"model": "books.book",
"fields": {
"publisher": 1,
"title": "Book One",
"authors" : [3, 4],
"publication_date": "2013-07-01"
}
},
{
"pk": 2,
"model": "books.book",
"fields": {
"publisher": 3,
"title": "Book Two",
"authors" : [5],
"publication_date": "2013-07-05"
}
}
]
We can see the authors
and publisher
only show the id. Then I read through that article on djangoproject.com. At the end, it introduces a method called natural_key
. By using that method, the authors
field will look like this:
....
{
"pk": 1,
"model": "books.book",
"fields": {
"publisher": 1,
"title": "Book One",
"authors" : ["Douglas", "Adams"],
"publication_date": "2013-07-01"
}
},
....
It's better, but still not what I exactly want. What I want is this:
[
{
"publisher":{
"website":"http://www.example.com/",
"city":"SYD",
"name":"Publisher",
"country":"AU",
"state":"NSW",
"address":"1 Avenue"
},
"authors":[
{
"first_name":"Eric",
"last_name":"Qian",
"email":"[email protected]"
},
{
"first_name":"Eric2",
"last_name":"Qian",
"email":"[email protected]"
}
],
"publication_date":"01/07/2013",
"title":"Book One"
}
]
The authors
and publisher
fields include all the data. I achieved this by adding a method called JSONEncode to all the models like this:
from django.db import models
# Create your models here.
class Publisher(models.Model):
name = models.CharField(max_length=30)
address = models.CharField(max_length=50)
city = models.CharField(max_length=60)
state_province = models.CharField(max_length=30)
country = models.CharField(max_length=50)
website = models.URLField()
def __unicode__(self):
return self.name
class Meta:
ordering = ['name']
def JSONEncode(self):
#init a dictionary
JSONData = {};
#name
JSONData['name'] = self.name
#address
JSONData['address'] = self.address
#city
JSONData['city'] = self.city
#state_province
JSONData['state'] = self.state_province
#country
JSONData['country'] = self.country
#website
JSONData['website'] = self.website
#return the json data
return JSONData
class Author(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=40)
email = models.EmailField()
def __unicode__(self):
return u'%s %s' % (self.first_name, self.last_name)
def JSONEncode(self):
#init a dictionary
JSONData = {};
#first_name
JSONData['first_name'] = self.first_name
#last_name
JSONData['last_name'] = self.last_name
#email
JSONData['email'] = self.email
#return the json data
return JSONData
class Book(models.Model):
title = models.CharField(max_length=100)
authors = models.ManyToManyField(Author)
publisher = models.ForeignKey(Publisher)
publication_date = models.DateField()
def __unicode__(self):
return self.title
class Meta:
ordering = ['title']
def JSONEncode(self):
#init a dictionary
JSONData = {};
#title
JSONData['title'] = self.title
#authors
authors = []
for author in self.authors.all():
authors.append(author.JSONEncode())
JSONData['authors'] = authors
#publisher
JSONData['publisher'] = self.publisher.JSONEncode()
JSONData['publication_date'] = self.publication_date.strftime('%d/%m/%Y')
#return the json data
return JSONData
Then I modify the code in books.views:
def getAllBooks(request):
book_list = Book.objects.all()
book_list_data = []
for book in book_list:
book_list_data.append(book.JSONEncode())
return HttpResponse(json.dumps(book_list_data), content_type="application/json")
It works quite good, but the draw back is obvious - I have to write a JSONEncode()
function to all models. So I am wondering is Django provide a better way to do this? Thanks in advance!
Upvotes: 2
Views: 127
Reputation: 86
Option 1 is Django-piston. By specifying model fields manually you can get nested listings and foreign key following. Probably the fastest way to get what you want, minimum errors, but not really flexible.
Option 2 if to do model serialization in naïve way by your own. For each model define a method that would convert itself to python dictionary, and then use simplejson.dumps in views.py to make it JSON. This approach gives you full control and infinite flexibility deciding about your JSON structure. Later you can replace it with more elegant solution using multiple inheritance to add as_dict and define own JsonResponse class.
Example:
# In models.py, add as_dict() method to all models
# Example for class Book
def as_dict(self):
d = {
"id": self.id,
"publisher": self.publisher.as_dict(), # avoid this
"title": self.title,
"publication_date": str(self.publication_date),
"publisher": self.publisher,
"authors": [author.as_dict() for author in self.authors.all()] # avoid this
}
# then in views.py
def getAllBooks(request):
book_list = [book.as_dict() for book in Book.objects.all().select_related()]
return HttpResponse(simplejson.dumps(book_list),
content_type="application/json")
HOWEVER
Both approaches are kind of dirty. Your database may and probably will struggle because of this. Django ORM will produce ugly sql. Best is to avoid nested model serialization at all. And if you really need it, dont forget to select_related().
Good luck!
Upvotes: 1
Reputation: 1666
You could try using Tastypie or django-rest-framework. You can customise the JSON emitted. Whilst it adds another layer of complexity it will probably pay off in the long run.
Upvotes: 1