update - ¿Cómo creo varias instancias de modelo con Django Rest Framework?
django rest framework viewset (7)
Me gustaría guardar y actualizar varias instancias usando el Django Rest Framework con una llamada API. Por ejemplo, digamos que tengo un modelo de "Aula" que puede tener varios "Maestros". Si quisiera crear varios maestros y luego actualizar todos sus números del aula, ¿cómo lo haría? ¿Tengo que hacer una llamada API para cada maestro?
Sé que actualmente no podemos guardar modelos anidados, pero me gustaría saber si podemos guardarlo en el nivel de profesor. ¡Gracias!
Aquí hay otra solución, no es necesario que anule el método __init__
serializadores. Simplemente anule el método ''create''
su vista (ModelViewSet). Observe many=isinstance(request.data,list)
. Aquí many=True
cuando envía una matriz de objetos para crear, y False
cuando envía solo uno. ¡De esta manera, puedes guardar tanto un artículo como una lista!
from rest_framework import status
from rest_framework.response import Response
class ThingViewSet(viewsets.ModelViewSet):
"""This view snippet provides both list and item create functionality."""
#I took the liberty to change the model to queryset
queryset = Thing.objects.all()
serializer_class = ThingSerializer
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data, many=isinstance(request.data,list))
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
Creo que lo mejor para respetar la arquitectura propuesta del framework será crear una mezcla como esta:
class CreateListModelMixin(object):
def create(self, request, *args, **kwargs):
"""
Create a list of model instances if a list is provides or a
single model instance otherwise.
"""
data = request.data
if isinstance(data, list):
serializer = self.get_serializer(data=request.data, many=True)
else:
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED,
headers=headers)
Entonces puede anular CreateModelMixin de ModelViewSet de esta manera:
class <MyModel>ViewSet(CreateListModelMixin, viewsets.ModelViewSet):
...
...
Ahora en el cliente puedes trabajar así:
var things = [
{''loads'':''foo'',''of'':''bar'',''fields'':''buzz''},
{''loads'':''fizz'',''of'':''bazz'',''fields'':''errrrm''}
]
thingClientResource.post(things)
o
var thing = {
''loads'':''foo'',''of'':''bar'',''fields'':''buzz''
}
thingClientResource.post(thing)
EDITAR:
Como sugiere Roger Collins en su respuesta, es más inteligente sobrescribir el método get_serializer que ''crear''.
La página de Vistas genéricas en la documentación de Django REST Framework indica que la vista genérica ListCreateAPIView se utiliza para que los puntos finales de lectura y escritura representen una colección de instancias modelo.
Ahí es donde empezaría a buscar (y lo voy a hacer en realidad, ya que también necesitaremos esta funcionalidad en nuestro proyecto pronto).
Tenga en cuenta también que los examples en la página Vistas genéricas usan ListCreateAPIView
.
Llegué a una conclusión similar a la de Daniel Albarral, pero aquí hay una solución más sucinta:
class CreateListModelMixin(object):
def get_serializer(self, *args, **kwargs):
""" if an array is passed, set serializer to many """
if isinstance(kwargs.get(''data'', {}), list):
kwargs[''many''] = True
return super(CreateListModelMixin, self).get_serializer(*args, **kwargs)
No pude entender cómo obtener la solicitud. DATA para convertir de un diccionario a una matriz, lo cual era un límite en mi capacidad para que la solución de Tom Manterfield funcionara. Aquí está mi solución:
class ThingSerializer(serializers.ModelSerializer):
def __init__(self, *args, **kwargs):
many = kwargs.pop(''many'', True)
super(ThingSerializer, self).__init__(many=many, *args, **kwargs)
class Meta:
model = Thing
fields = (''loads'', ''of'', ''fields'', )
class ThingViewSet(mixins.CreateModelMixin, viewsets.GenericViewSet ):
queryset = myModels/
.Thing/
.objects/
.all()
serializer_class = ThingSerializer
def create(self, request, *args, **kwargs):
self.user = request.user
listOfThings = request.DATA[''things'']
serializer = self.get_serializer(data=listOfThings, files=request.FILES, many=True)
if serializer.is_valid():
serializer.save()
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED,
headers=headers)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Y luego ejecuto el equivalente de esto en el cliente:
var things = {
"things":[
{''loads'':''foo'',''of'':''bar'',''fields'':''buzz''},
{''loads'':''fizz'',''of'':''bazz'',''fields'':''errrrm''}]
}
thingClientResource.post(things)
Sé que esto fue preguntado hace un tiempo pero lo encontré al tratar de resolver esto yo mismo.
Resulta que si pasa many=True
al crear una instancia de la clase de serializador para un modelo, puede aceptar múltiples objetos.
Esto se menciona here en los documentos de marco de descanso de django
Para mi caso, mi vista se veía así:
class ThingViewSet(viewsets.ModelViewSet):
"""This view provides list, detail, create, retrieve, update
and destroy actions for Things."""
model = Thing
serializer_class = ThingSerializer
Realmente no quería escribir una carga de repetición solo para tener control directo sobre la creación de instancias del serializador y pasar many=True
, así que en mi clase de serializador __init__
el __init__
en __init__
lugar:
class ThingSerializer(serializers.ModelSerializer):
def __init__(self, *args, **kwargs):
many = kwargs.pop(''many'', True)
super(ThingSerializer, self).__init__(many=many, *args, **kwargs)
class Meta:
model = Thing
fields = (''loads'', ''of'', ''fields'', )
Publicar datos en la URL de la lista para esta vista en el formato:
[
{''loads'':''foo'',''of'':''bar'',''fields'':''buzz''},
{''loads'':''fizz'',''of'':''bazz'',''fields'':''errrrm''}
]
Creó dos recursos con esos detalles. Lo cual fue agradable
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)