Django Rest Framework JSONAPI and sideloaded/included resources - django

I am using the Django Rest Framwork JSON API for my Ember back end.
The (data) response I am getting back includes the "relationship" key but I need to sideload resources for a particular model, and hence want to include the "included" key as shown on the Ember docs https://guides.emberjs.com/release/models/relationships
I have a Product model with a FK relationship to a Tax model.
Here is my tax serializer:
from rest_framework_json_api import serializers
from .models import Tax
class TaxSerializer(serializers.ModelSerializer):
class Meta:
model = Tax
fields = ('id', 'name', 'amount')
Here is my product serializer:
from rest_framework_json_api import serializers
from .models import Product
from tax.serializers import TaxSerializer
included_serializers = {
'tax': TaxSerializer
}
class Meta:
model = Product
fields = ('id', 'name', 'image', 'price','tax')
class JSONAPIMeta:
included_resources = ['tax']
For this I've followed the example from https://www.mattlayman.com/blog/2017/sideload-json-api-django/
However, my response still includes the "relationships" key, and not the "included" key eg
"data" : [
{
"type":"products",
"id": "1",
"attributes": {...omitted for brevity ...
},
"relationships": {
"tax": {
"data": {
"type":"tax",
"id":"1"
}
}
}
},
{...etc....}
]
Update:
I am now getting the included key back in the response which is great. However, the whole point of doing this was that in my Ember models I don't have to create explicit relationships... from the Ember docs
when the API returns a deeply nested, read-only object or array, there
is no need to create multiple models with DS.attr('hasMany') or
DS.attr('belongsTo') relationships. This could result in a potentially
large amount of unnecessary code. You can access these objects in the
template without transforming them. This can be done with DS.attr()
(No attribute type).
I have this done in my Product model in Ember:
tax: DS.attr()
In my templates, assuming I already have a product instance I would expect to to be able to access product.tax.amount - but I can't.

user serializer depth= 1 or 2
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = ('id', 'account_name', 'users', 'created')
depth = 1

Related

graphene-django: Determine Object type when multiple GraphQL object types use the same django model in node query

This is specifically for graphene-django (not just graphene) when performing a node(id: ...) {} query.
Assume a fixed schema with 2 (or more) different GraphQL object types using graphene_django.DjangoObjectType linked to the same django model:
import graphene_django
from .models import Org as OrgModel
class Org(graphene_django.DjangoObjectType):
class Meta:
model = OrgModel
fields = (
"id",
"name",
"billing"
)
class AnonymousOrg(graphene_django.DjangoObjectType):
class Meta:
model = OrgModel
fields = (
"id",
"name",
)
Let's assume a query to Org of ID 7eca71ed-ff04-4473-9fd1-0a587705f885.
btoa('Org:7eca71ed-ff04-4473-9fd1-0a587705f885')
'T3JnOjdlY2E3MWVkLWZmMDQtNDQ3My05ZmQxLTBhNTg3NzA1Zjg4NQ=='
{
node(id: "T3JnOjdlY2E3MWVkLWZmMDQtNDQ3My05ZmQxLTBhNTg3NzA1Zjg4NQ==") {
id
__typename
... on Org {
id
}
}
}
Return:
{
"data": {
"node": {
"id": "QW5vbnltb3VzT3JnOjdlY2E3MWVkLWZmMDQtNDQ3My05ZmQxLTBhNTg3NzA1Zjg4NQ==",
"__typename": "AnonymousOrg"
}
}
}
It returns the other object type 'AnonymousOrg:7eca71ed-ff04-4473-9fd1-0a587705f885', despite the relay ID specifying it was an Org object.
Is there a way in graphene-django to "hint" or provide detail to assure the return type if what's specified in the ID and its fragment?
Clarification on question
Other questions were discussing graphene, not specifically graphene-django, which is doing the type determinations in this case.
This is different from Django-graphene multiple types for the same model, as that one asked about how to handle field permissions and opened possibility to reconsidering schema structure (e.g. splitting between Public and Private API schemas) - that isn't a choice in this question.
Credit: This is graphene django models are based on the ones from the question at Django-graphene multiple types for the same model.
P.S. I created a bug issue on the tracker here: graphene-django#1291 named "DjangoObjectType using the same django model do not resolve to correct relay object". Supposedly this issue was resolved years ago, but communication lines got crossed.
The credit to this goes to #Boolangery (GitHub)'s workaround on May 25th, 2020.
class FixRelayNodeResolutionMixin:
#classmethod
def get_node(cls, info, pk):
instance = super(FixRelayNodeResolutionMixin, cls).get_node( info, pk)
setattr(instance, "graphql_type", cls.__name__)
return instance
#classmethod
def is_type_of(cls, root, info):
if hasattr(root, "graphql_type"):
return getattr(root, "graphql_type") == cls.__name__
return super(FixRelayNodeResolutionMixin, cls).is_type_of(root, info)
Usage, here is an example with a django User model with two Django Graphene objects pointing to it: PublicUserType and UserType:
from graphene_django import DjangoObjectType
class PublicUserType(FixRelayNodeResolutionMixin, DjangoObjectType):
class Meta:
model = User
interfaces = (graphene.relay.Node,)
fields = ['id', 'first_name', 'last_name']
class UserType(FixRelayNodeResolutionMixin, DjangoObjectType):
class Meta:
model = User
interfaces = (graphene.relay.Node,)
fields = ['id', 'first_name', 'last_name', 'profile']
today I was the same issue and I solved it as stackoverflow
For me works reorder the classes types from class type with less fields to class type with more fields.
So,
class AnonymousOrg(graphene_django.DjangoObjectType):
class Meta:
model = OrgModel
fields = (
"id",
"name",
)
class LiteOrg(graphene_django.DjangoObjectType):
class Meta:
model = OrgModel
fields = (
"id",
"name",
"billing"
)
class OrgAll(graphene_django.DjangoObjectType):
class Meta:
model = OrgModel
fields = '__all__'

