Estoy usando Django como servidor backend y Vue.js para la aplicación de películas front-end.
Tengo un modelo de Ticket
class MovieTicket(models.Model): show = models.ForeignKey(Show) seat = models.ForeignKey(Seat) user = models.ForeignKey(User) purchased_at = models.DateTimeField(default=timezone.now) qrcode = models.ImageField(upload_to='qrcode', blank=True, null=True) qrcode_data = models.CharField(max_length=255, unique=True, blank=True) class Meta: unique_together = ('show', 'seat')Y su serializador relacionado
class MovieTicketSerializer(serializers.ModelSerializer): class Meta: model = MovieTicket fields = '__all__'Para comprar un boleto nuevo, hay una vista asignada a esta URL http://dev.site.com/api/movies/buy-ticket/ :
@api_view(['POST']) @permission_classes([IsAuthenticated]) def buy_ticket(request): serialized = MovieTicketSerializer(data=request.data) if serialized.is_valid(): serialized.save() return Response(serialized.data, status=status.HTTP_201_CREATED) return Response(serialized._errors, status=status.HTTP_400_BAD_REQUEST)Ahora desde el front-end (Vue.js) puedo crear un nuevo boleto de cine:
const formBody = { show: this.$store.state.showSelected.showTime.id, user: this.$store.state.user.id, // selectedSeats is an array of seats that have been selected by the user. Here I am passing the first seat object. seat: this.$store.state.selectedSeats[0].seat.id }; this.$http.post("http://dev.site.com/api/movies/buy-ticket/", formBody) .then(function (response) { console.log(response.data); }) .catch(function (response) { console.log(response); }); return;Si el formulario era válido, se creará un nuevo objeto MovieTicket o se mostrarán los errores.
Ahora, supongamos que si el usuario seleccionó varios asientos, puedo recorrer cada matriz de asientos selectedSeats y obtener las identificaciones de los asientos en el lado del cliente. Y publica algo como esto:
{ "purchased_at": null, "qrcode": null, "qrcode_data": "", "show": 11, "seat": [ 106, 219 ], "user": 34 } Pero lo que estoy confundido es cómo puedo pasar múltiples seat.id si el marco de descanso de Django solo acepta un asiento por solicitud y muestra los errores en consecuencia. Es decir, mostrar errores si un boleto está disponible o no, y si está disponible, crear boletos de cine para ese asiento del espectáculo.
Inicie el serializador con many=True
En su implementación, esto es realmente fácil de lograr:
serialized = MovieTicketSerializer(data=request.data, many=True)Los datos no son un solo objeto, sino una matriz de objetos.
Su información sugiere que necesita transformar request.data para crear esos objetos múltiples (todos los mismos datos, solo un número de asiento diferente). ¿Derecha?
de todos modos:
ver: ¿Cómo creo múltiples instancias de modelo con Django Rest Framework?
EDITAR:
aquí la información en el docu drf: http://www.django-rest-framework.org/api-guide/serializers/#dealing-with-multiple-objects
(Recomiendo encarecidamente leer los documentos drf de arriba a abajo y simplemente jugar con ellos, antes de codificar su primera implementación real. Hay muchas formas de usar drf, y conocerlas todas conduce a mejores decisiones)
EDITAR 2 (después de la actualización de la pregunta):
Puede enviar este JSON desde el cliente (ver más abajo) o crear este formato desde el JSON actual que el cliente envía en su buy_ticket(request) antes de llamar a MovieTicketSerializer(...,many=True) :
[ { "purchased_at": null, "qrcode": null, "qrcode_data": "", "show": 11, "seat": 106, "user": 34 }, { "purchased_at": null, "qrcode": null, "qrcode_data": "", "show": 11, "seat": 219, "user": 34 } ]Esta respuesta fue una muy buena solución a este problema:
Simplemente puede sobrescribir el método get_serializer en su APIView y pasar many=True a get_serializer de la vista base de la siguiente manera:
class SomeAPIView(CreateAPIView): queryset = SomeModel.objects.all() serializer_class = SomeSerializer def get_serializer(self, instance=None, data=None, many=False, partial=False): if data is not None: data.is_valid(raise_exception=True) return super(SomeAPIView, self).get_serializer(instance=instance, data=data, many=True, partial=partial) else: return super(SomeAPIView, self).get_serializer(instance=instance, many=True, partial=partial) Como se mencionó en los comentarios de la publicación original, también debe llamar a data.is_valid() en los casos en que se pasa una palabra clave data al serializador.
Puede comprobar el número de asientos en la función de visualización y crear uno o varios billetes:
@api_view(['POST']) @permission_classes([IsAuthenticated]) def buy_ticket(request): # Check if seats is a list if isinstance(request.data['seat'], list): seats = request.data.pop('seat') models = [] for seat in seats: # validate each model with one seat at a time request.data['seat'] = seat serializer = MovieTicketSerializer(data=request.data) serializer.is_valid(raise_exception=True) models.append(serializer) # Save it only after all seats are valid. # To avoid situations when one seat has wrong id # And you already save previous saved_models = [model.save() for model in models] result_serializer = MovieTicketSerializer(saved_models, many=True) # Return list of tickets return Response(result_serializer.data) # Save ticket as usual serializer = MovieTicketSerializer(data=request.data) serializer.is_valid(raise_exception=True) serializer.save() return Response(serializer.data)Funcionará, pero honestamente es un desastre. Puede mover la lógica para la creación de asientos en diferentes funciones para que se vea mejor.