Django queryset to JSON object nested by primary key - python

I'm trying to parse a queryset into a JSON object, so that each key is the primary key of the model, and each value is a JSON object containing all other fields.
myjson = {
apple: { color: "r", calories: 10},
banana: { color: "w", calories: 50}
​ }
Here is the model and Django view collecting the data (based on this blog post):
class Fruit(models.Model):
fruit_id = models.CharField(primary_key=True)
color = models.CharField(max_length=50)
calories = models.IntegerField()
def get_FruitsTableDjango(request):
fruits_table = Fruit.objects.all().values()
fruits_table_list = list(fruits_table ) # important: convert the QuerySet to a list object
return JsonResponse(fruits_table_list , safe=False)
But on the client's side (via AJAX), this returns an array of objects:
mydata = [
0: { fruit_id: "apple", color: "r", calories: 10},
1: { fruit_id: "banana", color: "w", calories: 50}
]
I found also here how I can rework this array as expected :
//Restructure JSON by fruit_id name
fruits= {},
mydata.forEach(function (a) {
var temp = {};
Object.keys(a).forEach(function (k) {
if (k === 'fruit_id') {
fruits[a[k]] = temp; //gets fruit_id
return;
}
temp[k] = a[k]; //fill the temp variable with elements
});
});
mydata=fruits; //overwrite initial array with nicely-rearranged-by-fruitId object
I have basically two questions:
Is there a more direct way to obtain the desired JSON (nested by primary keys)?
If not, where is objectively the best place to perform the object-parsing logic: on the client's side in Javascript (like above), or on the server side, e.g. in the Django view?
​​

In DRF,
Let's say I have model.py as below
from django.db import models
class OrganisationalUnitGrouper(models.Model):
name = models.CharField(max_length=255)
def __str__(self):
return self.name
#property
def unit(self):
return self.organisationalunit_set.all()
class OrganisationalUnit(models.Model):
unit_id = models.CharField(max_length=255)
name = models.CharField(max_length=255)
display = models.CharField(max_length=255)
description = models.CharField(max_length=255)
path = models.CharField(max_length=255)
ou_group = models.ForeignKey(OrganisationalUnitGrouper, on_delete=models.PROTECT)
def __str__(self):
return self.name
class Meta:
verbose_name_plural = "OU Unit"
Serializer you can define like
from rest_framework import serializers
from .models import OrganisationalUnitGrouper, OrganisationalUnit
class OUSerializer(serializers.ModelSerializer):
# id = serializers.IntegerField(required=False)
class Meta:
model = OrganisationalUnit
fields = ("unit_id", "name", "display", "description", "path")
class OUGroupSerializer(serializers.ModelSerializer):
unit = OUSerializer(many=True)
class Meta:
model = OrganisationalUnitGrouper
fields = ("name", "unit")
And viewset could be
from rest_framework import viewsets
from .serializers import OUSerializer, OUGroupSerializer
from .models import OrganisationalUnitGrouper, OrganisationalUnit
class OUGroupViewSet(viewsets.ModelViewSet):
queryset = OrganisationalUnitGrouper.objects.all()
serializer_class = OUGroupSerializer
class OUViewSet(viewsets.ModelViewSet):
queryset = OrganisationalUnit.objects.all()
serializer_class = OUSerializer
This will give you nested API.

Related

GraphQL Mutation in Graphene for Object with Foreign Key Relation

