So I have two models Invoice and Items. Invoices can have many Items. Here's how they are defined:
class Invoice(models.Model):
customer_name = models.CharField(max_length = 200, verbose_name='Customer Name')
customer_phone = models.IntegerField(null = True, blank = True, verbose_name='Customer Phone')
customer_address = models.TextField(null = True, blank = True, verbose_name='Customer Address')
invoice_id = models.UUIDField(primary_key = True, unique = True, default=uuid.uuid4, verbose_name='Invoice ID')
invoice_date = models.DateField(auto_now_add=True, verbose_name='Invoice Date')
def __str__(self):
return self.customer_name ' - ' str(self.invoice_id)
class Meta:
verbose_name = 'Invoice'
verbose_name_plural = 'Invoices'
class Items(models.Model):
invoice = models.ForeignKey(Invoice, on_delete = models.CASCADE, related_name='invoice')
item_name = models.CharField(max_length = 200, null = False)
item_quantity = models.IntegerField()
item_price = models.IntegerField()
item_id = models.AutoField(primary_key=True)
def __str__(self):
return self.item_name
class Meta:
verbose_name = 'Items'
verbose_name_plural = 'Items'
I want to implement two API endpoints, one to get the list of invoices, and another to get a specific invoice with its items.
For example, /api/
will return all invoices
[
{
"customer_name": "John Doe",
"customer_phone": 111666,
"customer_address": "Palo Alto, California",
"invoice_id": "a8aeb5a8-5498-40fd-9b4f-bb09a7057c71",
"invoice_date": "2022-05-04"
},
{
"customer_name": "Cassian Green",
"customer_phone": 111000,
"customer_address": "2112 Illinois Avenue",
"invoice_id": "7d7b7878-3ffc-4dd2-a60a-fa207c147860",
"invoice_date": "2022-05-04"
},
{
"customer_name": "Chelsea Davis",
"customer_phone": 666777,
"customer_address": "2260 Coplin Avenue",
"invoice_id": "3dda2054-49d7-49dc-9eba-ddc0fdacfd3b",
"invoice_date": "2022-05-04"
},
{
"customer_name": "Derek Elliott",
"customer_phone": 111100,
"customer_address": "3236 Sundown Lane",
"invoice_id": "da112c0d-aff4-43d3-a465-910cc1483fc5",
"invoice_date": "2022-05-04"
}
]
and now if I request a specific invoice with its items, like /api/a8aeb5a8-5498-40fd-9b4f-bb09a7057c71
should return the following response
{
"invoice_id": "a8aeb5a8-5498-40fd-9b4f-bb09a7057c71",
"items": [
{
"item_name": "PC Cabinet",
"item_quantity": 1,
"item_price": 200
},
{
"item_name": "Motherboard",
"item_quantity": 1,
"item_price": 1000
},
{
"item_name": "PSU",
"item_quantity": 1,
"item_price": 300
}
]
}
Here are the serializers:
class ItemSerializer(serializers.ModelSerializer):
class Meta:
model = Items
fields = [
'item_name',
'item_quantity',
'item_price',
]
class InvoiceSerializer(serializers.ModelSerializer):
items = ItemSerializer(many=True, read_only=True)
class Meta:
model = Invoice
fields = [
'customer_name',
'customer_phone',
'customer_address',
'invoice_id',
'invoice_date',
'items'
]
And the views:
@api_view(['GET',])
def InvoiceList(request):
if request.method == 'GET':
queryset = Invoice.objects.all()
serializer_class = InvoiceSerializer(queryset, many=True)
return Response(serializer_class.data)
@api_view(['GET',])
def InvoiceDetail(request, pk):
if request.method == 'GET':
queryset = list(Items.objects.filter(invoice = pk))
serializer_class = ItemSerializer(instance=queryset, many=True)
return Response(serializer_class.data)
The first API endpoint is working as expected but the I need help with implementing the second one. How can I get that nested response?
Thanks.
CodePudding user response:
Your related_name of invoice in Items model is wrong, you should set it as "items" because you are going to call that from Invoice model like that.
Also if you want to have different response from that second API you should change it to use other serializer than InvoiceList
:
@api_view(['GET',])
def InvoiceDetail(request, pk):
if request.method == 'GET':
queryset = list(Items.objects.filter(invoice = pk))
serializer_class = InvoiceDetailSerializer(instance=queryset, many=True)
return Response(serializer_class.data)
I changed name of serializer to InvoiceDetailSerializer
so you need to create new serializer:
class InvoiceDetailSerializer(serializers.ModelSerializer):
items = ItemSerializer(many=True, read_only=True)
class Meta:
model = Invoice
fields = [
'invoice_id',
'items'
]
CodePudding user response:
you can try something like this. (not tested)
class ItemSerializer(serializers.ModelSerializer):
class Meta:
model = Items
fields = ['item_name', 'item_quantity', 'item_price',]
class InvoiceDetailsSerializer(serializers.ModelSerializer):
items = ItemSerializer(many=True, read_only=True)
class Meta:
model = Invoice
fields = ['id', 'items']
@api_view(['GET',])
def InvoiceDetail(request, pk):
if request.method == 'GET':
invoice = Invoice.objects.get(id=pk)
serializer_class = InvoiceDetailsSerializer(instance=invoice)
return Response(serializer_class.data)