Underlost, By Tyler Rilling
  • Home
  • About
  • Contact
  • Personal Projects
  • Client Work
  • Instagram
Development

A simple GeoJSON serializer

Published on Written May 27, 2022Last Updated May 27, 2022
2 min read

Hacking Django Rest Framework to give me data I can use in Mapbox.

Django’s GeoDjango library is excellent, but for a new project I started working on, I just needed to output existing data as GeoJSON, which could then be used with Mapbox. The problem is, that using GeoDjango library would require a lot of additional setup- I don't need PostGis, or geographic objects right now; I just need data output as JSON.

I’ve already been using Django Rest Framework, so I have an API set up for the rest of the site. I just needed an endpoint to return GeoJSON. The great thing about DRF is the ability to create custom serializers. After a little trial and error, I was able to create an endpoint that returns data in a standard GeoJSON format! So with our further ado:

My model:

A pretty basic model. The key field here is a JSONfield to store longitude and latitude.

class Event(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    name = models.CharField(max_length=250, blank=True, null=True)
    description = models.CharField(
        blank=True,
        null=True,
        max_length=512,
        help_text="Reading a book, watching a movie, etc",
    )
    content = models.TextField(blank=True, null=True)
    content_html = models.TextField(editable=False, blank=True, null=True)
    pub_date = models.DateTimeField(
        verbose_name="When the event happened, or is going to happen.", blank=True, null=True
    )
    date_created = models.DateTimeField(auto_now_add=True, editable=False)
    date_updated = models.DateTimeField(auto_now=True, editable=False)
    is_public = models.BooleanField(help_text="Should be checked to show up on global timelines.", default=False)
    is_active = models.BooleanField(help_text="Is it published?", default=False)
    location = models.ForeignKey(Location, blank=True, null=True, on_delete=models.SET_NULL)
    lnglat = models.JSONField(blank=True, null=True)

The view:

I love using viewsets. They keep everything organized, especially when you’re using a lot of different endpoints and methods. Here I'm doing two endpoints; retrieving a list of the latest public events, and a retrieve to get any given specific event.

class EventViewSet(ViewSet):
    """Events"""

    permission_classes = [OwnsObjectOrReadOnly]

    def list(self, request):
        """Get all public events"""
        events = Event.objects.public()
        serialized_data = GeoJsonEventSerializer(events, many=True, context={"request": request})
        return Response(
            {
                "result": serialized_data.data,
            }
        )

    def retrieve(self, request, pk=None):
        """Get an event"""
        if request.user.is_authenticated:
            event = get_object_or_404(Event, pk=pk)
        else:
            event = get_object_or_404(
                Event,
                pk=pk,
                is_active=True,
                is_public=True,
            )
        serialized_data = GeoJsonEventSerializer(event, context={"request": request})
        return Response(
            {
                "result": serialized_data.data,
            }
        )

The serializer:

The key is to format the JSON structure in a very specific way. Using to_representation lets us achieve that.

class GeoJsonEventSerializer(serializers.ModelSerializer):
    class Meta:
        model = Event
        fields = [
            "id",
            "user",
            "name",
            "description",
            "content_html",
            "pub_date",
            "date_updated",
            "date_created",
            "lnglat",
            "location",
            "timeline",
            "image",
        ]

    def to_representation(self, instance):
        # instance is the model object. create the custom json format by accessing instance attributes normaly and return it
        identifiers = dict()
        identifiers["name"] = instance.name
        identifiers["description"] = instance.description

        representation = {
            "type": "Feature",
            "geometry": {"type": "Point", "coordinates": [instance.lnglat.get("Lng"), instance.lnglat.get("Lat")]},
            "properties": {
                "name": instance.name,
                "description": instance.description,
                "url": "http://fubar/",
            },
        }

        return representation

That's it! My API is now returning GeoJSON objects that I can then load into Mapbox. Of course, this is just the start. Eventually, I very well replace a lot of this with the GeoDjango framework, but for now, this was a quick simple hack of getting Django Rest Framework to return exactly what I need to prototype a project.

Related Tags

Development

Read More

  • Seattle WordPress Theme

  • Social Media Predictions for 2023

  • Watch Dogs Legion

  • Retrieving Spotify Search Results via API

About the Author
Tyler Rilling

Tyler Rilling is the Author and creator of Underlost. They're not an Undertale game. They're also a senior developer for a design agency in Seattle. Probably still playing too much Destiny 2.


Sign up for the latest updates

I have a newsletter now! Want to stay up to date on the musings of a burnt-out developer that still wants to do way too much? Enter your email and you'll be added to the list, of which you can opt out any time.

View Past Issues

  • Home
  • About
  • Contact
  • Personal Projects
  • Client Work
  • Instagram
Underlost Copyright © Tyler Rilling 2001 - 2022.
Site last updated: 27/12/2022. ❤️