Фильтр Django по расстоянию, используя только Sqlite (без GeoDjango, PostGres, MySQL, GIS) - PullRequest
0 голосов
/ 07 февраля 2019

У меня есть приложение Django, которое должно фильтровать пользователей по местоположению (показывать пользователей, которые находятся на заданном расстоянии от зарегистрированного пользователя).

Прямо сейчас я могу записать координаты зарегистрированных пользователей latitude and longitude, используя HTML5, и сохранить их в таблицу Location в моем бэкэнде SQLite.

У меня есть списки GeoIP2 с city и country в папке в моем проекте, я не уверен, как я могу их использовать, но они кажутся хорошим вариантом, так как они принимают координаты lat и lon и генерируют местоположение.

Я знаю, что здесь есть несколько вариантов, например, получение значения point из координат lat lon, которое затем, возможно, сможет фильтровать по радиусу?И т.д., 5 км, 10 км, 15 км?

Я относительно новичок во всем этом, поэтому я прошу о чистом минимальном решении.

Я не хочу использовать PostGres, ГИСГеоДжанго.

Если у кого-то есть простое решение для этого с использованием Sqlite и, возможно, списков geoip2, я был бы очень признателен, если бы вы поделились им!

models.py

class Location(models.Model):
latitude    = models.DecimalField(max_digits=19, decimal_places=16)
longitude   = models.DecimalField(max_digits=19, decimal_places=16)
user        = models.OneToOneField(User, on_delete=models.CASCADE, null=False, related_name='loc')

def __str__(self):
    return f'{self.user}\'s location'


@login_required
def insert(request):
    location = Location(latitude=request.POST['latitude'], 
longitude=request.POST['longitude'], user = request.user)
    location.save()
    return JsonResponse({'message': 'success'})

def location_view(request):
    queryset = User.objects.annotate(
        radius_sqr=pow(models.F('loc__latitude') - 
request.user.loc.latitude, 2) + pow(models.F('loc__longitude') - 
request.user.loc.longitude, 2)
    ).filter(
        radius_sqr__lte=pow(radius_km / 9, 2)
    )
    return django.shortcuts.render_to_response(request, context, 'connect/home.html')

Пример страницы page.html

{% for user in location %}
<h5><b>{{ user.first_name }} {{ user.last_name }}</b></h5>
{% endfor %}

1 Ответ

0 голосов
/ 07 февраля 2019

Прежде всего, если у вашего пользователя только одна позиция, есть большая вероятность использовать OneToOneField вместо внешнего ключа.Также рекомендуется помещать параметр related_name в любое отношение, которое вы используете для улучшения построения запросов.Например:

    class Location(models.Model):
    lat = models.DecimalField(max_digits=19, decimal_places=16)
    lon = models.DecimalField(max_digits=19, decimal_places=16)
    user = models.OneToOneField('User', on_delete=models.CASCADE, related_name='loc')

    def __str__(self):
        return f'{self.user}\'s location'

Обратите внимание, что null = False является значением по умолчанию для связанных полей.Теперь нам нужно вычислить квадрат радиуса как сумму квадратов разностей между широтами и долготами.Для вычислений в каждой строке мы можем использовать класс django.models.F для использования в методе аннотирования или фильтрации.Наконец, мы используем вычисленное значение в методе фильтра lte (важно фильтровать по квадрату радиуса).Для request.user это будет выглядеть так:

from django.db.models import F
from django.shortcuts import render_to_response

def location_view(request):
    radius_km = request.data.get('radius', 0)
    queryset = User.objects.annotate(
        radius_sqr=pow(models.F('loc__latitude') - 
request.user.loc.latitude, 2) + pow(models.F('loc__longitude') - 
request.user.loc.longitude, 2)
    ).filter(
        radius_sqr__lte=pow(radius_km / 9, 2)
    )
    context = dict(location=queryset)
    return render_to_response('connect/home.html', context)

Поместить radius как число в полезную нагрузку POST запроса в функции javascript, которая будет запрашивать POST для location_view.

Примечание.что этот расчет будет неточным на больших расстояниях и более неточным в приполярной области.Поймать этот 9 коэффициент вблизи радиуса?Это количество километров в одном градусе на экваторе).Для большей точности нам нужно углубиться в математику и использовать вычисленный коэффициент в соответствии с широтой пользователя.

Нужна большая точность?Просто используйте GeoDjango или сторонний API, например, геокодирование Google.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...