Filtering Django ModelViewSet List From Url Parameter
Asked Answered
M

3

7

Hi I have a model like:

class Appointment(models.Model):
    hospital = models.ForeignKey(Hospital, on_delete=models.CASCADE)
    patient = models.ForeignKey(Patient, on_delete=models.CASCADE)

My View looks like:

class AppointmentViewSet(viewsets.ModelViewSet):
    queryset = Appointment.objects.all()
    serializer_class = AppointmentSerializer

In my urls:

router.register(r'appointments', AppointmentViewSet)

Now I want to filter the list of appointments by some patient id. This id should be given by the requester through url. I'm thinking about using kwargs to catch it. But I have no idea how to do it. I know I have to override the list method.

def list(self, request, *args, **kwargs):
    # what do I write here? so that the queryset would be filtered by patient id sent through the url? 

How do I customize the url and/or the view to accommodate the patient id parameter? I just want to modify the list request, all other actions(create, details, destroy) should be handled by the modelviewset's default behavior.

Thanks.

Montemontefiascone answered 14/6, 2017 at 4:31 Comment(3)
After providing the id, you need an instance or a list?Mountbatten
a list. list of appointments.Montemontefiascone
You're passing the id from the url, right?Mountbatten
M
4

Here is how I ended up doing it:

I've added an url entry like this:

url(r'appointments/ofpatient/(?P<patient>\d+)', AppointmentViewSet.as_view({'get': 'list'})),

Which I can call from the browser as:

http://localhost:8000/appointments/ofpatient/6

and in view:

def list(self, request, patient=None):
    if patient:
        patient = Patient.active.filter(id=patient)
        appts = Appointment.active.order_by('appt_time').filter(patient=patient)
        serializer = self.get_serializer(appts, many=True)
        return Response(serializer.data)
    else:
        appts = Appointment.active.order_by('appt_time')
        serializer = self.get_serializer(appts, many=True)
        return Response(serializer.data)

In this way, the /appointments url is also preserved.

Montemontefiascone answered 14/6, 2017 at 5:34 Comment(0)
M
2

You can define a custom method to handle the routing for the url with the patient id:

from rest_framework.decorators import list_route
rom rest_framework.response import Response

class AppointmentViewSet(viewsets.ModelViewSet):
    ...

    @list_route()
    def patient_appointments(self, request, id=None):
        serializer = self.get_serializer(queryset.filter(patient_id=id), many=True)
        return Response(serializer.data)

The list_route decorator marks your method as requiring routing.

Update:

You can manually register the url as:

url(r'^(?P<id>[0-9]+)/appointments/$', AppointmentViewSet.as_view({'get': 'patient_appointments'}), name='patient-appointments')
Mountbatten answered 14/6, 2017 at 4:56 Comment(5)
how should my request url look like in this case?Montemontefiascone
you mean <app_name>/appointments/<id>? but that has a problem. this url format is supposed to provide the 'detail' view. how would i pass the patient id through it?Montemontefiascone
It does not clash with the detail view, there is a trailing appointments. I suggest you try this out first before second guessing. If you have any errors/problems, report back.Mountbatten
i tried with this url: url(r'(\d+)/appointments/', AppointmentViewSet.as_view({'get': 'list'})), but it ignores the id at the beginning and gives me all the recordsMontemontefiascone
I've updated the answer. The view should be constructed from the patient_appointments method, not list, but I'm not sure why you want to write the url conf yourself.Mountbatten
A
0

try

if(self.request.get('pid')):
    pk = self.request.get('pid')
    all_appointment = Appointment.objects.filter(patient__pk=pk)

for patients url would be appoinment/?pid=9

and for appointment details appoinment/9

Adalard answered 14/6, 2017 at 4:41 Comment(9)
what do i write in the url then? how should i issue the request?Montemontefiascone
as you said you want to pass id, so appointment/1 or 2 anything like thatAdalard
but as i'm using modelviewset, any id after appointments is treated as the id of the appointment record itself, not the patient id.Montemontefiascone
ok so you can do one thing you can get the patients id from the appointment id and then can filter according to that users idAdalard
actually i'm trying to retrieve all the appointments a patient has. so the patient id must be obtained first.Montemontefiascone
tell me a thing bro if you pass like patient id 9 and get the pk and filter the objects like the one i gave, it will give you the appointment list of that user, the pk will be treated as appointment or something else doesnt matter, you will filter the query according to user id, so you dont need to think about itAdalard
if i understood correctly you are suggesting to do something like: servername/appointments/9. right? then treat that 9 as the patient id? but the same link has to work for appointment details itself too. for example: if an appointment record has id 9, then it has to be called like that.Montemontefiascone
apparently, it ignores the 'pid' part and calls the appointment details view.Montemontefiascone
sorry bro, made a little mistake, try the edited ansAdalard

© 2022 - 2024 — McMap. All rights reserved.