How To Build Location-Based App with GeošŸŒŽDjango

Written by lyama | Published 2021/06/15
Tech Story Tags: django | python3 | rest-api | postgis | postgresql | geodjango | geopy | location

TLDR GeoDjango is a built-in application that is included as a contrib module in Django. It’s actually a complete framework itself that can also be used separately from Django. The GeoGis extension needs to be installed on postgres or install Geospatial libraries GEOS, GDAL, and PROJ.4, or geopy psycopg2-binary. Here we are getting co-ordinates(Longitude, Latitude) of location by inputting names of country and city.via the TL;DR App

GeoDjangoĀ is a built-in application that is included as a contrib module in Django. It’s actually a complete framework itself that can also be used separately from Django. It provides a toolbox of utilities for building GIS web applications.
Purpose of this tutorial
Nothing much fancy, here we are getting co-ordinates(Longitude, Latitude) of location by inputting names of country and city.

RequirementsĀ :

Note: above requirements need to be installed on your machine separately before continuing

Project setup:

$ mkdir dj_gis && cd dj_gis
$ python3 -m venv env
$ source env/bin/activate
$ pip install django djangorestframework django-leaflet geopy psycopg2-binary 
$ django-admin.py startproject config
$ python manage.py startapp location
config/settings.py
#config/settings.py

INSTALLED_APPS = [
    "django.contrib.gis",
    "location",
    "rest_framework",
]
config/urls.py
#config/urls.py

from django.contrib import admin
from django.urls import path
from django.urls.conf import include


urlpatterns = [
    path("admin/", admin.site.urls),
    path("api/v1/", include("location.urls")),
]
includeĀ url.pyĀ file onĀ locationĀ app
#location/urls.py

from django.urls import path

urlpatterns = []
So we finished basic setups
Let's create a modelĀ location/models.py
from django.db import models
from django.contrib.gis.db import models # GeoDjango Model API


class Hotel(models.Model):
    name = models.CharField(max_length=255)
    address = models.CharField(max_length=255)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    location = models.PointField(null=True) # Spatial Field Types

    def __str__(self) -> str:
        return self.name
here, we include aĀ pointfieldĀ spatial field of geo django model api.
let's create aĀ serializerĀ andĀ viewsĀ for our model.
# location/serializers.py

from location.models import Hotel
from rest_framework import serializers


class HotelSerializer(serializers.ModelSerializer):
    class Meta:
        model = Hotel

        fields = ("id", "name", "address", "location")

        extra_kwargs = {"location": {"read_only": True}}

location/views.py
from rest_framework import generics
from .models import Hotel

from django.contrib.gis.geos import Point
from geopy.geocoders import Nominatim

geolocator = Nominatim(user_agent="location")


class ListCreateGenericViews(generics.ListCreateAPIView):
    queryset = Hotel.objects.all()
    serializer_class = HotelSerializer

    def perform_create(self, serializer):
        address = serializer.initial_data["address"]
        g = geolocator.geocode(address)
        lat = g.latitude
        lng = g.longitude
        pnt = Point(lng, lat)
        print(pnt)
        serializer.save(location=pnt)


class HotelUpdateRetreiveView(generics.RetrieveUpdateDestroyAPIView):
    queryset = Hotel.objects.all()
    serializer_class = HotelSerializer

    def perform_update(self, serializer):
        address = serializer.initial_data["address"]
        g = geolocator.geocode(address)
        lat = g.latitude
        lng = g.longitude
        pnt = Point(lng, lat)
        print(pnt)
        serializer.save(location=pnt)
Note: aboveĀ viewĀ can be further refactor usingĀ viewsetsĀ or your desired ones.
so,Ā geopyĀ library comes in handy, which is is a Python client for several popular geocoding web services. geopy makes it easy for Python developers to locate the coordinates of addresses, cities, countries, and landmarks across the globe using third-party geocoders and other data sources.
let's update our urls:
#location/urls.py

from django.urls import path
from .views import HotelUpdateRetreiveView, ListCreateGenericViews

urlpatterns = [
    path("hotels", ListCreateGenericViews.as_view()),
    path(
        "hotels/<str:pk>",
        HotelUpdateRetreiveView.as_view(),
    ),
]
Creating a Database:
sudo -u postgres psql

CREATE DATABASE locator;

CREATE USER locator WITH PASSWORD 'locator';

CREATE EXTENSION postgis;

ALTER ROLE locator SET client_encoding TO 'utf8';

ALTER ROLE locator SET default_transaction_isolation TO 'read committed';

ALTER ROLE locator SET timezone TO 'UTC';

ALTER ROLE locator SUPERUSER;

GRANT ALL PRIVILEGES ON DATABASE locator TO locator;
let's make some changes on ourĀ settings.py
INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    "django.contrib.gis",
    "location",
    "rest_framework",
    "leaflet",
]

DATABASES = {
    "default": {
        "ENGINE": "django.contrib.gis.db.backends.postgis",
        "NAME": "locator",
        "USER": "locator",
        "PASSWORD": "locator",
        "HOST": "localhost",
        "PORT": "5432",
    }
}

LEAFLET_CONFIG = {
    # "SPATIAL_EXTENT": (5.0, 44.0, 7.5, 46),
    "DEFAULT_CENTER": (13.3888599 52.5170365), #set your corordinate
    "DEFAULT_ZOOM": 16,
    "MIN_ZOOM": 3,
    "MAX_ZOOM": 20,
    "DEFAULT_PRECISION": 6,
    "SCALE": "both",
    "ATTRIBUTION_PREFIX": "powered by me",
}
Registering model on admin
from django.contrib import admin
from leaflet.admin import LeafletGeoAdmin

from .models import Hotel


@admin.register(Hotel)
class HotelAdmin(LeafletGeoAdmin):
    list_display = ("id", "name", "address", "location", "created_at", "updated_at")
so, we have added leaflet, leaflet_config and database. For more about Leaflet you can visitĀ Read the docs
let's run our app:
python manage.py makemigrations
python manage.py migrate
python manage.py createsuperuser
python manage.py runserver
here, what you get on the admin panel.
Let's add some data using browsable api or you can use postman too.
Try it out yourself for update and delete
You can find the code in github repo.

Written by lyama | Top Secret ......
Published by HackerNoon on 2021/06/15