I'm building a simple CRUD interface with Python, GraphQL (graphene-django) and Django. The CREATE mutation for an Object (Ingredient) that includes Foreign Key relations to another Object (Category) won't work. I want to give GraphQL the id of the CategoryObject and not a whole category instance. Then in the backend it should draw the relation to the Category object.
In the Django model the Ingredient Object contains an instance of the Foreign key Category Object (see code below). Is the whole Category Object needed here to draw the relation and to use Ingredient.objects.select_related('category').all()?
The create mutation expects IngredientInput that includes all properties and an integer field for the foreign key relation. So the graphQL mutation itself currently works as I want it to.
My question is similar if not the same as this one but these answers don't help me.
models.py:
class Category(models.Model):
name = models.CharField(max_length=50, unique=True)
notes = models.TextField()
class Meta:
verbose_name = u"Category"
verbose_name_plural = u"Categories"
ordering = ("id",)
def __str__(self):
return self.name
class Ingredient(models.Model):
name = models.CharField(max_length=100)
notes = models.TextField()
category = models.ForeignKey(Category, on_delete=models.CASCADE)
class Meta:
verbose_name = u"Ingredient"
verbose_name_plural = u"Ingredients"
ordering = ("id",)
def __str__(self):
return self.name
schema.py:
class CategoryType(DjangoObjectType):
class Meta:
model = Category
class CategoryInput(graphene.InputObjectType):
name = graphene.String(required=True)
notes = graphene.String()
class IngredientType(DjangoObjectType):
class Meta:
model = Ingredient
class IngredientInput(graphene.InputObjectType):
name = graphene.String(required=True)
notes = graphene.String()
category = graphene.Int()
class CreateIngredient(graphene.Mutation):
class Arguments:
ingredientData = IngredientInput(required=True)
ingredient = graphene.Field(IngredientType)
#staticmethod
def mutate(root, info, ingredientData):
_ingredient = Ingredient.objects.create(**ingredientData)
return CreateIngredient(ingredient=_ingredient)
class Mutation(graphene.ObjectType):
create_category = CreateCategory.Field()
create_ingredient = CreateIngredient.Field()
graphql_query:
mutation createIngredient($ingredientData: IngredientInput!) {
createIngredient(ingredientData: $ingredientData) {
ingredient {
id
name
notes
category{name}
}
graphql-variables:
{
"ingredientData": {
"name": "milk",
"notes": "from cow",
"category": 8 # here I ant to insert the id of an existing category object
}
}
error-message after executoin the query:
{
"errors": [
{
"message": "Cannot assign \"8\": \"Ingredient.category\" must be a \"Category\" instance.",
"locations": [
{
"line": 38,
"column": 3
}
],
"path": [
"createIngredient"
]
}
],
"data": {
"createIngredient": null
}
}
I had this same problem today.
The Cannot assign \"8\": \"Ingredient.category\" must be a \"Category\" instance. error is a Django error that happens when you try to create an object using the foreign key integer directly instead of an object.
If you want to use the foreign key id directly you have to use the _id suffix.
For example, instead of using:
_ingredient = Ingredient.objects.create(name="milk", notes="from_cow", category=8)
You have to use either
category_obj = Category.objects.get(id=8)
_ingredient = Ingredient.objects.create(name="milk", notes="from_cow", category=category_obj)
or
_ingredient = Ingredient.objects.create(name="milk", notes="from_cow", category_id=8)
In the case of using GraphQL, you would have to set your InputObjectType field to <name>_id. In your case:
class IngredientInput(graphene.InputObjectType):
name = graphene.String(required=True)
notes = graphene.String()
category_id = graphene.Int()
This, however will make your field in the schema show up as categoryId. If you wish to keep the category name, you must change to:
category_id = graphene.Int(name="category")
Cheers!

Django rest framework serializer foreign key

so i have the following models in my
models.py :
class Coordonnees(models.Model):
latitude = models.CharField(max_length=20)
longitude = models.CharField(max_length=20)
def __str__(self):
return self.latitude+','+self.longitude
class Ecole(models.Model):
nomEcole = models.CharField(max_length=100)
numTel = models.CharField(max_length=100)
coordonnee = models.ForeignKey(Coordonnees, on_delete=models.CASCADE)
def __str__(self):
return self.nomEcole
and in my serializers.py :
class CoordonneesSerializer(serializers.ModelSerializer):
class Meta:
model = Coordonnees
#our fields
fields = ('id','latitude','longitude')
class EcoleSerializer(serializers.ModelSerializer):
class Meta:
model = Ecole
#our fields
fields = ('id','nomEcole','numTel','coordonnee')
well the problem is that when i check the json file of my "Ecole" i get the following output
[{"id":1,"nomEcole":"draoui","numTel":"28747484","coordonnee":1}]
so the question is : instead of having 1 in "coordonnee" i want to show the latitude and the longitude
The easiest way to generate nested representations is by using depth option inside the Meta class.
class EcoleSerializer(serializers.ModelSerializer):
class Meta:
model = Ecole
depth = 1
fields = ('id', 'nomEcole', 'numTel', 'coordonnee')
Output will then be like:
[
{
"id":1,
"nomEcole":"draoui",
"numTel":"28747484",
"coordonnee": [
{
"id": 1,
"latitude": 0,
"longitude": 0
}
]
}
]
You are looking to serialize a nested relationship in Django Rest Framework. To achieve this you can use the serializer of the nested model as a serializer field:
class EcoleSerializer(serializers.ModelSerializer):
coordonnee = CoordonneesSerializer()
class Meta:
model = Ecole
#our fields
fields = ('id', 'nomEcole', 'numTel', 'coordonnee')

Django ManyToMany field as json format

I'm trying to get data as json format. I've one ManyToMany field which is returning just id. But I need that contents too. Here is my models.py
class Pricing(models.Model):
name = models.CharField(max_length = 100)
price = models.CharField(max_length = 100)
def __str__(self):
return self.name+' and '+self.price
class Service(models.Model):
name = models.CharField(max_length=100)
price = models.ManyToManyField(Pricing, blank=True)
def __str__(self):
return self.name
And also the views.py which is returning json format data
def all_service_json(request, name):
data = serializers.serialize("json", Service.objects.filter(name__icontains=name))
return HttpResponse(data)
Now Getting the output like below
[
{
"model": "myapp.service",
"pk": 2,
"fields":
{
"name": "Service name",
"price": [1, 2]
}
}
]
But want like below
[
{
"model": "myapp.service",
"pk": 2,
"fields":
{
"name": "Service name",
"price":
{
1: "Price 1",
2: "Price 2"
}
}
}
]
Creating ModelSerializer objects from within Django Rest Framework will let you display nested object data:
http://www.django-rest-framework.org/api-guide/serializers/#dealing-with-nested-objects
# myapp/serializers.py
...
from rest_framework import serializers
class PricingSerializer(serializers.ModelSerializer):
class Meta:
fields = '__all__'
model = Pricing
class ServiceSerializer(serializers.ModelSerializer):
price = PricingSerializer(read_only=True, many=True)
class Meta:
fields = '__all__'
model = Service
# myapp/views.py
def all_service_json(request, name):
services = Service.objects.filter(name__icontains=name)
data = ServiceSerializer(services, many=True).data
return HttpResponse(data)
As #robert mentioned using nested serializers will fix your issue.
But note that by default nested serializers are read-only. So If you
want to support write operations to a nested serializer field you'll
need to add create() and/or update() methods in order to explicitly
specify how the child relationships should be saved.
Writable Service Serializer
class ServiceSerializer(serializers.ModelSerializer):
price = PricingSerializer(many=True)
class Meta:
fields = '__all__'
model = Service
# sample create
def create(self, validated_data):
prices_data = validated_data.pop('price')
service = Service.objects.create(**validated_data)
for price_data in prices_data:
Price.objects.create(service=service, **price_data)
return service
# add update here
myapp/views.py
def all_service_json(request, name):
services = Service.objects.filter(name__icontains=name)
serializer = ServiceSerializer(services)
return HttpResponse(serializer.data)
So in your case all you have to do is to add depth = 1 and you will get nested representations.
Docs
The default ModelSerializer uses primary keys for relationships, but
you can also easily generate nested representations using the depth
option:
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = ['id', 'account_name', 'users', 'created']
depth = 1
I just start learning Django from last 8 hours and I stuck into this situation where many to many relationship is returning id instead of child data. I wrote some custom code. For me it solve my problem, hope this helps someone.
from django.core.serializers import serialize
import json
def modelToDict(model):
jsn = serialize("json", model) # convert to json
mydict = json.loads(jsn) # again convert to dictionary
return mydict
def all_service_json(request, name):
data = Service.objects.filter(name__icontains=name)
dictdata = modelToDict(data)
for i in range(len(dictdata)):
price = modelToDict(data[i].price.all())
dictdata[i]['fields']['price'] = price
return HttpResponse(json.dumps(`dictdata`), content_type="application/json")

In Django Rest, not able to serialize object which has One-to-Many mapping

I am beginner in Django Rest framework. I want to implement One to Many object mapping like following json schema:
{
"from_date": "2017-08-06T12:30",
"to_date": "2017-08-06T12:30",
"coupon_name": "WELCOME100",
"min_booking_value": 150,
"applicable_days": [
{
"from_time": "13:00",
"to_time": "15:00",
"applicable_day": 2
},
{
"from_time": "16:00",
"to_time": "18:00",
"applicable_day": 3
}
]
}
For above json schema, I have created following Django Model classes:
class Coupon(models.Model):
coupon_id = models.AutoField(primary_key=True)
from_date = models.DateTimeField()
to_date = models.DateTimeField()
coupon_name = models.TextField()
min_booking_value = models.FloatField()
def __unicode__(self):
return 'Coupon id: ' + str(self.coupon_id)
class CouponApplicableDays(models.Model):
from_time = models.TimeField()
to_time = models.TimeField()
applicable_day = models.IntegerField()
And following serializer class above models:
class CouponApplicableDaysSerializer(serializers.ModelSerializer):
class Meta:
model = CouponApplicableDays
fields = ('from_time', 'to_time', 'applicable_day')
class CouponSerializer(serializers.ModelSerializer):
coupon_applicable_days = CouponApplicableDaysSerializer(required=True, many=True)
class Meta:
model = Coupon
fields = ('coupon_id', 'from_date', 'to_date', 'coupon_name', 'min_booking_value', 'coupon_applicable_days',)
def create(self, validated_data):
coupon_applicable_days_data = validated_data.pop("coupon_applicable_days")
coupon = Coupon.objects.create(**validated_data)
CouponApplicableDays.objects.create(coupon=coupon, **coupon_applicable_days_data)
return coupon
When I save data using coupon-serializer. It saves only in Coupon table not in CouponApplicableDays.
I know, I have messed up somewhere but I don't know where. Can you guys please look into above code and tell me how can I solve this?
You have a list here
coupon_applicable_days_data = validated_data.pop("coupon_applicable_days")
Either iterate over the list and create the objects, like this:
for applicable_day in coupon_applicable_days_data:
CouponApplicableDays.objects.create(coupon=coupon, **applicable_day)
or use the bulk_create method
https://docs.djangoproject.com/en/1.11/ref/models/querysets/#bulk-create
CouponApplicableDays.objects.bulk_create(
[CouponApplicableDays(coupon=coupon, **aplicable_day)
for applicable_day in coupon_applicable_days_data]
)
Be aware that the bulk_create will not trigger pre_save/post_save signals.

Referencing relation's relations in Django Serializer

Let's say I have some models:
class A(models.Model):
...
class B(models.Model):
my_reference_to_a = models.ForeignKey(A)
b_field_1 = ...
b_field_2 = ...
class C(models.Model):
my_reference_to_b = models.ForeignKey(B)
c_field_1 = ...
...
In my serializer for C, I want to include all of the fields in C, all the fields in B, as well as the reference to A in B (but not the reference to B in C), so the JSON API output would be something like this:
{
"data": [{
"type": "C",
"id": "1",
"attributes": {
"b_field_1": "...",
"b_field_2": "...",
"c_field_1": "..."
},
"relationships": {
"a": {
"data": {
"type": "A",
"id": "1"
}
}
}
}],
...
}
How would I go about this? I've already tried doing something like this inside my serializer for C:
A = ASerializer(source='my_reference_to_b.my_reference_to_a')
But that doesn't work, as DRF doesn't seem to support dotted paths for sources. I've also tried supplying a method that returns the proper model (the model is valid inside the method) as the source, but that outputs the reference in the JSON as:
"a": {
"data": null
}
On my A model, I also have a reference to another model, D, that is not explicitly stated in A, but is instead defined in D as a OneToMany relationship (Many D models to one A model) with a resource_name on the ForeignKey declared in D, and trying to reference this in C to include that relationship in the JSON doesn't work, either. I get this error (trying to reference it by doing D = DSerializer(source='B.D')):
'RelatedManager' object has no attribute 'B'
Any help would be greatly appreciated.
I figured it out. Just answering my own question in case anyone lands on this page and they need help.
You need to use the SerializerMethodResourceRelatedField from the Django Rest Framework JSON API. I had tried the regular ResourceRelatedField without it working, looking through the source code showed me that ResourceRelatedField doesn't support dotted paths. Instead, use SerializerMethodResourceRelatedField with a source pointing to a method that returns the desired relation.
# Model
from django.db import models
class Album(models.Model):
album_name = models.CharField(max_length=100)
artist = models.CharField(max_length=100)
class Track(models.Model):
album = models.ForeignKey(Album, related_name='tracks',on_delete=models.DO_NOTHING)
order = models.IntegerField()
title = models.CharField(max_length=100)
duration = models.IntegerField()
class Meta:
unique_together = ('album', 'order')
ordering = ['order']
def __unicode__(self):
return '%d: %s' % (self.order, self.title)
# View
from rest_framework import generics,viewsets
from api.models import Album
from api.serializers import AlbumSerializer
class TracksView(generics.ListAPIView):
queryset = Album.objects.all()
serializer_class = AlbumSerializer
class TracksView(generics.CreateAPIView):
queryset = Album.objects.all()
serializer_class = AlbumSerializer
# serializers
from rest_framework import serializers
from api.models import Album
class AlbumSerializer(serializers.ModelSerializer):
tracks = serializers.StringRelatedField(many=True,)
class Meta:
model = Album
fields = ('album_name', 'artist', 'tracks')

Categories