Reputation: 13
I'm new to Django and I'm trying to create a route that can be called to retrieve an array of vehicles from my database, but I want the user to be able to provide multiple query params in the url (something like: http://127.0.0.1:8000/vehicles/?year=2020&make=Toyota
). The problem that I have come across is that my vehicle model includes references to foreign keys for the make and the v_model (so named to avoid conflict with the Django "model"). I have a solution that doesn't seem very graceful. The fact that I have three nested conditional statements for each search field makes me suspicious. I tried using "filters.SearchFilter" but I was only able to provide a single value on which to base the search. So a request like this: http://127.0.0.1:8000/vehicles/?search=2020&search=Toyota
would only search for vehicles with a make of "Toyota", ignoring the "year" parameter.
Is there some other way to do this that is cleaner or more "Django-approved"?
Here is my code:
models.py:
class Make(models.Model):
name = models.CharField(max_length=200, unique=True)
def __str__(self):
return self.name
class VModel(models.Model):
name = models.CharField(max_length=200, unique=True)
make = models.ForeignKey(Make, on_delete=models.CASCADE)
def __str__(self):
return self.name
class Vehicle(models.Model):
make = models.ForeignKey(Make, on_delete=models.CASCADE)
v_model = models.ForeignKey(VModel, on_delete=models.CASCADE)
year = models.CharField(max_length=5)
def __str__(self):
return self.year + " " + self.v_model.name
views.py:
Here is my attempt with filters.SearchFilter:
queryset = Vehicle.objects.all()
serializer_class = VehicleSerializer
filter_backends = [filters.SearchFilter]
search_fields = ['year', 'v_model__name','make__name']
And here is my "working" solution that seems hacky:
(NOTE: I am using name__icontains
so that if a user enters "Toyot" it will still get all cars with a make of Toyota).
class VehicleListViewSet(viewsets.ModelViewSet):
serializer_class = VehicleSerializer
queryset = Vehicle.objects.all()
pagination_class = CustomPagination
def get_queryset(self):
qs = super().get_queryset()
selected_make = self.request.query_params.get('make', None)
if selected_make:
try:
found_make = Make.objects.get(name__icontains=selected_make)
except:
return []
if found_make:
if found_make.id:
qs = qs.filter(make=found_make.id)
selected_v_model = self.request.query_params.get('v_model', None)
if selected_v_model:
try:
found_v_model = VModel.objects.get(name__icontains=selected_v_model)
except:
return []
if found_v_model:
if found_v_model.id:
qs = qs.filter(v_model=found_v_model.id)
selected_year = self.request.query_params.get('year', None)
if selected_year:
qs = qs.filter(year=selected_year)
return qs
Upvotes: 1
Views: 3997
Reputation: 1229
You shouldn't be using filters.SearchFilter
. Instead, use a filterset_fields
attribute in your ViewSet, like the example from this section of the documentation.
Your viewset would be like this:
class VehicleListViewSet(viewsets.ModelViewSet):
serializer_class = VehicleSerializer
queryset = Vehicle.objects.all()
pagination_class = CustomPagination
filter_backends = [DjangoFilterBackend]
filterset_fields = ['year', 'v_model__name','make__name']
(Note there's no get_queryset
override) You'll be able to query your API like this:
http://127.0.0.1:8000/vehicles/?year=2020&make__name=Toyota&v_model__name=Corolla
Upvotes: 5