Django Rest Framework-如何使用相关模型将序列化程序设置为CreateAPIView

时间:2019-09-13 00:56:51

标签: django django-rest-framework

我希望能够将具有相关对象数组的模型发布到模型,但是我不知道该怎么做。现在,我能够从这些关系中检索信息,但是不知道如何发布。

Django == 2.2.4 django-rest-framework == 3.10.3

models.py

from django.db import models
import datetime
from django.utils.html import format_html_join

from collections import Counter

class Cliente(models.Model):
    def __str__(self):
        return self.nome
    nome = models.CharField(max_length=200)

    class Meta:
        ordering = ['id']

class Produto(models.Model):
    def __str__(self):
        return self.nome + ", Preço unitário: " + str(self.preco_unitario)

    nome = models.CharField(max_length=200)
    preco_unitario = models.DecimalField(max_digits=14, decimal_places=2, default=0.00)
    multiplo = models.IntegerField(blank=True, null=True)

class Pedido(models.Model):

    cliente = models.ForeignKey(Cliente, models.DO_NOTHING, null=True, blank=True)
    created = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return '%s' % (self.pedido_id)

    def total(self):
        itens = ItensPedido.objects.filter(pedido=self.pk)
        valor = sum(Counter(item.preco * item.quantidade for item in itens))
        return str(valor)

class ItensPedido(models.Model):

    pedido = models.ForeignKey(Pedido, on_delete=models.CASCADE,related_name='itemspedido', null=True)
    produto = models.ForeignKey(Produto, on_delete=models.CASCADE, null=True)
    preco = models.DecimalField(max_digits=14, decimal_places=2, default=0.00)
    quantidade = models.IntegerField(default=1, null=False)
    created = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.pedido.pedido_id + ": " + self.produto.nome + ", preço: " + str(self.preco) + ", quantidade: " + str(self.quantidade)


然后我有我的serializers.py

from .models import Produto, Cliente, Pedido, ItensPedido
from rest_framework import serializers

class ProdutoSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    nome = serializers.CharField()
    preco_unitario = serializers.DecimalField(min_value=0.01, decimal_places=2, max_digits=None, coerce_to_string=False)
    multiplo = serializers.IntegerField()
    class Meta:
        model=Produto
        fields=('id', 'nome', 'preco_unitario', 'multiplo')

class ClienteSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    nome = serializers.CharField()
    class Meta:
        model=Cliente
        fields=('id', 'nome')

class ItensPedidoSerializer(serializers.ModelSerializer):
    produto = serializers.CharField(source='produto.nome')
    preco = serializers.DecimalField(min_value=0.01, decimal_places=2, max_digits=None, coerce_to_string=False)
    quantidade = serializers.IntegerField()

    class Meta:
        model=ItensPedido
        fields=('produto', 'preco', 'quantidade')

class PedidoSerializer(serializers.ModelSerializer):
    cliente = serializers.CharField(source='cliente.nome')
    produtos = serializers.SerializerMethodField()
    total = serializers.DecimalField(max_digits=None, decimal_places=2, read_only=True, coerce_to_string=False)

    class Meta:
        model=Pedido
        fields=('id', 'cliente', 'total', 'produtos')

    def get_produtos(self, instance):
        items = ItensPedido.objects.filter(pedido=instance)
        return ItensPedidoSerializer(items, many=True).data

    def create(self, validated_data):
        cliente_id = validated_data.pop('cliente')
        produtos_dados = validated_data.pop('produtos')
        pedido = Pedido.objects.create(cliente=cliente_id, **validated_data)
        for produto_dados in produtos_dados:
            ItensPedido.objects.create(pedido=pedido, **produto_dados)
        return pedido


和我的api_views.py

from rest_framework.generics import ListAPIView, CreateAPIView, RetrieveAPIView
from rest_framework.exceptions import ValidationError
from api.serializers import ProdutoSerializer, ClienteSerializer, PedidoSerializer
from api.models import Produto, Cliente, Pedido

class ListaProdutos(ListAPIView):
    queryset = Produto.objects.all()
    serializer_class = ProdutoSerializer

class ListaClientes(ListAPIView):
    queryset = Cliente.objects.all()
    serializer_class = ClienteSerializer

class ListaPedidos(ListAPIView):
    queryset = Pedido.objects.all()
    serializer_class = PedidoSerializer

class ListaPedido(RetrieveAPIView):
    queryset = Pedido.objects.all()
    serializer_class = PedidoSerializer
    lookup_field = 'id'


class CriaPedido(CreateAPIView):
    serializer_class = PedidoSerializer

最近是我的urls.py:

urlpatterns = [
    path('api/v1/produtos/', api.api_views.ListaProdutos.as_view()),
    path('api/v1/clientes/', api.api_views.ListaClientes.as_view()),
    path('api/v1/pedidos/', api.api_views.ListaPedidos.as_view()),
    path('api/v1/pedido/novo', api.api_views.CriaPedido.as_view()),
    path('api/v1/pedido/<int:id>/', api.api_views.ListaPedido.as_view()),
]

