# Additionally filtering Django map by other criteria with viewsets.py if found in GET value

**URL:** <https://forum.djangoproject.com/t/additionally-filtering-django-map-by-other-criteria-with-viewsets-py-if-found-in-get-value/11582>\
**Category:** Using Django\
**Created:** [January 14, 2022, 12:52pm UTC](https://forum.djangoproject.com/t/additionally-filtering-django-map-by-other-criteria-with-viewsets-py-if-found-in-get-value/11582 "2022-01-14T12:52:39Z")\
**Posts on this page:** 17\
**Page:** 1

<div class="post-metadata">

**Author:** ![chrisjdixon](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/chrisjdixon/32/6463_2.png) [@chrisjdixon](https://forum.djangoproject.com/u/chrisjdixon)\
**Post date:** [January 14, 2022, 12:52pm UTC](https://forum.djangoproject.com/t/additionally-filtering-django-map-by-other-criteria-with-viewsets-py-if-found-in-get-value/11582/1 "2022-01-14T12:52:39Z")

</div>

[Asked over at SO](https://stackoverflow.com/questions/70692390/adding-criteria-to-django-drf-responsive-map-inbboxfilter-with-viewsets-py-not), I’d like to further filter the map made from @pauloxnet 's [great tutorial](https://www.paulox.net/2021/07/19/maps-with-django-part-2-geodjango-postgis-and-leaflet/#adding-the-marker-viewset) by GET values if they’re found, but I don’t know how to do this with viewsets.py / fancy API stuff I don’t understand. Please see SO question for full context.

views.py makes a lot more sense to me and I’ve already done this with another part of my project. Would this be better accomplished by trying to convert this viewsets.py class into something for views.py? Would that even be possible with what we’re doing with api.py and serializers.py?

Also, I’m unsure if this should be asked here or over in some Django Rest Framework forum, so please feel free to tell me to go ask elsewhere!

---

<div class="post-metadata">

**Author:** ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)\
**Post date:** [January 14, 2022, 1:54pm UTC](https://forum.djangoproject.com/t/additionally-filtering-django-map-by-other-criteria-with-viewsets-py-if-found-in-get-value/11582/2 "2022-01-14T13:54:08Z")

</div>

There are a number of people here with various degrees of knowledge of DRF who are certainly willing to try and help.

ViewSets are documented at [Viewsets - Django REST framework](https://www.django-rest-framework.org/api-guide/viewsets/#viewsets). Your viewset class implements one or more of the defined methods - each of which can create querysets with whatever parameters are available.

(It would be a lot easier to explain if we were talking in the context of some actual code. The examples on the referenced docs do show the basic ideas.)

---

<div class="post-metadata">

**Author:** ![chrisjdixon](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/chrisjdixon/32/6463_2.png) [@chrisjdixon](https://forum.djangoproject.com/u/chrisjdixon)\
**Post date:** [January 15, 2022, 6:26am UTC](https://forum.djangoproject.com/t/additionally-filtering-django-map-by-other-criteria-with-viewsets-py-if-found-in-get-value/11582/3 "2022-01-15T06:26:25Z")

</div>

OK cool I’ll share some code and elaborate on what I supplied in the SO question. My (@pauloxnet 's) viewsets.py:

```auto
class MarkerViewSet(viewsets.ReadOnlyModelViewSet):
    """Marker view set."""

    bbox_filter_field = "geom"
    filter_backends = (filters.InBBoxFilter,)
    queryset = Tablename.objects.all()
    serializer_class = MarkerSerializer
    pagination_class = GeoPaginator

```

This works nicely despite me not really understanding it. The response entries are what are found within the bbox supplied in the url we defined in map.js: "const markers\_url = `/api/markers/?in_bbox=${map.getBounds().toBBoxString()}`", which comes in like “/api/markers/?in\_bbox=149.7503471374512,-36.96112442377135,150.1019096374512,-36.90486603587978”. Side note: this is far too many decimals but I’ll figure that out later. All works beautifully.

I’d like to expand on this functionality to also filter by other criteria if they’re found in GET values, but I don’t know how. I’ve already done this for another part of my project with views.py and can easily expand upon this by adding more `qs = qs.filter()`:

```auto
def EgFilterView(request):
    qs = Tablename.objects.all()
    date_min_query = request.GET.get('dmin')

    min_date = 1970

    qs = qs.filter(date__gte=dt.datetime(int(date_min_query), 1, 1, tzinfo=pytz.UTC))

    context = {
        'queryset': qs,
        'dmin': min_date,
    }

    return render(request, "main_page.html", context)

```

I’d like to expand on what viewsets.py is doing with similar ease but I don’t know how. So for example if the `dmin` GET value contains an int corresponding to a year I’d like to filter results to be everything in the bbox and occurring after `dmin`. I think I might be able to by adding to `filter_backends = (filters.InBBoxFilter,)`, but I don’t know where to start.

My interpretation of viewsets.py documentation (ty for the link) is that viewsets.py accomplishes the same as views.py in a more advanced and elegant way but I’m not at that level yet. I’m thinking it might be best to try converting all of this to views.py but I don’t know if that’s even possible. Maybe I’m missing something?

Sorry for the silly questions. Thank you so much for your patience and help!

---

<div class="post-metadata">

**Author:** ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)\
**Post date:** [January 15, 2022, 12:21pm UTC](https://forum.djangoproject.com/t/additionally-filtering-django-map-by-other-criteria-with-viewsets-py-if-found-in-get-value/11582/4 "2022-01-15T12:21:10Z")

</div>

Nope, not silly and you are really close.

I’m not familiar with the rest\_framework\_gis filters, but I wouldn’t believe they would be required in this case.

In your view you have:

> [@chrisjdixon](#):
>
> ```auto
> qs = Tablename.objects.all()
> date_min_query = request.GET.get('dmin')
> qs = qs.filter(date__gte=dt.datetime(int(date_min_query), 1, 1, tzinfo=pytz.UTC))
> 
> ```

You’ve got a queryset (`qs`), that you are modifying in the last line (`qs = qs.filter(...)`)

In your viewset, you have a queryset:

> [@chrisjdixon](#):
>
> ```auto
> queryset = Tablename.objects.all()
> 
> ```

That you then want to modify, in the same manner as you did in your view. (`queryset = queryset.filter(...)`)

It really should be that simple. (The only caveat would be if there’s some unusual and unexpected interaction between the filters package being used and standard queryset construction - which I highly doubt)

---

<div class="post-metadata">

**Author:** ![chrisjdixon](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/chrisjdixon/32/6463_2.png) [@chrisjdixon](https://forum.djangoproject.com/u/chrisjdixon)\
**Post date:** [January 16, 2022, 3:11am UTC](https://forum.djangoproject.com/t/additionally-filtering-django-map-by-other-criteria-with-viewsets-py-if-found-in-get-value/11582/5 "2022-01-16T03:11:27Z")

</div>

Hah, wow! That really is simple once you see it. I feel like an idiot 😆

I’ve added `queryset = queryset.filter(date__gte=dt.datetime(2020, 1, 1, tzinfo=pytz.UTC))` and that’s worked nicely!

There’s a slight complication though: I don’t know how to supply the GET values and that only worked cause I manually supplied `2020`. In views.py I’ve got the request coming in as `EgFilterView(request)` but I can’t seem to supply `request` as an argument of `MarkerViewSet()`. I also don’t understand how viewsets.py seems to have got the bbox values with `filter_backends = (filters.InBBoxFilter,)`, so dunno how I might be able to tweak that.

The request URL seems built in map.js: "const markers\_url = `/api/markers/?in_bbox=${map.getBounds().toBBoxString()}`", so the URL itself doesn’t include the GET values and it seems it might need to. Is there a good way to make JS include all other GET values? I’ve had a search and couldn’t find anything.

Thanks again,

---

<div class="post-metadata">

**Author:** ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)\
**Post date:** [January 16, 2022, 3:45am UTC](https://forum.djangoproject.com/t/additionally-filtering-django-map-by-other-criteria-with-viewsets-py-if-found-in-get-value/11582/6 "2022-01-16T03:45:11Z")

</div>

Something I didn’t pay too much attention to at first is that you’re working with a Class Based View, which works a bit differently than a function-based view.

If you’re not used to working with them, see [Class-based views | Django documentation | Django](https://docs.djangoproject.com/en/4.0/topics/class-based-views/) and [Introduction to class-based views | Django documentation | Django](https://docs.djangoproject.com/en/4.0/topics/class-based-views/intro/).  
Then continue with the DRF-specific documentation at [Views - Django REST framework](https://www.django-rest-framework.org/api-guide/views/#class-based-views) and [Generic views - Django REST framework](https://www.django-rest-framework.org/api-guide/generic-views/#generic-views). (Yes, it’s a lot of reading. Understanding ClassBased Views does take time and effort.)

The Reader’s Digest version is that you actually modify the functionality of a CBV by overriding functions within the class.

For example, the [ReadOnlyModelViewSet](https://www.django-rest-framework.org/api-guide/viewsets/#readonlymodelviewset) class has a [`get_queryset`](https://www.django-rest-framework.org/api-guide/viewsets/#api-reference) method that is what you override to change the queryset on a per-call basis.

Changing the class-based definition as you have here sets the queryset one time - it doesn’t change it for every call. See the docs at [Viewsets - Django REST framework](https://www.django-rest-framework.org/api-guide/viewsets/#modelviewset) for an example of a viewset with a get\_queryset method.

I know that wrapping your head around class-based views can be difficult if you’re not comfortable with class inheritance. In this case, since you’re working with DRF, I’d recommend you become familiar with the [Classy Django REST Framework](https://www.cdrf.co/) site.

> [@chrisjdixon](#):
>
> so the URL itself doesn’t include the GET values and it seems it might need to. Is there a good way to make JS include all other GET values? I’ve had a search and couldn’t find anything.

I’m sorry, I’m not following you at all here. Can you clarify what you’re trying to ask?

---

<div class="post-metadata">

**Author:** ![chrisjdixon](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/chrisjdixon/32/6463_2.png) [@chrisjdixon](https://forum.djangoproject.com/u/chrisjdixon)\
**Post date:** [January 16, 2022, 5:06am UTC](https://forum.djangoproject.com/t/additionally-filtering-django-map-by-other-criteria-with-viewsets-py-if-found-in-get-value/11582/7 "2022-01-16T05:06:58Z")

</div>

Aha you’ve got me very confused now 😅

I’m trying to supply the GET values from other inputs on the page eg `<input type="hidden" name="test", id="test"/>`. They’re supplied with `request` in views.py:

```auto
def EgFilterView(request):
    ...
    date_min_query = request.GET.get('dmin')

```

But in viewsets.py `class MarkerViewSet(viewsets.ReadOnlyModelViewSet)` throws a name error when supplied `MarkerViewSet(...,request)` and I dunno how else to supply it.

Complicating this, the request seems driven by the code in map.js: "const markers\_url = `/api/markers/?in_bbox=${map.getBounds().toBBoxString()}`". I’d like to figure out how to expand on this so that the request also includes all GET values so they can be used when `request` is hopefully supplied to `class MarkerViewSet`.

---

<div class="post-metadata">

**Author:** ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)\
**Post date:** [January 16, 2022, 1:21pm UTC](https://forum.djangoproject.com/t/additionally-filtering-django-map-by-other-criteria-with-viewsets-py-if-found-in-get-value/11582/8 "2022-01-16T13:21:13Z")

</div>

I understand what you’re trying to do. However, you _can’t_ do what you want until you start to use the class-based views properly.

First step - create a `get_queryset` method and move all the code related to making the queryset for this class into it.

Then, either review the previously-referenced docs at [Generic views - Django REST framework](https://www.django-rest-framework.org/api-guide/generic-views/#methods) to see how to access the `request` object within an overridden method, or review the `setup` method in the Classy DRF docs page for the [ReadOnlyModelViewSet](https://www.cdrf.co/3.12/rest_framework.viewsets/ReadOnlyModelViewSet.html) to see where the request is stored in the class.

> [@chrisjdixon](#):
>
> I’d like to figure out how to expand on this so that the request also includes all GET values…

This line of code you’re referencing - is this code in a file you’ve created, or is it part of some 3rd party library?  
At the time that this code is going to execute, how are you going to know what values to supply? Are there other widgets on the page from which you are going to retrieve values?  
How / when are you issuing the GET?  
(I think we’re going to need to see more of the JS to be able to address this.)

---

<div class="post-metadata">

**Author:** ![chrisjdixon](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/chrisjdixon/32/6463_2.png) [@chrisjdixon](https://forum.djangoproject.com/u/chrisjdixon)\
**Post date:** [January 17, 2022, 12:45am UTC](https://forum.djangoproject.com/t/additionally-filtering-django-map-by-other-criteria-with-viewsets-py-if-found-in-get-value/11582/9 "2022-01-17T00:45:23Z")

</div>

> (I think we’re going to need to see more of the JS to be able to address this.)

OK, cool! This JS comes from [this section](https://www.paulox.net/2021/07/19/maps-with-django-part-2-geodjango-postgis-and-leaflet/#rendering-markers-incrementally) of @pauloxnet 's tutorial.

> At the time that this code is going to execute, how are you going to know what values to supply? Are there other widgets on the page from which you are going to retrieve values?

Yes, there are several other inputs on the page which may be populated with GET values that I’d like the query to filter for if the value is supplied.

Simply adding a new input like `<input type="hidden" name="test", id="test"/>` automatically populates the regular request urls with the GET parameter `&test=`, but as the map request is explicitly defined as "const markers\_url = `/api/markers/?in_bbox=${map.getBounds().toBBoxString()}` " I don’t know how to include this input value in the URL. Maybe there’s some JS function which can create a string of all the inputs and values to include in `markers_url`?

> First step - create a `get_queryset` method and move all the code related to making the queryset for this class into it.

I’m kinda lost now… I haven’t worked with methods yet and still don’t fully understand classes. I’ve tried what seemed the simplest step of that by adding `def_queryset()`:

```auto
class MarkerViewSet(viewsets.ReadOnlyModelViewSet):
    ...
    @classmethod
    def get_queryset(self):
        print(self.request)
    ...

```

But this breaks it… am I going about this the wrong way? Should I include this `@classmethod`? Do you mean move the entirety of the class code into this new method?

Thanks again for your patience!

---

<div class="post-metadata">

**Author:** ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)\
**Post date:** [January 17, 2022, 1:00am UTC](https://forum.djangoproject.com/t/additionally-filtering-django-map-by-other-criteria-with-viewsets-py-if-found-in-get-value/11582/10 "2022-01-17T01:00:35Z")

</div>

> [@chrisjdixon](#):
>
> But this breaks it… am I going about this the wrong way? Should I include this `@classmethod` ? Do you mean move the entirety of the class code into this new method?

You move _only_ the code that constructs the queryset into this method.

And no, you don’t use the classmethod decorator.

---

<div class="post-metadata">

**Author:** ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)\
**Post date:** [January 17, 2022, 1:04am UTC](https://forum.djangoproject.com/t/additionally-filtering-django-map-by-other-criteria-with-viewsets-py-if-found-in-get-value/11582/11 "2022-01-17T01:04:57Z")

</div>

> [@chrisjdixon](#):
>
> const markers\_url = `/api/markers/?in_bbox=${map.getBounds().toBBoxString()}`

You’re just building a string. You can concatenate whatever values desired on to this string. How you want to do this depends both upon whatever other JavaScript libraries you’re using and **specifically** how you’re adding these values on the page.

---

<div class="post-metadata">

**Author:** ![chrisjdixon](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/chrisjdixon/32/6463_2.png) [@chrisjdixon](https://forum.djangoproject.com/u/chrisjdixon)\
**Post date:** [January 17, 2022, 10:40am UTC](https://forum.djangoproject.com/t/additionally-filtering-django-map-by-other-criteria-with-viewsets-py-if-found-in-get-value/11582/12 "2022-01-17T10:40:42Z")

</div>

OK, after a few hours (new to JS) I’ve figured out how to build the URL!

```auto
const form = document.querySelector('#theForm');
var object = Object.values(form).reduce((obj,field) => { obj[field.name] = field.value; return obj }, {}); // https://stackoverflow.com/a/47188324
delete object[""];
const markers_url = `/api/markers/?in_bbox=${map.getBounds().toBBoxString()}` + '&' + new URLSearchParams(object).toString()

```

Now only need to feed this URL in and we’re there! I’ll start trying to figure out this method…

---

<div class="post-metadata">

**Author:** ![chrisjdixon](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/chrisjdixon/32/6463_2.png) [@chrisjdixon](https://forum.djangoproject.com/u/chrisjdixon)\
**Post date:** [January 18, 2022, 7:51am UTC](https://forum.djangoproject.com/t/additionally-filtering-django-map-by-other-criteria-with-viewsets-py-if-found-in-get-value/11582/13 "2022-01-18T07:51:07Z")

</div>

OK, I’ve had a good play around with this and have somehow sorta got it with the help of [this SO answer](https://stackoverflow.com/a/48299712/4107349). Moving the code that constructs the queryset into the method (without extra filter discussed above):

```auto
class MarkerViewSet(viewsets.ReadOnlyModelViewSet):
    """Marker view set."""
    bbox_filter_field = "geom"
    filter_backends = (filters.InBBoxFilter,)
    queryset = Tablename.objects.all()

    def get_queryset(self):
        bbox = self.request.query_params.get('in_bbox')
        print(bbox)
        queryset = Tablename.objects.all()
        return queryset

    serializer_class = MarkerSerializer
    pagination_class = GeoPaginator

```

This prints `bbox` which is the GET param we want! So I can do this for other params and use those values to filter `queryset` as discussed above and we’re in business!

Only issue is `queryset = Tablename.objects.all()` and filtering I’ve removed for eg simplicity is duplicated and if I remove either I get “AssertionError: `basename` argument not specified, and could not automatically determine the name from the viewset, as it does not have a `.queryset` attribute” or “AttributeError: ‘NoneType’ object has no attribute ‘filter’”.

So now I don’t understand where the filtering is done or how to remove this duplicated code. Is there something I’ve missed on a more fundamental level?

---

<div class="post-metadata">

**Author:** ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)\
**Post date:** [January 18, 2022, 1:30pm UTC](https://forum.djangoproject.com/t/additionally-filtering-django-map-by-other-criteria-with-viewsets-py-if-found-in-get-value/11582/14 "2022-01-18T13:30:20Z")

</div>

Ok, we’re really getting out of the areas where I have any real knowledge. A cursory scan of the docs at [Filtering - Django REST framework](https://www.django-rest-framework.org/api-guide/filtering/#overriding-the-initial-queryset) lead me to believe you can replace `filter_backends` with `filterset_class` to use that filter in the CBV. (You might need to start reading from earlier in that page - perhaps from the top - for this to make sense.)  
But this is all conjecture based upon what I’m reading and not on any specific knowledge or experience.

---

<div class="post-metadata">

**Author:** ![massover](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/massover/32/1792_2.png) [@massover](https://forum.djangoproject.com/u/massover)\
**Post date:** [January 18, 2022, 4:53pm UTC](https://forum.djangoproject.com/t/additionally-filtering-django-map-by-other-criteria-with-viewsets-py-if-found-in-get-value/11582/15 "2022-01-18T16:53:34Z")

</div>

> So now I don’t understand where the filtering is done or how to remove this duplicated code. Is there something I’ve missed on a more fundamental level?

The filtering happens inside the [filter\_queryset](https://github.com/encode/django-rest-framework/blob/master/rest_framework/generics.py#L140) method. The drf code is very opinionated and object oriented. The [ReadOnlyModelViewSet](https://github.com/encode/django-rest-framework/blob/master/rest_framework/viewsets.py) is made up of two mixins. When you’re making a `list` request, it’s being handled by the `ListModelMixin`’s [list](https://github.com/encode/django-rest-framework/blob/master/rest_framework/mixins.py#L33) method.

To extend the filtering behavior that already exists on the viewset, you can extend the filter\_queryset method

```python
def filter_queryset(self, queryset):
   queryset = super().filter_queryset(queryset)
   extra_field1 = self.request.query_params.get('extra_field1')
   if extra_field1:
      queryset = queryset.filter(extra_field1=extra_field1)
   return queryset

```

If you haven’t done so yet, take the time to go through the [django rest framework tutorial](https://www.django-rest-framework.org/tutorial/quickstart/)

---

<div class="post-metadata">

**Author:** ![chrisjdixon](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/chrisjdixon/32/6463_2.png) [@chrisjdixon](https://forum.djangoproject.com/u/chrisjdixon)\
**Post date:** [January 20, 2022, 2:31am UTC](https://forum.djangoproject.com/t/additionally-filtering-django-map-by-other-criteria-with-viewsets-py-if-found-in-get-value/11582/16 "2022-01-20T02:31:30Z")

</div>

This worked perfectly! Thank you so much!

I don’t fully understand class based views / what “object oriented” is but I now see how defining `filter_queryset()` overrides the default method.

I also updated [my SO thread](https://stackoverflow.com/a/70780155/4107349). Please feel free to correct me if I’ve explained anything inaccurately in my answer.

Thanks again fellas!

---

<div class="post-metadata">

**Author:** ![massover](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/massover/32/1792_2.png) [@massover](https://forum.djangoproject.com/u/massover)\
**Post date:** [January 20, 2022, 3:16am UTC](https://forum.djangoproject.com/t/additionally-filtering-django-map-by-other-criteria-with-viewsets-py-if-found-in-get-value/11582/17 "2022-01-20T03:16:13Z")

</div>

You’re welcome! And your summary in the stack overflow post is great.
