DRF:如何创建自定义 FilterSet 以按距离过滤最近的用户
我正在尝试使用django-filter
来创建用于通过距离过滤附近用户过滤附近用户的自定义 例如,如果我发送
想让所有附近的用户 等于
get/api/list/?dance = 300
,我
latitude = models.DecimalField( # [-90.000000, 90.000000]
max_digits=8,
decimal_places=6,
null=True
)
longitude = models.DecimalField( # [-180.000000, 180.000000]
max_digits=9,
decimal_places=6,
null=True
)
objects = ClientManager()
300m > clientmanager 具有从模型中获取坐标的功能:
def get_geo_coordinates(self, pk):
"""
:param pk: - client id
:return: client's coords
"""
instance = self.get(pk=pk)
data = (instance.latitude, instance.longitude)
return data
我的getListapiview
class GetClientListAPIView(ListAPIView):
"""
Returns list with filtering capability
Available filter fields:
gender, first_name, last_name, distance
"""
serializer_class = ClientSerializer
queryset = Client.objects.all()
permission_classes = [IsAuthenticated]
filter_backends = [DjangoFilterBackend]
filter_class = ClientFilter
我的clientfilter
class ClientFilter(FilterSet):
distance = filters.NumberFilter(method='get_nearest_clients')
def get_nearest_clients(self, queryset, name, value):
sender_coords = Client.objects.get_geo_coordinates(pk=self.request.user.id)
test_coords = Client.objects.get_geo_coordinates(pk=31)
dist = get_great_circle_distance(sender_coords, test_coords)
class Meta:
model = Client
fields = ['gender', 'first_name', 'last_name']
在这里,我正在使用我的功能来计算两个客户端之间的距离:
def get_great_circle_distance(first_coords, second_coords):
"""
:param first_coords: (first_client_latitude, first_client_longitude) in degrees
:param second_coords: (second_client_latitude, second_client_longitude) in degrees
:return: distance
"""
earth_radius = 6_400_000 # in metres
la_1, lo_1 = map(radians, first_coords)
la_2, lo_2 = map(radians, second_coords)
coefficient = acos(
cos(la_1) * cos(la_2) * cos(lo_1 - lo_2) +
sin(la_1) * sin(la_2)
)
distance = earth_radius * coefficient
return distance
我不知道如何过滤QuerySet
,并从数据库访问侧面进行最佳操作。
I'm trying to create custom FilterSet
for filtering nearby users by distance using django-filter
For example if I sendGET /api/list/?distance=300
, I want to get all nearby users who are lower or equals to 300m far away
My model has 2 fields:
latitude = models.DecimalField( # [-90.000000, 90.000000]
max_digits=8,
decimal_places=6,
null=True
)
longitude = models.DecimalField( # [-180.000000, 180.000000]
max_digits=9,
decimal_places=6,
null=True
)
objects = ClientManager()
My ClientManager
has function for getting coords from model:
def get_geo_coordinates(self, pk):
"""
:param pk: - client id
:return: client's coords
"""
instance = self.get(pk=pk)
data = (instance.latitude, instance.longitude)
return data
My GetListAPIView
class GetClientListAPIView(ListAPIView):
"""
Returns list with filtering capability
Available filter fields:
gender, first_name, last_name, distance
"""
serializer_class = ClientSerializer
queryset = Client.objects.all()
permission_classes = [IsAuthenticated]
filter_backends = [DjangoFilterBackend]
filter_class = ClientFilter
My ClientFilter
class ClientFilter(FilterSet):
distance = filters.NumberFilter(method='get_nearest_clients')
def get_nearest_clients(self, queryset, name, value):
sender_coords = Client.objects.get_geo_coordinates(pk=self.request.user.id)
test_coords = Client.objects.get_geo_coordinates(pk=31)
dist = get_great_circle_distance(sender_coords, test_coords)
class Meta:
model = Client
fields = ['gender', 'first_name', 'last_name']
Here I'm using my function for calculating distance between two clients:
def get_great_circle_distance(first_coords, second_coords):
"""
:param first_coords: (first_client_latitude, first_client_longitude) in degrees
:param second_coords: (second_client_latitude, second_client_longitude) in degrees
:return: distance
"""
earth_radius = 6_400_000 # in metres
la_1, lo_1 = map(radians, first_coords)
la_2, lo_2 = map(radians, second_coords)
coefficient = acos(
cos(la_1) * cos(la_2) * cos(lo_1 - lo_2) +
sin(la_1) * sin(la_2)
)
distance = earth_radius * coefficient
return distance
I do not know how to filter the queryset
and do it optimally from the database accesses side.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
I would recommend using the existing tools that solved this problem a long time ago. Doing accurate (and efficient) distance calculations on an irregularly shaped sphere is more complicated than this.
https://docs.djangoproject.com/en/4.0 /ref/conver/gis/install/postgis/
gis字段上的gis字段:
这为您提供了直接在querySet上进行距离计算的正确工具,并且在数据库侧AFAIK上进行了计算。( https://djangoproject.com/44.0/ref /contrib/gis/tutorial/#spatial-queries)
It is a bit more overhead to setup GIS properly but it's worth the effort.
Sidenote:可以通过QuerySet
Annotate()
和q
和f
表达方式手工做。过滤django-filter
在您尝试的情况下,在客户端上过滤,几乎会失败的目的是首先使用django-filter
。希望有助于获得更好的理解。I would recommend using the existing tools that solved this problem a long time ago. Doing accurate (and efficient) distance calculations on an irregularly shaped sphere is more complicated than this.
https://docs.djangoproject.com/en/4.0/ref/contrib/gis/install/postgis/
GIS field on model:
This gives you the proper tools to do distance calculations directly on the queryset and the computation is done on the database side afaik.(https://docs.djangoproject.com/en/4.0/ref/contrib/gis/tutorial/#spatial-queries)
It is a bit more overhead to setup GIS properly but it's worth the effort.
Sidenote: It is possible to do it by hand via queryset
annotate()
andQ
andF
expressions but as I said it's tricky to get right. Filteringdjango-filter
on the client-side, as you attempted there, pretty much defeats the purpose of usingdjango-filter
in the first place. Hope that helps getting a better understanding.我解决了这个问题
I fixed this issue