Custom Serializer for Django Rest Framework

I'm searching for a solution to create a serializer / API Endpoint to represent data in a custom order. When adding serializers and viewsets to DRF, I only get the fields associated with that Model. But what I like to have is a custom structure of all my models together. As an example:
I have a model called season, a model called evenings and a model called events. Now I'd like to have an API Endpoint to have that all together, like so:
{
"requestTime": "2021-11-09 08:20",
"requestURL": "/all",
"requestMethod": "GET",
"responseCode": 200,
"season": "2021/2022",
"evenings": [
{
"evevning_id": 0,
"day": "",
"date": "2021-11-11",
"event_count": 2,
"events": [
{},
{}
]
}
]
}
For data structure in the models I have some ForeignKeys like:
season
|
evening
|
event
Any suggestions how to achieve this?
Use nested serializer (Season > Evening > Event) like this.
class EventSerializer(serializers.ModelSerializer):
class Meta:
model = models.Event
fields = ['id',...]
class EveningSerializer(serializers.ModelSerializer):
events = EventSerializer(many=True)
class Meta:
model = models.Evening
fields = ['id', 'day', 'date','events',...]
class SeasonSerializer(serializers.ModelSerializer):
evenings = EveningSerializer(many=True)
class Meta:
model = models.Season
fields = ['id', 'season', 'evenings',...]
make sure when fetching season from database, use prefetch related in queryset.

Serializing a Profile with associated Products with Django Rest Framework

I'm building a sample iOS app where users would buy and sell products, and I'm trying to design a page where the upper section will have basic profile details and the lower section will have the products that they're currently selling, something like this:
Sample Frontend Image
So I'm trying to create a Serializer / API endpoint which would give me the Profile, with the products that they are currently selling.
My Product model has a ForeignKey relationship to User:
class Product(models.Model):
def __str__(self):
return self.name
name = models.CharField(max_length=200)
seller = models.ForeignKey(User, on_delete=models.CASCADE, related_name="product_seller")
category = models.ForeignKey("Category", on_delete=models.CASCADE)
(...)
And I have a Profile model which has a one-to-one relationship with the default Django user. As this view will be based on Profiles, I think it would make more sense to serialize the User or the Profile model and get the products where they are the "seller".
So the JSON response that I want is something like this:
{
"id": 1,
"username": “some username”,
"profile_image": "http://192.168.1.101:8000/images/profile_pictures/732339C5-E419-4A3D-9022-A314416F5F02.png",
"description": “Some description for this particular profile.”
“products”: [
{ “id”: 1,
“image” = http://192.168.1.101:8000/images/abc.jpg,
},
{ “id”: 2,
“image” = http://192.168.1.101:8000/images/abc.jpg,
},
{ “id”: 3,
“image” = http://192.168.1.101:8000/images/abc.jpg,
}
]
}
What would be the best way to approach this using django-rest-framework?
I've tried using Nested Serializers, but my User or Profile models don't explicitly have a relationship to Product, so they haven't worked so far:
class SellerProductsSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = ["id", "images"]
class SellerSerializer(serializers.ModelSerializer):
products = SellerProductsSerializer(read_only=True, many=True)
profile_image = serializers.CharField(source="profile.profile_image")
description = serializers.CharField(source="profile.description")
class Meta:
model = User
fields = ["id", "username", "profile_image", "description", "products"]
I've also tried using the SerializerMethodField, which I think would work in this case, but I haven't quite figured out how I would filter the products where the particular user is the seller. The endpoints look like this:
.../application/api/seller/1
If I could access the "1" - which is the user id, I could filter with something like:
class SellerSerializer(serializers.ModelSerializer):
products = serializers.SerializerMethodField()
profile_image = serializers.CharField(source="profile.profile_image")
description = serializers.CharField(source="profile.description")
class Meta:
model = User
fields = ["id", "username", "profile_image", "description", "products"]
def get_products(self):
# get the id from the request / or the url
# filter the Product model.
I'm used to doing filtering on the viewsets, but not on the serializer itself. I think filtering on the viewset is not possible in this case, as I'm working with two different models and the one that I'm trying to filter is not the main model for the serializer.
I feel like there has to be a simple way to do this but I have been stuck for quite some time. Any help would be greatly appreciated.
You can retrieve a user with pk in the url,and return a response with that particular user, and the result of the following query:
Product.objects.filter(seller_id=pk)
And add the results to a list,and return a response that looks like this:
Response({
"Seller":Fetched User,
"Their Products":list of products
})

