Как я могу сериализовать одну ко многим моделям в Django Rest?
Итак, у меня есть две модели Invoice и Items. Счета-фактуры могут иметь много элементов. Вот как они определяются:
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'
Я хочу реализовать две конечные точки API, одну для получения списка счетов, а другую для получения конкретного счета с его элементами.
Например, /api/ вернет все счета-фактуры
[
{
"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"
}
]
и теперь, если я запрошу конкретный счет с его элементами, например /api/a8aeb5a8-5498-40fd-9b4f-bb09a7057c71 должен быть получен следующий ответ
{
"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
}
]
}
Вот сериализаторы:
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'
]
Виды:
@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)
Первая конечная точка API работает как ожидалось, но мне нужна помощь в реализации второй. Как я могу получить этот вложенный ответ?
Спасибо.
Ваше related_name счета в модели Items неверно, вы должны установить его как "items", потому что вы собираетесь вызывать его из модели Invoice таким образом.
Также, если вы хотите иметь другой ответ от второго API, вы должны изменить его, чтобы использовать другой сериализатор, а не 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)
Я изменил имя сериализатора на InvoiceDetailSerializer, поэтому вам нужно создать новый сериализатор:
class InvoiceDetailSerializer(serializers.ModelSerializer):
items = ItemSerializer(many=True, read_only=True)
class Meta:
model = Invoice
fields = [
'invoice_id',
'items'
]
вы можете попробовать что-то вроде этого. (не проверено)
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)