一切正常,除了URL的POST方法:'api / v1 / pedido / novo'

要发布才能做什么:

{
            "cliente": 1,
            "produtos":[
                {"produto":1, "preco": 5000.00, "quantidade": 3},
                {"produto":3, "preco": 4000.00, "quantidade": 4}
            ]

        }

我是DRF的新手。感谢您的帮助!


更新-2019-09-13

我更改了一些代码,但是得到了相同的错误。

我创建了另一个序列化器来创建模型:

class CriaPedidoSerializer(PedidoSerializer):
    produtos = ItensPedidoSerializer(many=True)

    def create(self, validated_data):
        produtos_dados = validated_data.pop('produtos')
        pedido = Pedido.objects.create(**validated_data)
        for produto_dados in produtos_dados:
            ItensPedido.objects.create(pedido=pedido, **produto_dados)
        return pedido

在我看来:

class CriaPedido(CreateAPIView):
    serializer_class = CriaPedidoSerializer

    def create(self, request, *args, **kwargs):
        try: 
            cliente = request.data.get('cliente')
            if not cliente:
                raise ValidationError({'cliente': 'É necessário um cliente para realizar um pedido!'})
        except ValueError:
            raise ValidationError({'cliente': 'Deve ser um cliente já cadastrado!'})
        return super().create(self, request, *args, **kwargs)

在DRF界面上,单击OPTIONS,我看到了:

{
    "name": "Cria Pedido",
    "description": "",
    "renders": [
        "application/json",
        "text/html"
    ],
    "parses": [
        "application/json",
        "application/x-www-form-urlencoded",
        "multipart/form-data"
    ],
    "actions": {
        "POST": {
            "id": {
                "type": "integer",
                "required": false,
                "read_only": true,
                "label": "ID"
            },
            "cliente": {
                "type": "field",
                "required": true,
                "read_only": false,
                "label": "Cliente"
            },
            "total": {
                "type": "string",
                "required": false,
                "read_only": true,
                "label": "Total",
                "min_length": 2,
                "max_length": 30
            },
            "produtos": {
                "type": "field",
                "required": true,
                "read_only": false,
                "label": "Produtos",
                "child": {
                    "type": "nested object",
                    "required": true,
                    "read_only": false,
                    "children": {
                        "id": {
                            "type": "integer",
                            "required": false,
                            "read_only": true,
                            "label": "ID"
                        },
                        "produto": {
                            "type": "field",
                            "required": true,
                            "read_only": false,
                            "label": "Produto"
                        },
                        "preco": {
                            "type": "decimal",
                            "required": false,
                            "read_only": false,
                            "label": "Preco"
                        },
                        "quantidade": {
                            "type": "integer",
                            "required": false,
                            "read_only": false,
                            "label": "Quantidade",
                            "min_value": -2147483648,
                            "max_value": 2147483647
                        }
                    }
                }
            }
        }
    }
}

仍然在界面中,在RAW数据中,我得到以下信息:

{
    "cliente": null,
    "produtos": []
}

但是当我尝试发布时:

{
            "cliente": 1,
            "produtos":[
                {"produto":1, "preco": 5000.00, "quantidade": 3},
                {"produto":3, "preco": 4000.00, "quantidade": 4}
            ]

        }

我收到错误消息:

'CriaPedido'对象没有属性'data'

更新-仍然是2019-09-13 -------------------------------------

问题似乎出在我的CriaPedidoSerializer下的create方法上,该方法经过几处更改后就是这样的:

class CriaPedidoSerializer(serializers.ModelSerializer):
    cliente = serializers.PrimaryKeyRelatedField(many=False, queryset=Cliente.objects.all())
    produtos = ItensPedidoSerializer(many=True)

    class Meta:
        model=Pedido
        fields=('id', 'cliente', 'total', 'produtos') 
        ready_only_fields=('id',)

    def create(self, validated_data):
        cliente_dados = validated_data['cliente']
        produtos_dados = validated_data['produtos']
        pedido, created = Pedido.objects.get_or_create(cliente=cliente_dados)
        for produto_dados in produtos_dados:
            ItensPedido.objects.create(pedido=pedido.id, **produto_dados)
        return pedido

仍然出现相同错误:'CriaPedido'对象没有属性'data'

我尝试打印“ validated_data”,但不知道如何。使用pprint不会发生任何事情。

有人吗?好吗?

1 个答案:

答案 0 :(得分:-1)

您需要在视图中设置Many = True。告诉序列化程序您可能正在发送序列化列表。 您需要在视图中覆盖get_serializer。

class YourAPIView(viewsets.ModelViewSet):


    def get_serializer(self, *args, **kwargs):
        if "data" in kwargs:
            data = kwargs["data"]

            # check if many is required
            if isinstance(data, list):
                kwargs["many"] = True

        return super(YourAPIView, self).get_serializer(*args, **kwargs)