0

I am very new to django / python. I am currently experimenting building a web application using GeoDjango on MySQL/Spatialite (later I will move to postgres/postGIS when I decide to pay for a dedicated environment).

I use the following expression to get a queryset which is annotated with the distance of each Splitter's company's service_area's centroid (point field).

sl = Splitter.objects.filter(company__in=companies, seats__gte=int(seatval), selfhire=selfval).annotate(distancefrom=Distance('company__service_areas__centroid', postcode_location)).order_by('distancefrom')

Not sure if this is important, but I am importing Distance from django.contrib.gis.db.models.functions

It works fine, however the default value for the "Distance" calculation is in metres. As such I get annotated values generated that look like this: 46035.39602585269 m

I'd much rather get these values in miles or km. How can I alter the expression to force the annotation to be in these units instead? Alternatively what method could I use to update the annotations? I'd rather not iterate over the queryset.

Typically distance.mi or distance.km would give me these values, but I am not sure how to do this as part of an annotation?

What I tried: I tried simply adding .mi into the expression but it does not seem to work.

sl = Splitter.objects.filter(company__in=companies, seats__gte=int(seatval), selfhire=selfval).annotate(distancefrom=Distance('company__service_areas__centroid', postcode_location).mi).order_by('distancefrom')

I am reluctant to use a for loop over the queryset as sometimes django bugs out and says sl (the queryset) is not iterable, though I am unsure why. I've also heard this isn't ideal performance wise?

Something else I tried was using a serializer as discussed here: How to annotate a postgis distance in a queryset in an specific unit?

The implementation wasn't really explained so I took a guess at it in a serializers.py file:

from rest_framework import serializers
from splitter.models import Splitter

class SplitterSerializer(serializers.ModelSerializer):
    distancefrom = serializers.DecimalField(
        source='distancefrom.mi', max_digits=5, decimal_places=2, read_only=True)

    class Meta:
        model = Splitter
        fields = [
            'company',
            'vehicle_model',
            'seats',
            'fee',
            'comments',
            'trailer',
            'selfhire',
            'tourmanager',
            'sleeper',
            'foh',
            'lampy',
            'tech',
            'distancefrom'
        ]

Basically, I am just putting all the fields in the model into the serializer, with the addition of the annotation (distancefrom) converted into the measurement of choice (mi). I played around with this in the django shell, for example:

sl = Splitter.objects.all()
serializer = SplitterSerializer(sl)
serializer.data

I got a lot of errors whenever I tried to access the data (the last line) which I could not work around:

"AttributeError: Got AttributeError when attempting to get a value for field `company` on serializer `SplitterSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `QuerySet` instance.
Original exception text was: 'QuerySet' object has no attribute 'company'."

To try to fix this I appended "_set" on the foreign key fields (company, vehicle_model), but then I got this error:

"django.core.exceptions.ImproperlyConfigured: Field name `company_set` is not valid for model `Splitter`."

I then tried labelling the foreign keys with related_name='vehicle_model' etc in the model itself and migrated, this again threw this error:

"django.core.exceptions.ImproperlyConfigured: Field name `company_set` is not valid for model `Splitter`."

As such I am giving up on the serializers method. If this is the only / best way I am happy to revisit it though.

Advice much appreciated!

nicks101
  • 1,065
  • 11
  • 20

0 Answers0