I'm working with graphene-django ,graphql and django and i want to use the reverse foreign key concept of drf using related name but currently i facing difficulty in achieving that i have put up the models, code and also the output screenshot below
import asyncio
asyncio.set_event_loop(asyncio.new_event_loop())
import graphene
from graphene_django import DjangoObjectType
from .models import Category, Book, Grocery, FileUpload
from django.db.models import Q
from graphene_file_upload.scalars import Upload
import decimal
class BookType(DjangoObjectType):
class Meta:
model = Book
fields = (
'id',
'title',
'author',
'isbn',
'pages',
'price',
'quantity',
'description',
'status',
'date_created',
)
class FileUploadType(DjangoObjectType):
class Meta:
model = FileUpload
fields = ('id', 'file')
class GroceryType(DjangoObjectType):
class Meta:
model = Grocery
# fields = (
# 'product_tag',
# 'name',
# 'category',
# 'price',
# 'quantity',
# 'imageurl',
# 'status',
# 'date_created',
# )
class CategoryType(DjangoObjectType):
grocery = graphene.List(GroceryType)
class Meta:
model = Category
fields = ('id', 'title', 'grocery')
class Query(graphene.ObjectType):
books = graphene.List(
BookType,
search=graphene.String(),
first=graphene.Int(),
skip=graphene.Int(),
)
categories = graphene.List(CategoryType)
# books = graphene.List(BookType)
groceries = graphene.List(GroceryType)
files = graphene.List(FileUploadType)
# def resolve_books(root, info, **kwargs):
# # Querying a list
# return Book.objects.all()
def resolve_books(self, info, search=None, first=None, skip=None, **kwargs):
qs = Book.objects.all()
if search:
filter = (
Q(id=search) |
Q(title__icontains=search)
)
qs = qs.filter(filter)
if skip:
qs = qs[skip:]
if first:
qs = qs[:first]
return qs
def resolve_categories(root, info, **kwargs):
# Querying a list
r = []
for ele in Category.objects.all().prefetch_related("grocery"):
r.append(ele.grocery.all())
return Category.objects.all().prefetch_related("grocery")
def resolve_groceries(root, info, **kwargs):
# Querying a list
print("help")
qs = Grocery.objects.all()
print(qs)
return qs
def resolve_files(root, info, **kwargs):
# Querying a list
return FileUpload.objects.all()
class UpdateCategory(graphene.Mutation):
class Arguments:
# Mutation to update a category
title = graphene.String(required=True)
id = graphene.ID()
category = graphene.Field(CategoryType)
@classmethod
def mutate(cls, root, info, title, id):
category = Category.objects.get(pk=id)
category.title = title
category.save()
return UpdateCategory(category=category)
class CreateCategory(graphene.Mutation):
class Arguments:
# Mutation to create a category
title = graphene.String(required=True)
# Class attributes define the response of the mutation
category = graphene.Field(CategoryType)
@classmethod
def mutate(cls, root, info, title):
category = Category()
category.title = title
category.save()
return CreateCategory(category=category)
class FileUploadMutation(graphene.Mutation):
class Arguments:
file = Upload(required=True)
success = graphene.Boolean()
def mutate(self, info, file, **kwargs):
# do something with your file
file_obj = FileUpload()
file_obj.file = file
file_obj.save()
return FileUploadMutation(success=True)
class BookInput(graphene.InputObjectType):
title = graphene.String()
author = graphene.String()
pages = graphene.Int()
price = graphene.Int()
quantity = graphene.Int()
description = graphene.String()
status = graphene.String()
class GroceryInput(graphene.InputObjectType):
product_tag = graphene.String()
name = graphene.String()
category_id = graphene.String(required=True)
price = graphene.Int()
quantity = graphene.Int()
imageurl = graphene.String()
status = graphene.String()
class CreateBook(graphene.Mutation):
class Arguments:
input = BookInput(required=True)
book = graphene.Field(BookType)
@classmethod
def mutate(cls, root, info, input):
book = Book()
book.title = input.title
book.author = input.author
book.pages = input.pages
book.price = input.price
book.quantity = input.quantity
book.description = input.description
book.status = input.status
book.save()
return CreateBook(book=book)
class UpdateBook(graphene.Mutation):
class Arguments:
input = BookInput(required=True)
id = graphene.ID()
book = graphene.Field(BookType)
@classmethod
def mutate(cls, root, info, input, id):
book = Book.objects.get(pk=id)
book.name = input.name
book.description = input.description
book.price = decimal.Decimal(input.price)
book.quantity = input.quantity
book.save()
return UpdateBook(book=book)
class CreateGrocery(graphene.Mutation):
class Arguments:
input = GroceryInput(required=True)
grocery = graphene.Field(GroceryType)
category = graphene.Field(CategoryType)
@classmethod
def mutate(cls, root, info, input):
if input is None:
return CreateGrocery(grocery=None)
grocery = Grocery.objects.create(**input)
return CreateGrocery(grocery=grocery)
class Mutation(graphene.ObjectType):
update_category = UpdateCategory.Field()
create_category = CreateCategory.Field()
create_book = CreateBook.Field()
update_book = UpdateBook.Field()
create_grocery = CreateGrocery.Field()
# file_upload = FileUpload.Field()
schema = graphene.Schema(query=Query, mutation=Mutation)
Models that i have created are as follows
from django.db import models
# Create your models here.
class Category(models.Model):
title = models.CharField(max_length=255)
class Meta:
verbose_name_plural = 'Categories'
def __str__(self):
return self.title
class Book(models.Model):
title = models.CharField(max_length=150)
author = models.CharField(max_length=100, default='John Doe')
isbn = models.CharField(max_length=13)
pages = models.IntegerField()
price = models.IntegerField()
quantity = models.IntegerField()
description = models.TextField()
status = models.BooleanField()
date_created = models.DateField(auto_now_add=True)
class Meta:
ordering = ['-date_created']
def __str__(self):
return self.title
class Grocery(models.Model):
product_tag = models.CharField(max_length=10)
name = models.CharField(max_length=100)
category = models.ForeignKey(Category, related_name='grocery', on_delete=models.CASCADE)
price = models.IntegerField()
quantity = models.IntegerField()
imageurl = models.URLField()
status = models.BooleanField()
date_created = models.DateField(auto_now_add=True)
class Meta:
ordering = ['-date_created']
def __str__(self):
return self.name
class FileUpload(models.Model):
file = models.FileField()
Now i want for each category i should be able to list groceries as well as shown below
{
"data": {
"categories": [
{
"id": "1",
"title": "Plastic"
"grocery" :{
#grocery related fields
}
},
]
}
}
Currently when i try i get the following error
Request that i'm using currently
query listCategories{
categories {
id
title
grocery {
name
}
}
}
Error Response which i'm getting
{
"errors": [
{
"message": "Expected Iterable, but did not find one for field 'CategoryType.grocery'.",
"locations": [
{
"line": 56,
"column": 7
}
],
"path": [
"categories",
0,
"grocery"
]
},
{
"message": "Expected Iterable, but did not find one for field 'CategoryType.grocery'.",
"locations": [
{
"line": 56,
"column": 7
}
],
"path": [
"categories",
1,
"grocery"
]
},
{
"message": "Expected Iterable, but did not find one for field 'CategoryType.grocery'.",
"locations": [
{
"line": 56,
"column": 7
}
],
"path": [
"categories",
2,
"grocery"
]
},
{
"message": "Expected Iterable, but did not find one for field 'CategoryType.grocery'.",
"locations": [
{
"line": 56,
"column": 7
}
],
"path": [
"categories",
3,
"grocery"
]
}
],
"data": {
"categories": [
{
"id": "1",
"title": "Plastic",
"grocery": null
},
{
"id": "2",
"title": "Plastic",
"grocery": null
},
{
"id": "3",
"title": "Plastic",
"grocery": null
},
{
"id": "4",
"title": "Plastic",
"grocery": null
}
]
}
}
Use graphene_django.DjangoListField
:
from graphene_django import DjangoListField, DjangoObjectType
class CategoryType(DjangoObjectType):
grocery = DjangoListField(GroceryType)
Alternatively, implement a resolver:
import graphene
from graphene_django import DjangoObjectType
class CategoryType(DjangoObjectType):
grocery = graphene.List(GroceryType)
@staticmethod
def resolve_grocery(parent, info):
return parent.grocery.all()