update - django rest framework vs django




Creando múltiples objetos con una solicitud en Django y Django Rest Framework (4)

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.

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.


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.


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)




django-rest-framework