Как представить результат запроса, состоящий из атрибутов 3 связанных моделей, в одном сериализаторе? Django REST API
Хочу сделать вывод представления WheelsSupplesView в json, которое содержит queriset. Мне необходимо отобразить поля из разных моделей в одном выводе. Проблема в том, что данные модели связаны следующим образом:
StockType - Stock: 1-M
StockType - WheelsCharacter: 1-1
Модель WheelsCharacter содержит поле stock_type, которое является внешним ключом на модель StockType (OneToOneField).
Модель Stock содержит поле stock_type, которое является внешним ключом на модель StockType (ForeignKey).
Мне нужно написать сериализатор, который смог бы преобразовать результат запроса к следующему виду:
[
{
"stock_type": "Текст",
"model_name": "Текст",
"diameter": 50
"width": 20
"profile": 30
"run_flat": True
"marking_c": True
"grade": 5.0
"date": "2022-02-27",
"number": 4
},
{
"stock_type": "Текст2",
"date": "2022-02-27",
"model_name": "Текст2",
"diameter": 51
"width": 23
"profile": 35
"run_flat": True
"marking_c": False
"grade": 4.0
"number": 3
}]
Пробовал различные варианты написания сериализаторов, а также способов получения данных путем смены основной модели в запросе, но так ничего и не помогло. Чаще всего ошибки сводятся к тому, что в выбранной в queriset модели отсутствует поле из другой модели.
views.py
class WheelsSupplesView(APIView):
def get(self, request):
queriset = Stock.objects.prefetch_related('stock_type').select_related('stock_type__wheelscharacter').filter(Q(stock_type__item_type='Шина') | Q(stock_type__item_type='Колесо'))
serializer = WheelsCharacterSupplesSerializer(queriset, many=True)
return Response(serializer.data)
models.py
class StockType(models.Model):
item_type = models.CharField(max_length=150, verbose_name='Тип')
name = models.CharField(max_length=150, verbose_name='Название')
default_price = models.IntegerField(verbose_name='Закупочная цена')
sale_price = models.IntegerField(verbose_name='Стоимость продажи', null=True, blank=True)
def __str__(self):
return '%s %s' % (self.item_type, self.name)
class Meta:
verbose_name = 'Товар'
verbose_name_plural = 'Товар'
ordering = ['name']
class Stock(models.Model):
date = models.DateField(auto_now_add=True, verbose_name='Дата операции')
number = models.IntegerField(verbose_name='Количество')
stock_type = models.ForeignKey('StockType', on_delete=models.PROTECT, verbose_name='Товар')
def __str__(self):
return '%s %s %s' % (self.stock_type, self.date, self.number)
class Meta:
verbose_name = 'Запись со склада'
verbose_name_plural = 'Записи со склада'
ordering = ['date']
class WheelsCharacter(models.Model):
model_name = models.CharField(max_length=150, verbose_name='Модель')
diameter = models.IntegerField(verbose_name='Диаметр')
width = models.IntegerField(verbose_name='Ширина')
profile = models.IntegerField(verbose_name='Профиль')
run_flat = models.BooleanField(verbose_name='Run flat')
marking_c = models.BooleanField(verbose_name='Маркировка С')
status = models.CharField(max_length=150, verbose_name='Статус')
stock_type = models.OneToOneField('StockType', on_delete=models.PROTECT, verbose_name='Товар')
winter = models.BooleanField(verbose_name='Зима')
grade = models.FloatField(verbose_name='Оценка')
def __str__(self):
return '%s %s %s' % (self.model_name, self.diameter, self.status)
class Meta:
verbose_name = 'Шина'
verbose_name_plural = 'Шины'
ordering = ['model_name']
Получилось немного иначе, но работает Вывод выглядит следующим образом:
"wheels": {
"stock_type": "Текст",
"model_name": "Текст",
"diameter": 14,
"width": 185,
"profile": 65,
"run_flat": false,
"marking_c": false,
"grade": 3.5,
"winter": true,
"status": "Статус"
},
"stock": [
{
"date": "2022-02-27",
"number": 4
},
{
"date": "2022-03-05",
"number": 1
}
]
views.py
class WheelsSupplesView(APIView):
def get(self, request):
queriset = StockType.objects.prefetch_related('stock').select_related('wheels').filter(Q(item_type='Шина') | Q(item_type='Колесо'))
serializer = WheelsCharacterSupplesSerializer(queriset, many=True)
return Response(serializer.data)
serializers.py
class WheelsCharacterSupplesSerializer(serializers.ModelSerializer):
class LocalStockSerializer(serializers.ModelSerializer):
class Meta:
model = Stock
fields = ('date', 'number')
wheels = WheelsCharacterSerializer()
stock = LocalStockSerializer(many=True)
class Meta:
model = StockType
fields = ('wheels', 'stock')
depth = 1
models.py
class StockType(models.Model):
item_type = models.CharField(max_length=150, verbose_name='Тип')
name = models.CharField(max_length=150, verbose_name='Название')
default_price = models.IntegerField(verbose_name='Закупочная цена')
sale_price = models.IntegerField(verbose_name='Стоимость продажи', null=True, blank=True)
def __str__(self):
return '%s %s' % (self.item_type, self.name)
class Meta:
verbose_name = 'Товар'
verbose_name_plural = 'Товар'
ordering = ['name']
class Stock(models.Model):
date = models.DateField(auto_now_add=True, verbose_name='Дата операции')
number = models.IntegerField(verbose_name='Количество')
stock_type = models.ForeignKey('StockType', related_name='stock', on_delete=models.PROTECT, verbose_name='Товар')
def __str__(self):
return '%s %s %s' % (self.stock_type, self.date, self.number)
class Meta:
verbose_name = 'Склад'
verbose_name_plural = 'Склад'
ordering = ['date']
class WheelsCharacter(models.Model):
model_name = models.CharField(max_length=150, verbose_name='Модель')
diameter = models.IntegerField(verbose_name='Диаметр')
width = models.IntegerField(verbose_name='Ширина')
profile = models.IntegerField(verbose_name='Профиль')
run_flat = models.BooleanField(verbose_name='Run flat')
marking_c = models.BooleanField(verbose_name='Маркировка С')
status = models.CharField(max_length=150, verbose_name='Статус')
stock_type = models.OneToOneField('StockType', related_name='wheels', on_delete=models.PROTECT, verbose_name='Товар')
winter = models.BooleanField(verbose_name='Зима')
grade = models.FloatField(verbose_name='Оценка')
def __str__(self):
return '%s %s %s' % (self.model_name, self.diameter, self.status)
class Meta:
verbose_name = 'Шина/Колесо'
verbose_name_plural = 'Шины/Колеса'
ordering = ['model_name']