How can I get the url parameter for javascript script

Hello

I will do my best to explain my question

First if you look at this short video

  1. You will see, I can use Leaflet to show my stations, but it show ALL stations and I need to filter by field
  2. At the begining of the video, I selected the first field with the ID 1, the second with the ID 2 and the last with the ID 4.

At the url, you will see map/1/field, map/2/field and map/4/field

Now if you look at the javascript,

  1. I call here at an API
  2. The api will call a viewsets
  3. The viewsets make a query
  4. the viewsets call a serializer
  5. and the serializer return a json (see one line above)
  6. and then with marker_s I can build my leaflet map

All works fine, but my problem I do not know how I can change my query to a new filter as the following

queryset = Stations.objects.filter(station_active=1, map=1, fields_id_field=4)

My problem is how can get the value of 1 or 2 or 4 (remember map/1/field, map/2/field and map/4/field)

My idea is may be here. I could add something like

/api/map/?in_bbox=4 or /api/map/4/

but both generate an error.

Or may be in the viewsets file, I could find a way to get the parameter of my URL (1,2 or 4) and may be change to something like:

queryset = Stations.objects.filter(station_active=1, map=1, fields_id_field={% id_field %})

I really have no idea. Any suggestion? Many thnaks

Just like every other Django-supplied CBV - a viewset is no different here.

The URL args are available in self.kwargs. So if your path in the URL is defined with the parameter <int:some_id>, then your view has access to self.kwargs['some_id'].

Hello

Thanks for your reply, but it’s still not very clear.
That’s correct, my urls.py contains

path('<int:idfield>/field', views.field, name='field'),
Then the following should be : self.kwargs['idfield']

Now where should I have self.kwargs?

I tried in viewsets.py

class MarkerViewSet(viewsets.ReadOnlyModelViewSet):
    """Marker view set."""
    print(self.kwargs['idfield'])
    bbox_filter_field = "location"
    filter_backends = (filters.InBBoxFilter,)
    queryset = Stations.objects.filter(station_active=1, map=1, fields_id_field=self.kwargs['idfield'])
    serializer_class = StationsSerializer

I first add an issue and I had to add, at the top of the field import self and I also tried to have self.kwargs.get('idfield')

None works, and the error I receive is

module ‘self’ has no attribute ‘kwargs’

I just tried this which look interresting

class MarkerViewSet(viewsets.ReadOnlyModelViewSet):
    """Marker view set."""
    #print(self.kwargs.get('idfield'))
    bbox_filter_field = "location"
    filter_backends = (filters.InBBoxFilter,)
    queryset = Stations.objects.filter(station_active=1, map=1)
    serializer_class = StationsSerializer

    def get_queryset(self, **kwargs):
        #field_id = self.kwargs['idfield']
        #print(field_id)
        return Stations.objects.filter(fields_id_field=1)

In the get_queryset, I play to change the value

return Stations.objects.filter(fields_id_field=1)
return Stations.objects.filter(fields_id_field=2)
return Stations.objects.filter(fields_id_field=4)

and on the map, the right station were displayed, nice!!!

But then I uncomment
field_id = self.kwargs[‘idfield’]

class MarkerViewSet(viewsets.ReadOnlyModelViewSet):
    """Marker view set."""
    #print(self.kwargs.get('idfield'))
    bbox_filter_field = "location"
    filter_backends = (filters.InBBoxFilter,)
    queryset = Stations.objects.filter(station_active=1, map=1)
    serializer_class = StationsSerializer

    def get_queryset(self, **kwargs):
        field_id = self.kwargs['idfield']
        #print(field_id)
        return Stations.objects.filter(fields_id_field=field_id)

but the terminal display that error

field_id = self.kwargs['idfield']
KeyError: 'idfield'

but my urls.py conatins this

app_name="map"
urlpatterns = [
    #path('', views.IndexView.as_view(), name='index'),
    path('', views.index, name='index'),
    path('<int:idfield>/field', views.field, name='field'),
    path('<int:idfield>/<int:idstation>/station', views.station, name='station'),
    path('<int:idfield>/<int:idstation>/<int:idsensor>/sensor', views.sensor, name='sensor'),
    path('<int:idfield>/', views.api, name='api'),
]

