Multiple lookup_fields in APIView?

Hi, I have got a model which has both pk field(integer) and uuid field(v4).
I need to get the object by both pk and uuid.
But the problem is I don’t want to create two separate endpoints for pk and uuid respectively.
Instead, I’d rather have two lookup_fields and validate what the given lookup_field is on get_object method by overriding it.
Looking at the get_object method of APIView, it’s just a string.
So I am wondering how I can have two lookup_fields so I can still use a single endpoint with either pk or uuid.
For now, I cannot use uuid as it requires id as integer only.

Thanks in advance

I did something similar a while ago. I had QrCode objects, which could be looked up by either a short_uuid (string), or uuid (uuidv4). I implemented to get_object() method as follows:

    def get_object(self, queryset=None):
        uid = self.kwargs.get(self.pk_url_kwarg)
        try:
            try:
                # This will raise a ValueError depending if uid is a valid uuidv4 string
                short_uuid = uuid.UUID(uid)
                # short_uuid is a valid uuid object
                return self.queryset.get(uuid=short_uuid)
            except ValueError:
                # short_uuid is not a valid uuid object
                return self.queryset.get(short_uuid=uid)
        except QRCode.DoesNotExist or ValidationError:
            raise Http404

I implemented it similar way already but the problem is since pk is integer type, on my Swagger UI, the input field requires integer only, so I cannot input uuid string.
That’s the problem.
I am not sure where that validation for integer type in implemented.

I know it’s “hacky” but you could try changing the pk url param type to string, then cast the str to a uuid object or an integer, depending on what it is. This way swagger will accept string as well, and type conversion is done behind the scenes

Where can I change the pk url param type?
Could you please let me know more details?

In your urls.py

now you have something like:

path('your/route/here/<int:pk>/', your_view)

change it to:

path('your/route/here/<str:pk>/', your_view)

Aha, yes!

That makes sense

1 Like