Добавление упорядочивания в сериализаторе (Django)
У меня есть проблема, для которой я не уверен, что я получаю правильный путь к решению, надеюсь, вы, ребята, могли бы мне помочь.
Я добавил в свой сериализатор дополнительное поле, называемое distance (расстояние равно расстоянию в милях между двумя разными местами). Я хочу вернуть порядок бизнес-объекта в это новое поле, возможно ли это? Или я иду неверным путем для решения этой проблемы?
Внизу у вас есть мой Serializer и ModelViewSet
Serializer
class BusinessesSerializer(serializers.ModelSerializer):
distance = serializers.SerializerMethodField('get_location')
class Meta:
model= Businesses
fields = ('id', 'address_first_line', 'address_second_line',
'city', 'region', 'post_code', 'phone_number', 'logo', 'join_date', 'distance')
def get_location(self, business):
ip_info = requests.get('https://api64.ipify.org?format=json').json()
ip_address = ip_info["ip"]
response = requests.get(f'http://api.ipstack.com/{ip_address}?access_key=8eba29fcae0bbc63c1e93b8c370e4bcf').json()
latitude = response.get("latitude")
longitude = response.get("longitude")
first = (float(latitude), float(longitude))
second = (business.lat, business.long)
distance = great_circle(first, second).miles
return distance
ModelViewSet
class BusinessesViewSet(ModelViewSet):
serializer_class = BusinessesSerializer
queryset = Businesses.objects.all()
Вы не можете фильтровать в представлении по полю SerializerMethodField (или даже свойству объекта), так как фильтры Django работают на уровне базы данных.
Кроме того, вы не можете использовать annotate()
в этом случае, так как он не принимает функцию python. Самым простым решением, вероятно, будет просто добавить новое поле в вашу модель, где вы будете хранить это значение, и вы сможете легко фильтровать / заказывать по нему, когда захотите.
Вероятно, вам следует перенести вызовы внешних API для получения поля distance за пределы вашего сериализатора, поскольку вызов API является побочным эффектом и не должен выполняться в сериализаторе. Его цель - просто сериализовать данные, а не получать их из Интернета. Если вы выполняете эти вызовы на большом наборе данных, ваш код будет выполняться медленно и, возможно, будет заблокирован ipify.
Возможным решением может быть переход от ModelViewSet к обычному APIView и выполнение пользовательского запроса.
class BusinessesAPIView(APIView):
def get(self, request):
# Get IP info once
ip_info = requests.get('https://api64.ipify.org?format=json').json()
ip_address = ip_info["ip"]
response = requests.get(
f'http://api.ipstack.com/{ip_address}?access_key=8eba29fcae0bbc63c1e93b8c370e4bcf').json()
latitude = response.get("latitude")
longitude = response.get("longitude")
first = (float(latitude), float(longitude))
# Calculate distances for all businesses and pass them as a context to our serializer
businesses = Businesses.objects.all()
distances = {}
for business in businesses:
second = (business.lat, business.long)
distance = great_circle(first, second).miles
distances[business.id] = distance
# Sort by distance
businesses_processed = BusinessesSerializer(businesses, many=True, context={'distances': distances}).data
businesses_processed.sort(key=lambda x: x['distance'])
return Response({'businesses': businesses_processed})
class BusinessesSerializer(serializers.ModelSerializer):
distance = serializers.SerializerMethodField()
class Meta:
model= Businesses
fields = ('id', 'address_first_line', 'address_second_line',
'city', 'region', 'post_code', 'phone_number', 'logo', 'join_date', 'distance')
# Get distance by business id from context we passed from our APIView
def get_distance(self, business):
return self.context['distances'][business.id]