Search code examples
pythondjangodjango-rest-frameworkgeodjango

Implementing point search in a polygon using Django


There were some difficulties while implementing my app in Django. What the app should do:

  • Accepts and saves Delivery zones as a set of coordinates;
  • Accepts Couriers data with reference to the Delivery zone;
  • Accepts the coordinates of Delivery location and returns Courier data and Delivery zone ID.

I solved two points, but there problems with the last one. I need to know if Delivery location is in Delivery zone or not. I found how to solve this problem in Python shell:

from django.contrib.gis.geos import GEOSGeometry

p = GEOSGeometry(location, srid=4326)
DeliveryZone.objects.filter(coordinates__contains=p)

But I don't know how to implement this inside my app.

models.py

from django.contrib.gis.db import models


class DeliveryZone(models.Model):
    coordinates = models.MultiPolygonField(srid=4326)

    def __str__(self):
        return f"#{self.id}"


class Courier(models.Model):
    delivery_zone = models.ForeignKey(DeliveryZone, on_delete=models.PROTECT) 
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)

    def __str__(self):
        return f"#{self.id} {self.last_name}"


class Delivery(models.Model):
    location = models.PointField(srid=4326)

    def __str__(self):
        return f"#{self.id}"

views.py

from django.contrib.gis.geos import GEOSGeometry
from rest_framework import viewsets

from .serializers import DeliveryZoneSerializer, CourierSerializer, DeliverySerializer
from .models import DeliveryZone, Courier, Delivery


class DeliveryZoneViewSet(viewsets.ModelViewSet):
    queryset = DeliveryZone.objects.all()
    serializer_class = DeliveryZoneSerializer


class CourierViewSet(viewsets.ModelViewSet):
    queryset = Courier.objects.all()
    serializer_class = CourierSerializer


class DeliveryViewSet(viewsets.ModelViewSet):
    queryset = Delivery.objects.all()
    serializer_class = DeliverySerializer

I can provide more information if necessary.


Solution

  • The best practice is to use a manager for that. Then in django rest framework you would hook into get_queryset and remove queryset.

    Pseudo code.

    from rest_framework.generics import ListAPIView
    
    class DeliveryViewSet(ListAPIView):
        serializer_class = DeliverySerializer
    
        def get_queryset(self):
            # put a ref to zone_id in your url
            zone_id = self.kwargs.get('zone_id')
            zone_instance = get_object_or_404(DeliveryZone, id=zone_id)
            # implement filter_by_zone in your manager
            return Delivery.objects.filter_by_zone(zone_instance).all()