path('<int:idfield>/field', views.field, name='field'), idfield is not fine?

I think I am close to the solution…

Yep, you’re on the right track.

I would print(self.kwargs) to see what you’re getting in get_queryset.

(Also, when you’re overriding get_queryset, you no longer need the class-level definition for queryset.)

I also don’t understand the relationship between the views you’ve defined in your URLs and this MarkerViewSet class. What is views.field?

class MarkerViewSet(viewsets.ReadOnlyModelViewSet):
    """Marker view set."""
    #print(self.kwargs.get('idfield'))
    bbox_filter_field = "location"
    filter_backends = (filters.InBBoxFilter,)
    queryset = Stations.objects.filter(station_active=1, map=1)
    serializer_class = StationsSerializer

    def get_queryset(self, **kwargs):
        #field_id = self.kwargs['idfield']
        print(self.kwargs)
        print(kwargs)
        return Stations.objects.filter(fields_id_field=1)

print(self.kwargs) print me nothing (empty {} ) but I might have an idea why.

I also don’t understand the relationship between the views you’ve defined in your URLs and this MarkerViewSet class. What is views.field?

It’s not really clear for me. In 10 day, I am going to have a training and I will clarify that.

The field view (in views.py), allow me to get build the menu (fileds and stations), on the left side

But to build the map, I am using Djanog-rest-framework. The viewsets is called by the api (see 2) and not by a click action… Did I understand?

Now if you look at the javascript,

  1. I call here at an API
  2. The api will call a viewsets
  3. The viewsets make a query
  4. the viewsets call a serializer
  5. and the serializer return a json (see one line above)
  6. and then with marker_s I can build my leaflet map

Ok, so what URL being invoked causes MarkerViewSet to be executed?

As I understand, when my page is loaded, the map.js is launched.

At the line 24, the map is initiate
At the line 40, the function render_markers is call and the function load_marker is calling the api. iT should answer to your question, isn’t?
In my urls.py, I have this:urls.py

The API calls the viewsets here

And in my view set, I have to find a way to get the self.kwargs['idfield']

class MarkerViewSet(viewsets.ReadOnlyModelViewSet):
    """Marker view set."""
    #print(self.kwargs.get('idfield'))
    bbox_filter_field = "location"
    filter_backends = (filters.InBBoxFilter,)
    queryset = Stations.objects.filter(station_active=1, map=1)
    serializer_class = StationsSerializer

    def get_queryset(self, **kwargs):
        #field_id = self.kwargs['idfield']
        print(self.kwargs)
        print(kwargs)
        return Stations.objects.filter(fields_id_field=1)

I’m actually looking for the specific URL definition that effectively says “run the MarkerViewSet” view.

So yes, by your last link, your url definition is “/api/map”.

Notice that you’re not passing any parameters in to this url.
You’re actually using the default url router scheme, which applies a different semantic meaning to the structure of a url. (See Routers - Django REST framework)

And at this point, you’ve gone farther with DRF than I have. I know that somehow you want to define your url with the necessary parameter - but I don’t know how to do that in the context of DRF; and your javascript needs to provide that parameter in the url being called.
(My first guess would be to define the url as a regular url and not a DRF router url - but I have no idea whether or not that would work as expected.)

Follow up for clarity - what I’m suggesting is that you add a url to your urls file (working it into your structure however you feel appropriate - this is just an example):
path("api/map/<int:idfield>/", MarkerViewSet.as_view(), name="marker_view_set")

You then would need to change your javascript to append the value of idfield (with the trailing slash) to the api/map/ url to be the address for the GET.

Hello @KenWhitesell

It’s what I tried without success, the past 2 hour :(.
First I add the number 2 here

Then I modified my urls.py file as the following

This file is calling the api.py and I beleive, the bug start at this point.
At the line 9, is the default line. I comment it to add the line 10.
How can I get the id of the field, that I add at the url const markers_url = /api/map/2/;
Except of this, do I start correctly?

Any way, the next problem, is how can I retrieve the id in my viewsets file

The above test give me some errors

Someone told me, I should use request.GET.get("idfield"). Should it be an alternative solution, that I may be can use in the viewsets.py file???

Thanks so much

You’re using the URL /api/map/2/, but your url is defined as:
path("api/<int:idf>/", include("map.api")),
which doesn’t align with either the url you are providing or with my previous suggestion.

I can’t comment on alternative suggestions without knowing the full context of the question as posed and the complete answer.

Dear @KenWhitesell
I am sorry, but I have not read correctly your suggestion above and I have some doubt. I also tried some else that I will describe later.

I past your proposition in my urls.py file (the project urls.py). I received an error

path(“api/map/int:idfield/”, MarkerViewSet.as_view(), name=“marker_view_set”)
NameError: name ‘MarkerViewSet’ is not defined

I supposed…
MarkerViewSet.as_view()
I need to create MarkerViewSet in my views.py file now?
(dont pain attention at return 0)

def MarkerViewSet(request):
    return 0

In spite of this, I still receive the error

NameError: name ‘MarkerViewSet’ is not defined

I am confused about your suggestion and I may missed something.

However, I tried something else and I would appreciate your point of view.

I kept this, in my project/urls.py
path("api/map/<int:idf>/", include("map.api")),

in my api.py file, I tried this

router = routers.DefaultRouter()
#router.register(r"map", MarkerViewSet) #!!!?????
#router.register(r"^map/{idf}/$", MarkerViewSet) # here way a try
#router.register(r"^map/(?P<id_f>[0-9]+)/$", MarkerViewSet) # here way a try
router.register(r"^map/1/$", MarkerViewSet) # I hard code the id of the field
urlpatterns = router.urls

I think, I have to find the right format to get the number of my filed, above. Remeber const markers_url = /api/map/1/;

The terminal does not print any error.

I am desparted :slight_smile:

No, you need to import it in your urls.py file from the file in which it currently resides.

from map.viewsets import MarkerViewSet

None of the other things you’ve listed in your post should be necessary - they just confuse the issue.

Dear @KenWhitesell
(I wrote this post at the same time of my tries. You will read, it works at the end)
Thanks so much to follow me and your patience, but I still do not understand.
I do not understand where is should be from map.viewsets import MarkerViewSet
I removed all my tries and I try again your suggestion.

So, we are agree this is correct

async function load_markers() {
    const markers_url = `/api/map/1/`; // this is correct
    console.log("markers_url: ",markers_url);
    const response = await fetch(markers_url);
    //console.log("response: ",response);
    const geojson = await response.json();
    console.log("geojson: ",geojson);
    return geojson;
}

Then the project/urls.py, I follow your example:
path("api/map/<int:idfield>/", MarkerViewSet.as_view(), name="marker_view_set")

But I beleive, in any case, the api.py file is not correct

router = routers.DefaultRouter()
#router.register(r"map", MarkerViewSet) #!!!?????
#router.register(r"^map/{idf}/$", MarkerViewSet)
#router.register(r"^map/(?P<id_f>[0-9]+)/$", MarkerViewSet)
router.register(r"map/", MarkerViewSet)
urlpatterns = router.urls

because I have to pass my id filed parameter, isn’t?

But I am confused where should I import from map.viewsets import MarkerViewSet

I do not really understand your sentence

No, you need to import it in your urls.py file from the file in which it currently resides.

If I past from map.viewsets import MarkerViewSet to my project/urls.py I have this error

TypeError: The actions argument must be provided when calling .as_view() on a ViewSet. For example `.as_view({‘get’: ‘list’})

but now, with that, I get no errors
path("api/map/<int:idfield>/", MarkerViewSet.as_view({'get':'list'}), name="marker_view_set")

But better IT’S WORK!!!

but this {'get':'list'} is correct??? I am really confused with get and list

Why I am saying it work. Give me a short time to describe it.
First, in my map.js file, I have this

async function load_markers() {
    //const markers_url = `/api/map/?in_bbox=${map.getBounds().toBBoxString()}`;
    const markers_url = `/api/map/4/`; // I CHANGE 4 to 2 and to 1 and reload the page
    console.log("markers_url: ",markers_url);
    const response = await fetch(markers_url);
    //console.log("response: ",response);
    const geojson = await response.json();
    console.log("geojson: ",geojson);
    return geojson;
}

in my project/urls.py I added this

from django.contrib import admin
from django.urls import path, include
from map.viewsets import MarkerViewSet

urlpatterns = [
    path('admin/', admin.site.urls),
    path('map/', include('map.urls')),
    path('', include('map.urls')),
    #path("api/", include("map.api")),
    #path("api/map/<int:idf>/", include("map.api")),
    path("api/map/<int:idfield>/", MarkerViewSet.as_view({'get':'list'}), name="marker_view_set")
    #path("api/{idf}", include("map.api")),
]

Is it what your are expected me to do, isn’t?
in my api.py file, I have this

router = routers.DefaultRouter()
#router.register(r"map", MarkerViewSet) #!!!?????
#router.register(r"^map/{idf}/$", MarkerViewSet)
#router.register(r"^map/(?P<id_f>[0-9]+)/$", MarkerViewSet)
router.register(r"map/", MarkerViewSet)
urlpatterns = router.urls

I kept the original line, is really correct?

Then in my viewsets file, I have this

from rest_framework import viewsets
from rest_framework_gis import filters


from map.models import Stations
from map.serializers import StationsSerializer

class MarkerViewSet(viewsets.ReadOnlyModelViewSet):
    """Marker view set."""
    bbox_filter_field = "location"
    filter_backends = (filters.InBBoxFilter,)
    queryset = Stations.objects.filter(station_active=1, map=1)
    serializer_class = StationsSerializer
    """
    def get_tags(self):
        return Stations.objects.filter(fields_id_field=1)
    """
    def get_queryset(self):
        print("iii",self.kwargs['idfield'])
        #print("ppp:", self.kwargs['idfield'])
        return Stations.objects.filter(fields_id_field=2)

the line print("iii",self.kwargs['idfield'])
prints iii 1 when const markers_url = /api/map/1/;
prints iii 2 when const markers_url = /api/map/2/;
prints iii 4 when const markers_url = /api/map/4/;

and I tried
prints iii 40 when const markers_url = /api/map/40/;

Now I modified this

class MarkerViewSet(viewsets.ReadOnlyModelViewSet):
    """Marker view set."""
    bbox_filter_field = "location"
    filter_backends = (filters.InBBoxFilter,)
    queryset = Stations.objects.filter(station_active=1, map=1)
    serializer_class = StationsSerializer
    """
    def get_tags(self):
        return Stations.objects.filter(fields_id_field=1)
    """
    def get_queryset(self):
        print("iii",self.kwargs['idfield'])
        idf = self.kwargs['idfield']
        #print("ppp:", self.kwargs['idfield'])
        return Stations.objects.filter(fields_id_field=idf)

and when I change the value 1 to 2 to 4 const markers_url = /api/map/1/; and I refresh the page, it’s work. The map show the correct field!!! Houhaaa!!

But I am still not confident, if my code is well coded

router = routers.DefaultRouter()
router.register(r"map/", MarkerViewSet)
urlpatterns = router.urls

Why that does not generate an error?

Is there a way to make it better? :slight_smile:
Why this does not generate an error ({'get':'list'}). Could you clarify me that? Is it really correct?

The api.py file is irrelevent here. We’re not using it.

See the docs at ViewSets

Because it’s not wrong.

You’re just not using it.

You’re asking good questions, but there isn’t a quick and simple answer to them.

You need to focus on understanding the Django fundamentals here - what is a URL definition, what’s a view, how does a URL get mapped to a view. These are all concepts that you’re going to need to understand to be able to understand the answers to the specific questions you’re asking.

Ok I understand

there isn’t a quick and simple answer to them.
Yes

So many thanks for all of your help. You really help me a lot.
I will read tomorrow the doc about ‘get’ and ‘list’

In 10 day, I have a django training and I will overview this interesting part.

Now I have to find a way to get the id of the filed in my map.js, but that is not a big issue.

Thanks you!!!