I'm using Django 2.2.x and djangorestframework 3.11.1.
Inside a ModelViewSet I had to override the .create() method to customize the default behavior.
Sometimes I need to really create a new model instance upon receiving a POST http request, sometimes I need to .update() an existing instance but all I can receive are POST requests (the source is an external server I don't have control over). I know it's a bad idea to conflate create and update...
The problem is that whenever my update-instead-of-create logic kicks in, I get the following error:
AssertionError: Expected view EventViewSet to be called with a URL keyword argument named "pk". Fix your URL conf, or set the `.lookup_field` attribute on the view correctly
I tried two other ways to work around this issue:
Event.objects.update()
but I realized this is a no go for me, since I overrode the get_serializer()
method to alter the raw json payload coming from the external server and I don't want to duplicate the customization logic here..update_or_create()
method but the filtering was a bit too complex and I still have the same problem of the first bulletMy view:
class EventViewSet(LoggingMixin, viewsets.ModelViewSet):
"""this is our main api. It allows an event to be created, retrieved, updated and
deleted
"""
def create(self, request, *args, **kwargs):
# first off, we need to find if there is one event NOT closed for the same
# customer, device name and ip address
filters = Q(customer = request.data["Customer"]) & \
~Q(status = "CLOSED") & \
Q(device_name = request.data["DisplayName"]) & \
Q(ip_address = request.data["Address"])
query_set = Event.objects.filter(filters)
if len(query_set) == 1:
# it means there is already an event still NOT closed for that customer/device,
# so we update the existing event
# we need to specify which event we are updating
kwargs["pk"] = str(query_set[0].pk)
return super().update(request, *args, **kwargs)
elif len(query_set) == 0:
# it means there is no open event for that customer/device, hence we need to create it
return super().create(request, *args, **kwargs)
# per our business logic, we should never be here, because there should never be more
# than 1 *open* event for the same customer/device.
else:
pass
I am not sure how to fix the AssertionError
returned.
Thanks
Found a solution.
I was very close...what I was missing was:
self.kwargs["pk"] = str(query_set[0].pk)
instead of kwargs["pk"] = str(query_set[0].pk)
As far as I understood, this piece of information is then used inside the ModelViewSet get_object()
method to retrieve the actual instance to update.