Фильтрация по расстоянию учитывая широту и долготу Django Filter

Пишу проект на Django + DRF. Имеется модель пользователя с заданной широтой и долготой! Во views выводится список пользователей с возможностью фильтрации по расстоянию, например все пользователи в радиусе 10 км от request.user. Саму функцию определения расстояния реализовал, фильтр тоже (через get_queryset), но возникла проблема, не пойму как получать в json само значение distance, расстояние с конкретным пользователем. Подскажите пожалуйста.

class Profiles(models.Model):
   """ Модель участников """
   user = models.OneToOneField(User, on_delete=models.CASCADE, verbose_name='Пользователь')
   longitude = models.FloatField(verbose_name='Долгота')
   latitude = models.FloatField(verbose_name='Широта')

views.py

class ProfilesListView(ListAPIView):
   """ Эндпоинт списка участников """
   serializer_class = ProfilesListSerializer
   filter_backends = [DjangoFilterBackend]
   filterset_class = ProfilesFilter
   filter_fields = ['distance']

   def get_queryset(self):
       try:
           distance = float(self.request.GET.get('distance'))
           mylon = self.request.user.profiles.longitude
           mylat = self.request.user.profiles.latitude
           lon1 = mylon - distance / abs(cos(radians(mylat)) * 111.0)
           lon2 = mylon + distance / abs(cos(radians(mylat)) * 111.0)
           lat1 = mylat - (distance / 111.0)
           lat2 = mylat + (distance / 111.0)
           queryset = Profiles.objects.filter(latitude__range=(lat1, lat2)).filter(longitude__range=(lon1, lon2))
           return queryset
       except TypeError:
           queryset = Profiles.objects.all()
           return queryset

serializers.py

class UsersSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ['first_name', 'last_name', 'email']


class ProfilesListSerializer(serializers.ModelSerializer):
    user = UsersSerializer()

    class Meta:
        model = Profiles
        exclude = ['id']

Класс фильтрации и метод вычисляющий расстояние

def get_distance_between_users(lon1, lat1, lon2, lat2):
    lon1, lat1, lon2, lat2 = map(radians, [lon1, lat1, lon2, lat2])
    dlon = lon2 - lon1
    dlat = lat2 - lat1
    length = 2 * asin(sqrt(sin(dlat / 2) ** 2 + cos(lat1) * cos(lat2) * sin(dlon / 2) ** 2))
    km = 6371 * length
    return round(km, 3)


class CharFilterInFilter(filters.BaseInFilter, filters.CharFilter):
    pass


class ProfilesFilter(filters.FilterSet):
    """ Фильтрация участников по полу имени и фамилии """
    first_name = CharFilterInFilter(field_name='user__first_name', lookup_expr='in')
    last_name = CharFilterInFilter(field_name='user__last_name', lookup_expr='in')

    class Meta:
        model = Profiles
        fields = ['first_name', 'last_name']

Не пойму как выводить расстояние близжайших пользователей используя метод get_distance_between_users...


Ответы (1 шт):

Автор решения: Za Ars

Если нужно расстояние до пользователя, от конкретной точки, то схематично можно сделать так:

Profiles.objects.filter(...<ваши фильтры>...).annotate(distance=Sqrt(Abs(F('longitude') - longitude) + Abs(F('latitude') - latitude))).order_by('distance')

Расстояние в примере находится по теореме Пифагора.

Где latitude, longitude - координаты точки. до которой ищете расстояния

Функции БД

На случай, если нудо будет исключить записи с нулевой дистанцией

F-объекты


Пример может не работать. тк не проверял, писал по памяти, только посмотрел нужны функции БД (можно найти по прикрепленным ссылкам) чтобы не ошибиться

→ Ссылка