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

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?

Related

Populate model with JSON data in Angular

I have a class with a model like this:
model = new questions(
1, // Id
'', // Name
'', // LastName
'undefined', // Gender
'undefined', // Filling Status
...
)
The I have a service that makes a GET request to an API, and the response is like this:
{
"id": "5b6f",
"name": "John",
"lastName": "Doe",
"gender": "Male",
"fillingStatus": "Single",
...
}
How can I populate the model with this response, should I iterate to the object?
Thanks for your help!
Angular have a very good tutorial on this exact theme.
https://angular.io/guide/dynamic-form
This uses reactive forms.
If you are not familiar with reactive forms you might want to do these tutorials first:
https://angular.io/guide/reactive-forms
https://angular.io/guide/form-validation
Basically, in the dynamic form tutorial, an HTTP request is made to the server to get form data using a service.
(the tutorial actually uses hardcoded data in a service but it is easy to swap this out for an HTTP request)
Once the data is retrieved it used to dynamically generate a reactive form.
The questions are then iterated to render the view and to render suitable form controls. The view hooks into the reactive form. You won't find a better tutorial.
Use the spread operator like this:
let json = {
"id": "5b6f",
"name": "John",
"lastName": "Doe",
"gender": "Male",
"fillingStatus": "Single",
}
const model = {...json}
console.log(model);
Keep in mind that this is new syntax and not all browsers might support this syntax. So it is smart to transpile this code to ES5 syntax using babel.

Grails Json Views "model" key conflicts with model keyword

I am using the rest-api profile for a Grails app and have the following in one of my json views (_event.gson):
model {
Event event
}
json g.render(event, [excludes: ['product']]) {
product {
id event.product.id
name event.product.name
model event.product.model
}
}
In short, a Product belongs to an Event. By default, I would get the product key with an id inside it as json. I wanted to add more fields to that.
So I used excludes so I could define the fields that would appear under the embedded json document detailing the product. My goal is to have the following as json:
{
"id": 123,
...,
"product": {
"id": 545434,
"name": "Something Cool",
"model": "MZX 1234"
}
}
The last fields -- model -- is not appearing. It seems it is being confused with the model keyword that is used in the very first line of my _event.gson file. Is there any way around this? I tried adding quotes to "model" but it still does not work.
The issue has been reported as a bug: http://github.com/grails/grails-views/issues/45

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.

Ember-Data: How to get properties from nested JSON

I am getting JSON returned in this format:
{
"status": "success",
"data": {
"debtor": {
"debtor_id": 1301,
"key": value,
"key": value,
"key": value
}
}
}
Somehow, my RESTAdapter needs to provide my debtor model properties from "debtor" section of the JSON.
Currently, I am getting a successful call back from the server, but a console error saying that Ember cannot find a model for "status". I can't find in the Ember Model Guide how to deal with JSON that is nested like this?
So far, I have been able to do a few simple things like extending the RESTSerializer to accept "debtor_id" as the primaryKey, and also remove the pluralization of the GET URL request... but I can't find any clear guide to reach a deeply nested JSON property.
Extending the problem detail for clarity:
I need to somehow alter the default behavior of the Adapter/Serializer, because this JSON convention is being used for many purposes other than my Ember app.
My solution thus far:
With a friend we were able to dissect the "extract API" (thanks #lame_coder for pointing me to it)
we came up with a way to extend the serializer on a case-by-case basis, but not sure if it really an "Ember Approved" solution...
// app/serializers/debtor.js
export default DS.RESTSerializer.extend({
primaryKey: "debtor_id",
extract: function(store, type, payload, id, requestType) {
payload.data.debtor.id = payload.data.debtor.debtor_id;
return payload.data.debtor;
}
});
It seems that even though I was able to change my primaryKey for requesting data, Ember was still trying to use a hard coded ID to identify the correct record (rather than the debtor_id that I had set). So we just overwrote the extract method to force Ember to look for the correct primary key that I wanted.
Again, this works for me currently, but I have yet to see if this change will cause any problems moving forward....
I would still be looking for a different solution that might be more stable/reusable/future-proof/etc, if anyone has any insights?
From description of the problem it looks like that your model definition and JSON structure is not matching. You need to make it exactly same in order to get it mapped correctly by Serializer.
If you decide to change your REST API return statement would be something like, (I am using mock data)
//your Get method on service
public object Get()
{
return new {debtor= new { debtor_id=1301,key1=value1,key2=value2}};
}
The json that ember is expecting needs to look like this:
"debtor": {
"id": 1301,
"key": value,
"key": value,
"key": value
}
It sees the status as a model that it needs to load data for. The next problem is it needs to have "id" in there and not "debtor_id".
If you need to return several objects you would do this:
"debtors": [{
"id": 1301,
"key": value,
"key": value,
"key": value
},{
"id": 1302,
"key": value,
"key": value,
"key": value
}]
Make sense?

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'))