serialize - Creando múltiples objetos con una solicitud en Django y Django Rest Framework
django rest framework serialize queryset (5)
Estoy usando Django como servidor back-end y Vue.js para la aplicación de película de la interfaz.
Tengo un modelo de Boleto
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 nuevo Ticket hay una vista que se asigna 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 ticket de película:
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, esto creará un nuevo objeto MovieTicket, o bien mostrará el error / s.
Ahora, supongamos que si el usuario selecciona varios asientos, puedo recorrer cada conjunto selectedSeats
asientos y obtener los identificadores del asiento en el lado del cliente. Y publique 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 Django rest framework solo acepta un asiento por solicitud y muestra los errores en consecuencia? Es decir, se muestran errores de visualización si un ticket está disponible o no, y si está disponible, cree tickets de cine para ese asiento.
Si desea que el usuario pueda seleccionar varios asientos para un boleto, probablemente sea una buena idea eliminar el mapeo MovieTicket
de Seat
y MovieTicket
, y crear una relación de muchos a uno. al igual que:
Serializadores:
class SeatSerializer(serializers.ModelSerializer):
class Meta:
model = Seat
class MovieTicketSerializer(serializers.ModelSerializer):
seats = SeatSerializer(many=True)
class Meta:
model = MovieTicket
fields = ''__all__''
def create(self, vlaidated_data):
seats = validated_data.pop(''seats'')
instance = MovieTicket.objects.create(
**validated_data)
for seat in seats:
Seat.objects.create(
movieticket=instance, **seats)
return instance
Y el Modelo debería verse así:
class MovieTicket(models.Model):
show = models.ForeignKey(Show)
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 Seat(models.Model):
movieticket = ForeignKey(
MovieTicket, related_name="movieticket")
# ... other fields.
Esto le permitiría pasar una serie de ''asientos'' en la solicitud.
Puede verificar el número de asientos en la función de vista y crear uno o más tickets:
@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)
return Response(serializer.data)
Funcionará, pero sinceramente es un desastre. Puede mover la lógica para la creación de asientos en diferentes funciones, se verá mejor.
Si no le importa agregar otra aplicación a su proyecto django, puede probar con django-rest-framework-bulk , si no puede verificar el código y ver cómo se implementó.
Si usa esta aplicación, podrá realizar operaciones de creación masiva, enviando una lista de elementos en su solicitud POST.
p.ej:
[{''name'': ''Jane''}, {''name'': ''John''}, {''name'': ''Johny''}]
Inicia 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.
Sus informaciones sugieren que debe transformar request.data para hacer esos objetos múltiples (todos los datos son solo números de asiento diferentes). ¿Derecha?
de todos modos:
ver: ¿Cómo creo varias instancias de modelo con Django Rest Framework?
EDITAR:
aquí la información en el drf docu: http://www.django-rest-framework.org/api-guide/serializers/#dealing-with-multiple-objects
(sugiero leer los documentos drf de arriba a abajo y jugar con ellos, antes de codificar su primera implementación real. Hay muchas maneras de usar drf, y conocerlos todos lleva a tomar mejores decisiones)
EDIT 2 (después de la actualización de la pregunta):
Puede enviar este JSON del cliente (consulte a continuación) o crear este formato a partir del 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 sobreescribir el método get_serializer
en su APIView y pasar many=True
en get_serializer
de la vista base así:
class SomeAPIView(CreateAPIView):
queryset = SomeModel.objects.all()
serializer_class = SomeSerializer
def get_serializer(self, instance=None, data=None, many=False, partial=False):
return super(SomeAPIView, self).get_serializer(instance=instance, data=data, many=True, partial=partial)