Search code examples
pythondjangoleafletgeodjango

Django view sometimes routes to wrong URL


I have a Django project with this layout

enter image description here

in the urls.py

from django.contrib import admin
from django.conf.urls import url as path
from mapping import views
urlpatterns = [
    path('admin/', admin.site.urls),
    path('map/', views.Map, name='map'),
    path('address',views.Address_Search,name='address')
]

views.py

from django.shortcuts import render
from django.views.decorators.csrf import csrf_exempt
from django.http import JsonResponse,HttpResponse
import geocoder
import pdb
bingkey='mykey'

def Map(request):
    return render(request,'mapping.html')

@csrf_exempt
def Address_Search(request):
    try:
        address=request.POST.get('fname')
        print(address)
        g = geocoder.bing(address,key=bingkey)
        x,y= g.latlng[1],g.latlng[0]
        print(x,y)
        return JsonResponse({'id': 1,'x': x, 'y': y,'Address': address})
    except Exception as e:
        print(e)
        return render(request,'mapping.html')

and in templates I have a mapping.html which contains

{% block content %}
<html>
{% load static %}
{% load leaflet_tags %}
{% load bootstrap4 %}
<head>
    {% leaflet_js %}
    {% leaflet_css %}
    {% bootstrap_css %}
    <title>Sals Food</title>
    <style type="text/css">
        #map {width: 100%;height:800px;}
        </style>

    <link rel="stylesheet" type="text/css" href="{% static 'search_bar.css' %}">
    <link rel="stylesheet" href="{% static 'bootstrap-4.0.0-dist/css/bootstrap.css' %}" crossorigin="anonymous">
    <link href="https://fonts.googleapis.com/css?family=Poppins" rel="stylesheet" />

    <script type="text/javascript" src="{% static 'jquery/jquery-3.3.1.min.js' %}" > </script>
    <script type="text/javascript" src="{% static 'dist/leaflet.ajax.js' %}" > </script>
    <script type="text/javascript" src="{% static 'turf/turf.min.js' %}" > </script>
    <script type="text/javascript" src="{% static 'basemaps/leaflet-providers.js' %}" > </script>

    <script>
        function subForm()
        {
          var jsdata = {"fname": $('#fname').val()};
          console.log(jsdata);
          var url = "/address/";
          var jqHdr = $.ajax({
                  async: true,
                  cache: false,
                  type: "POST",
                  url: url,
                  data: jsdata,
                  dataType: "json"
              }).done(function (data, textStatus) {
                  if (data.id == 1)
                  {
                      console.log("Longitude: "+data.x + " Latitude: "+data.y + " Address "+data.Address );
                      $('#map').remove();
                      var t = $("<div id='map'></div>");
                      $("body").append(t);
                      createmap(data.x,data.y,data.Address);
                    }
              });
          }

        function createmap(x,y,address){
            var map = L.map('map');
            map.setView([40.70,-73.90], 11);

            var boros = new L.GeoJSON.AJAX("../static/boros.geojson",{
                onEachFeature: function(feature,layer){
                    layer.bindPopup(feature.properties.boro_name)
                }
            });       
            boros.addTo(map);

            L.marker([y, x]).addTo(map).bindPopup(address);
        
            lyrOSM = L.tileLayer.provider('OpenStreetMap.Mapnik');
            lyrESRIWSM = L.tileLayer.provider('Esri.WorldStreetMap');
            lyrESRITopo = L.tileLayer.provider('Esri.WorldTopoMap');
            lyrESRIImagery = L.tileLayer.provider('Esri.WorldImagery').addTo(map);

            objBaseMaps = {
                "Street - OSM":lyrOSM,
                "Street - ESRI":lyrESRIWSM,
                "Imagery - ESRI Imagery":lyrESRIImagery,
                "Topo - ESRI Topo":lyrESRITopo,
            };
            ctlLayers = L.control.layers(objBaseMaps,{}, {sortLayers:true}).addTo(map);
        }  
      </script>

</head>
<body>
    <div class="s01">
        <form action="/address/" method="post">
          <div class="inner-form">
            <div class="input-field second-wrap">
                <input type="text" id="fname" name="fname" value="Enter a address">
            </div>
            <div class="input-field third-wrap">
              <button class="btn-search" onclick="subForm()" type="button">Search</button>
            </div>
          </div>
        </form>
      </div>
 
</body>
</html>
{% endblock %}

How it works

The user enters in an address on the http://localhost:8000/map/ like this enter image description here

the entered address is put into a html POST request form and uses AJAX to pipe the POST request to the view.py Address_Search. The address is geocoded and it returns the x,y and address back to the page. For this example it works well as you can see the output is correct. [3]: https://i.sstatic.net/hKYU3.jpg [![enter image description here][3]][3]

Problem

There are times where I enter in an address that's wrong or just random letters

for example I just entered in 55 water street and this is what gets returned. It doesn't throw an error in the Address_Search view, it geocodes the input but for some reason it redirects to http://localhost:8000/address/ most of the time if the address is accurate this does not happen, thats why I am very confused. I must have something wrong here...

// 20210120180356
// http://localhost:8000/address/

{
  "id": 1,
  "x": -74.00910186767578,
  "y": 40.70320129394531,
  "Address": "55 water street"
}

Solution

  • It looks like there's an error on the address api somewhere. I would dig into that on the django side and see what's causing it. But, when that error is thrown, you're using render, which is going to render the html template you provide and let the page redirect. Since you're calling /address/ and letting the page redirect, it's redirecting to that url.

    You can change the response to a json response instead. I would change both the try and catch, and include a status key value pair as well as the data you want.

    @csrf_exempt
    def Address_Search(request):
        try:
            address=request.POST.get('fname')
            print(address)
            g = geocoder.bing(address,key=bingkey)
            x,y= g.latlng[1],g.latlng[0]
            print(x,y)
            return JsonResponse({'status': 'success', 'data': {'id': 1,'x': x, 'y': y,'Address': address}})
        except Exception as e:
            print(e)
            return JsonResponse({'status': 'error', 'error': e})
    

    You could even clean it up a bit, specifically by using one JsonResponse:

    @csrf_exempt
    def Address_Search(request):
        response = {'status': None,
                    'data': None,
                    'error': None,
                   }
        try:
            address=request.POST.get('fname')
            print(address)
            g = geocoder.bing(address,key=bingkey)
            x,y= g.latlng[1],g.latlng[0]
            print(x,y)
            response['status'] = 'success'
            response['data'] = {'id': 1,'x': x, 'y': y,'Address': address}
    
        except Exception as e:
            print(e)
            response['status'] = 'error'
            response['error'] = e
        return JsonResponse(response)
    

    This might need some cleaning up as I'm just typing in SO, but that's the general idea

    edit - Also, this will result in some changes on the javascript side - you'll need to look for the status of error and deal with it, and get the data from the data value instead of just the returned object itself.

    edit 2 - Make sure the form is not actually submitting when you press the button (it's being treated as a submit button even though you didn't explicitly say so). You can add onsubmit="return false;" to the form itself, or at the end of your done method in jquery, add return false;