insertion in nested serializer in DRF

Currently i am using django rest_framework.I have two different class name as Customer and Customerinfo . My code is working properely . Now I want to insert value in the customer serializer. In CustomerSerializer it has Customerinfo field.
These are the serializer:
class CustomerinfoSerializer(serializers.ModelSerializer):
class Meta:
model = Choice
fields = ['id','phone']
class CustomerSerializer(serializers.ModelSerializer):
info = CustomerinfoSerializer(many=True, source='customerinfo_set')
class Meta:
model = Question
fields = ["id","name","info"]
How can i use post method to insert value ? sample values are:
{
"name": "user10",
"info":
[
{ "phone":12345
},
{ "phone":54321
}
]
}
If I understand you correctly your looking for writable nested serializers.
As documentation says,
by default nested serializers are read-only. If you want to support
write-operations to a nested serializer field you'll need to create
create() method.
You could try something similar to this (but you need to adjust your naming):
class CustomerSerializer(serializers.ModelSerializer):
info = CustomerinfoSerializer(many=True, source='customerinfo_set')
class Meta:
model = Question
fields = ["id", "name", "info"]
def create(self, validated_data):
info_data = validated_data.pop('info')
question = Question.objects.create(**validated_data)
for info in info_data:
Choice.objects.create(question=question, **info)
return question

Django rest framework POST many to many with extra fields

I am trying to create a model in Django that has a many-to-many relationship to another model with extra fields. I am using the rest framework to provide CRUD operations on these and am having a chicken-and-egg scenario I believe...
The issue is that when I go to POST the new MainObject, it throws an error in the many-to-many part due to not having a MainObject id to point to. But I want it to point to the MainObject I am creating, which doesn't exist at time of POST'ing. I believe this to be an issue with the serializers, but am unsure of how to resolve it. I assume my assumptions might also be off in how I am formulating the POST data.
I am using Django 2.1.8
Model Code
class RelatedObject(models.Model):
...
class MainObject(models.Model):
related_objects = models.ManyToManyField(RelatedObject, through='ManyRelatedObject')
class ManyRelatedObject(models.Model):
main_object = models.ForeignKey(MainObject, on_delete=models.DO_NOTHING)
related_object = models.ForeignKey(RelatedObject, on_delete=models.DO_NOTHING)
other_attribute = models.BooleanField(...)
Serializer Code
class ManyRelatedObjectSerializer(serializers.ModelSerializer):
main_object = serializers.PrimaryKeyRelatedField(queryset=MainObject.objects.all())
related_object = serializers.PrimaryKeyRelatedField(queryset=RelatedObject.objects.all())
class Meta:
model = ManyRelatedObject
fields = '__all__'
class MainObjectSerializer(serializers.ModelSerializer):
related_object = ManyRelatedObjectSerializer(many=True)
class Meta:
model = MainObject
fields = '__all__'
POST Payload
( It is assumed that there exists a RelatedObject that has an id of 1)
{
"related_object": [
{
"related_object": 1,
"other_attribute": true
}
],
...
}
Response
{
"related_object": [
{
"main_object": [
"This field is required."
]
}
]
}
Goal Response:
{
"id": 1,
"related_object": [
{
"main_object": 1,
"related_object": 1,
"other_attribute": true
}
],
...
}
REST endpoint setup
class MainObjectViewSet(viewsets.ModelViewSet):
queryset = MainObject.objects.all()
serializer_class = MainObjectSerializer
Override the __init__() method of the MainObjectSerializer.
class MainObjectSerializer(serializers.ModelSerializer):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.context['request'].method == 'GET':
self.fields['related_object'] = ManyRelatedObjectSerializer(many=True)
related_object = ManyRelatedObjectSerializer(many=True)# remove this line
class Meta:
model = MainObject
fields = '__all__'
What this snippt do is, the serializer will render the response/output using ManyRelatedObjectSerializer serializer, if the request is a HTTP GET, otherwise it will render the stock mode(PrimaryKeyRelatedField)
For posterity, ended up hand-jamming this with poorly overridden create and update methods, due to time constraints. Seems ridiculous that django can't handle this scenario, seems like it's a fairly common use case...