简体   繁体   中英

Django Rest how to order on a model method

Using Django Rest Framework is it possible to order on a model method? I have tried it and DRF requires it to be field. So gives the error:

Cannot resolve keyword 'distance' into field.

View class ActivityListView(ListCreateView):

    queryset = model_activity.objects.all()
    serializer_class = ActivityListSerializer
    filter_backends = (OrderingFilter,)
    # default ordering
    ordering = ('distance')

Serializer

class ActivityListSerializer(serializers.ModelSerializer):

    distance = serializers.SerializerMethodField('_get_distance')

    class Meta:
        model = model_activity
        fields = ('id', 'distance')

    def _get_distance(self, obj):
        return obj.get_distance(user=self.context['request'].user)

I think the answer is no, the OrderingFilter helps you to generate the queryset(essentially SQL).

but ordering by a model method means the django needs to load all the records from the database to the memory, then order it in the memory.

If you have a amount of records there, your scrips will extremely slow.

can you implement it in a SQL function? so that you can annotate the value by SQL function, then order by it.

from django.db import models

class ActivityListView(ListCreateView):

    queryset = model_activity.objects.all()
    serializer_class = ActivityListSerializer
    filter_backends = (OrderingFilter,)
    # default ordering
    ordering = ('distance')

    def filter_queryset(self, queryset):
        cur_latitude, cur_longitude = self.get_cur_latitude_longitude()
        # add the distance field to sql dataset result, so that we can order by it
        queryset = queryset.annotate(
            distance=models.Func(cur_latitude, cur_longitude, 'latitude', 'longitude', function='get_distance')
        )

        return super(ActivityListView, self).filter_queryset(self, queryset)

and don't forget to create the sql function, here's a example for postgresql:

CREATE OR REPLACE FUNCTION public.geodistance(alat float8, alng float8, blat float8, blng float8)
  RETURNS float8
AS
$BODY$
  SELECT
        case when $1 is null or $2 is null or $3 is null or $4 is null
        then 9999999
        else
            asin(
            sqrt(
                sin(radians($3-$1)/2)^2 +
                sin(radians($4-$2)/2)^2 *
                cos(radians($1)) *
                cos(radians($3))
            )
            ) * 7926.3352
        end
        AS distance;
$BODY$
LANGUAGE sql IMMUTABLE;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM