Sending location data from template JS to view

Hello,

I have a small problem in my simple application.

Background
I have two views (home and search). On the home view, there is a search bar where the user types in a name of a service and the ‘search’ view is rendered. The search view (which requires a service name) then filters through my Shop model and finds all shops offering the service. In my home view template, I have the following JS script:

const findLoc = () => {
        var URL = "{% url 'home' %}"
        var django_csrf_token = '{{ csrf_token }}'
        const success = (position) => {
            const latitude = position.coords.latitude;
            const longitude = position.coords.longitude; 
            // console.log("longitude: " + longitude)
            // console.log("latitude: " + latitude)
            var data = {'latitude': latitude,
                        'longitude': longitude,
                        'csrfmiddlewaretoken': django_csrf_token };
            $.post(URL, data);
        }

        const error = () => {
            console.log('Location gathering Not allowed')
            const latitude = 43.6771296     // Default Latitude
            const longitude = -79.6333512    // Default Longtitude
            var data = {'latitude': latitude,
                        'longitude': longitude,
                        'csrfmiddlewaretoken': django_csrf_token };
            $.post(URL, data);
        }

        navigator.geolocation.getCurrentPosition(success,error);
    }
    // Location is fetched when user loads screen
    Window.onload = findLoc();
    // Location is fetched when user presses enter on home screen
    input_field = document.getElementById('service_name')
    input_field.addEventListener('keydown', (e) => {
      if (e.key =='Enter'){
        findLoc();
      }
    });
    // Location is fetched when user clicks on search button
    document.getElementById('searchButton').addEventListener('click', findLoc)

This script is basically sending user coordinates to the home view. In my search view, I have the coordinates of each shop and ultimately I want to display the distance from the user for each shop while rendering the search view, for this I will be using a Google API.

Problem

My problem is that the user longitude and latitude are extracted on the ‘home’ view and I don’t want to pass this information to the ‘search’ view via URL, because I don’t want to show this information in my URL path.

The other option I tried was to add the above JS on the ‘search’ view but I am not able to extract and post the user longitude and latitude like I did on the ‘home’ view.

def search(request, service_name):

    if request.method == 'POST': 
        user_lng = request.POST.get('longitude')
        user_lat = request.POST.get('latitude')
        print(user_lng, user_lat)
    else: 
        print('nothing')
    
    shops = Shop.objects.filter(services__name__icontains = service_name).order_by("-created_on").distinct()

    return render(request, 'main/search.html', context = {
                                                          'searched':service_name,
                                                          'shops_found': shops})

Is there a better way of doing this? I know how to calculate the distance but I need to have the coordinates of the from location and to location in my search view so I can execute my downstream logic. (For each shop, I will save the distance which is a field of my shop model. Then I can re-filter the shops query set and order by distance and then send that to my search template.)

Is your jQuery post sending the data as JSON or as HTTP post data? (You can check this by seeing what’s being sent in the network tab of your browser’s developer tools.)

If it’s sending it as JSON, then you need to handle it as JSON in the view - which means reading it from request.body instead of request.POST. See Request and response objects | Django documentation | Django.

For a situation such as this, you’re probably better off sending it as POST data.

Side note: Either way, I’d also suggest handling these data elements in a form. At a minimum, you could verify that they are reasonable and that the data hasn’t been inappropriately tampered with on the client to send bogus or malicious data. (Never trust data submitted from the browser.)

1 Like

Thank you Ken!

Sorry I realized that I didn’t upload the JS script I am using on the ‘search’ template and I also didn’t import the JQuery Script. I’m new to JS development so I didn’t know that the “$.post” is a JQuery thing. Here is my script now and it is working, I am able to get the user’s location when the search page loads and I am sending POST data because in my ‘search’ view I can extract the data using request.post.get

<script src="https://code.jquery.com/jquery-3.6.0.js"></script>
    
    <script src="https://code.jquery.com/ui/1.13.2/jquery-ui.js"></script>
    
    <script>
        const findLoc = () => {
            var URL = "{% url 'search' searched %}"
            var django_csrf_token = '{{ csrf_token }}'
            const success = (position) => {
                const latitude = position.coords.latitude;
                const longitude = position.coords.longitude; 
                var data = {'latitude': latitude,
                            'longitude': longitude,
                            'csrfmiddlewaretoken': django_csrf_token };
                $.post(URL, data);
            }
    
            const error = () => {
                console.log('Location gathering Not allowed')
                const latitude = 43.6771296     // Default Latitude
                const longitude = -79.6333512    // Default Longtitude
                var data = {'latitude': latitude,
                            'longitude': longitude,
                            'csrfmiddlewaretoken': django_csrf_token };
                $.post(URL, data);
            }
    
            navigator.geolocation.getCurrentPosition(success,error);
        }
        // Location is fetched when user loads screen
        Window.onload = findLoc();
      </script>

Because I am getting the location on Window Load, I am thinking of asking the user to provide their location on the homepage to avoid having to reload the page if they allow location access on the search page.

Two Questions

  1. In regards to what you said here, to achieve this I can create a form using the longitude and latitude within my JS script and then submit that form via JS right? Can you please provide an example of this if you have anything.

  2. Also, from a high level perspective, is my method of using the user location and calculating distances within the search view to show nearby shops first an efficient way of achieving what I want. I’m just thinking if there are 100-200 shops in the future, and I have to calculate the distance for each, it will be alot of API calls and alot of computation. Unfortunately, I don’t think I can save this data for each user that visits my website, but maybe I can do this for users who are authenticated?

Thank you so much!

You don’t need to do it that way.

Keep in mind that a “Django Form” is a Python object that exists in the server. One of its functions is to create an HTML representation of that object. However, a Django Form is not HTML. There is nothing within a Django Form that requires any HTML be associated with it.

You can create the form, and bind the post data to it, without ever having generated any HTML.

When you create an instance of a form class with data that has been submitted, it’s known as “binding” data to the form. In the general case, it’s most frequently used with POST data that has been submitted.

However, request.POST is a type of dict. What’s really happening is that the dict is the data being bound to the forms. And the dict that you use doesn’t need to be retrieved from request.POST. You can bind any dict to a form - including JSON submitted via AJAX and deserialized.

See the docs for Bound and unbound forms for an example.

That’s one of the purposes and features of the GIS-related functions in GeoDjango (e.g., PostGIS for PostgreSQL). See GeoDjango | Django documentation | Django
(Also, skip ahead and see GeoDjango Database API | Django documentation | Django to get some idea of what’s immediately available to you.)

Depending upon the number of users and the number of shops, you could create a many-to-many relationship with a through model containing the distance between the two.

So based on what I’m reading here, I would create a Form in forms.py for holding the latitude and longitude coordinates. Then in my ‘search’ view, when I read the POST dict returned template, I would instantiate the form and then do a form.is_valid() to make sure I didn’t get any malicious or incorrect data right?

Yep, that’s it. After that, your view can process those values anyway you want.