Reputation: 453
I've read the docs on serializers and Googled (a lot), but haven't found the exact answer I'm looking for.
I'm trying to write a generic "archive" process (class) to handle changing a status field to "archived" for any model instance based on what's passed in the URL.
For example:
example.com/archive/clients.client/123
That would change the status to "archived" for ID 123 of the Client model.
Here's my URLconf:
path('archive/<model>/<int:pk>/', Archive.as_view()),
And here's my view class. I had first got this working for a specific model, so I've copied that code and am trying to adapt it to be more generic. Also, I know this needs more bulletproofing, but I'm trying to show the simplest version of my code.
class Archive(RetrieveAPIView):
def retrieve(self, request, model=None, pk=None, *args, **kwargs):
app, model_name = model.split('.')
get_model = apps.get_model
model = get_model(app, model_name)
self.queryset = model.objects.all()
instance = self.get_object(pk=pk)
if instance.status == 'archived':
return APIMessage('That item has already been archived.', message_code='already_archived')
setattr(instance, 'status', 'archived')
instance.save()
serializer = self.get_serializer(instance)
return Response(serializer.data)
When I try to run this as is, I get:
'Archive' should either include a `serializer_class` attribute, or override the `get_serializer_class()` method.
And that was pretty much what I expected, but I don't have anything I could actually set serializer_class to from the outset, and if I set it to None, I get the same error. Of course, I already knew about get_serializer_class, but that's where I would need something like get_model, except that it would return the serializer class based on the model I've determined from the URL.
Upvotes: 2
Views: 479
Reputation: 5958
Since you are updating your resource, you should use a view that supports PUT/PATCH requests, which are exactly for updating properties by design (in HTTP).
RetrieveAPIView
is:
Used for read-only endpoints to represent a single model instance
In your specific case, you should use the most generic APIView
from DRF, since it does not require defining serializer_class
and other properties that you wouldn't use.
from rest_framework.views import APIView
class Archive(APIView):
def put(self, request, model=None, pk=None, *args, **kwargs):
app, model_name = model.split('.')
get_model = apps.get_model
model = get_model(app, model_name)
self.queryset = model.objects.all()
instance = self.get_object(pk=pk)
if instance.status == 'archived':
return APIMessage('That item has already been archived.', message_code='already_archived')
setattr(instance, 'status', 'archived')
instance.save()
return Response({'success': True})
def patch(self, request, *args, **kwargs):
return self.put(request, *args, **kwargs)
This should do the work. I've also mapped the PATCH method to perform the PUT logic, which means you can use both of these HTTP methods to update your model instance.
Now your view is as generic as possible with the help of APIView.
Upvotes: 3