Django 如何对嵌套的 ORM 和非 ORM 对象进行排序和分页
Django how to sort and paginate nested ORM and non-ORM object
我需要进行查询以获取一些属性,并使用其 category
FK 在另一个 table 中获取每月费用的数据。有了结果,需要按天、分期、总价和到期日计算价值。
一切正常,但需要按属性模型的某些字段对数据进行排序,按总价排序并对结果进行分页。
我可以同时进行排序和分页,但不能同时进行。
另外,我想问一下那些带installments
的计算是放在serializer.py
上是对的,还是应该移到viewset.py
或其他地方?
到目前为止,这是我的代码:
ViewSet.py 分页:
class MyViewSet(viewsets.ModelViewSet):
queryset = MyModel.Property.objects.filter(status)\
.prefetch_related('owner__user')\
.prefetch_related('host__user')\
.prefetch_related('category__user')
serializer_class = MySerializer
pagination_class = StandardResultsSetPagination
filterset_class = MyFilterSet
filter_backends = [filters.OrderingFilter, dj_filters.DjangoFilterBackend]
def get_queryset(self):
today = str(datetime.now())
start_date = self.request.query_params.get('start_date')
end_date = self.request.query_params.get('end_date')
'''
validate start_date and end_date
'''
return self.queryset
ViewSet.py 排序:
class MyViewSet(viewsets.ModelViewSet):
queryset = MyModel.Property.objects.filter(status)\
.prefetch_related('owner__user')\
.prefetch_related('host__user')\
.prefetch_related('category__user')
serializer_class = MySerializer
# pagination_class = StandardResultsSetPagination
filterset_class = MyFilterSet
filter_backends = [filters.OrderingFilter, dj_filters.DjangoFilterBackend]
def get_queryset(self):
today = str(datetime.now())
start_date = self.request.query_params.get('start_date')
end_date = self.request.query_params.get('end_date')
'''
validate start_date and end_date
'''
return self.queryset
def list(self, request, *args, **kwargs):
response = super().list(request, args, kwargs)
ordering = request.query_params.get('ordering')
if ordering:
response.data = sorted(
response.data,
key=operator.itemgetter(ordering.replace('-', ''),)
)
if "-" in ordering:
response.data = sorted(
response.data,
key=lambda k: (k[ordering.replace('-', '')], ),
reverse=True
)
else:
response.data = sorted(
response.data,
key=lambda k: (k[ordering], )
)
return Response(response.data)
Serializer.py
class MySerializer(serializers.ModelSerializer):
monthly_price = serializers.SerializerMethodField()
total_price = serializers.SerializerMethodField()
class Meta:
model = MyModel.Property
def get_monthly_price(self, instance):
start_date = self.context['request'].query_params['start_date']
end_date = self.context['request'].query_params['end_date']
months=helper.calc_months(start_date, end_date)
'''
calc months between start_date and end_date
'''
prices = MyModel.Category_month_price.objects.filter(
category=instance.category_location.id,
month__in=months)
installments = helper.calc_installments(dates, start_date, prices)
'''
installments is a list of dicts with installments due dates and values like:
[{
"instalment": 0,
"value": 1000,
"due_date": 2022-01-01,
}, ...]
'''
total_price = sum([x['value'] for x in installments])
self.total_price = total_price
return installments
def get_total_price(self, instance):
return self.total_price
还在 list Method
尝试了分页器,但没有找到构建下一页和上一页的方法 link
from django.core.paginator import Paginator
from django.core.paginator import EmptyPage
from django.core.paginator import PageNotAnInteger
def list(self, ...) :
"""
...
"""
paginator = Paginator(response.data, 25)
page = self.request.GET.get('page')
try:
result = paginator.page(page)
except PageNotAnInteger:
result = paginator.page(1)
except EmptyPage:
result = paginator.page(paginator.num_pages)
return Response({
"count": "",
"next": "",
"previous": "",
"results": []
)
您尝试过在查询集中使用 order_by 吗?
我认为这是解决此问题的更好方法
class MyViewSet(viewsets.ModelViewSet):
queryset = MyModel.Property.objects.filter(status)\
.prefetch_related('owner__user')\
.prefetch_related('host__user')\
.prefetch_related('category__user')
serializer_class = MySerializer
# pagination_class = StandardResultsSetPagination
filterset_class = MyFilterSet
filter_backends = [filters.OrderingFilter, dj_filters.DjangoFilterBackend]
...
def list(self, request, *args, **kwargs):
response = super().list(request, args, kwargs)
ordering = request.query_params.get('ordering')
if ordering:
response.data = self.queryset.order_by('category__user')
return Response(response.data)
我尝试使用 order_by
向 return 数据显示表格
也许你稍微改变一下你的模型,使查询更容易
class MyModel(models.Model):
category= models.ForeignKey(User, on_delete=models.CASCADE)
# ...
class Meta:
ordering = ['category']
参考:https://docs.djangoproject.com/en/4.0/ref/models/options/
我找到了一个简单的解决方案:
class MyViewSet(viewsets.ModelViewSet):
queryset = MyModel.Property.objects.filter(status)\
.prefetch_related('owner__user')\
.prefetch_related('host__user')\
.prefetch_related('category__user')
serializer_class = MySerializer
filterset_class = MyFilterSet
filter_backends = [filters.OrderingFilter, dj_filters.DjangoFilterBackend]
def get_queryset(self):
today = str(datetime.now())
start_date = self.request.query_params.get('start_date')
end_date = self.request.query_params.get('end_date')
'''
validate start_date and end_date
'''
return self.queryset
def list(self, request, *args, **kwargs):
response = super().list(request, args, kwargs)
response.request = self.request
ordering = request.query_params.get('ordering')
# ORDERING WORKING
if ordering:
response.data = sorted(
response.data,
key=operator.itemgetter(ordering.replace('-', ''),)
)
if "-" in ordering:
response.data = sorted(
response.data,
key=lambda k: (k[ordering.replace('-', '')], ),
reverse=True
)
else:
response.data = sorted(
response.data,
key=lambda k: (k[ordering], )
)
# HERE IS ALL I NEED TO DO TO GET PAGINATION WORK
paginator = StandardResultsSetPagination()
result_page = paginator.paginate_queryset(response.data, request)
return paginator.get_paginated_response(result_page)
我需要进行查询以获取一些属性,并使用其 category
FK 在另一个 table 中获取每月费用的数据。有了结果,需要按天、分期、总价和到期日计算价值。
一切正常,但需要按属性模型的某些字段对数据进行排序,按总价排序并对结果进行分页。
我可以同时进行排序和分页,但不能同时进行。
另外,我想问一下那些带installments
的计算是放在serializer.py
上是对的,还是应该移到viewset.py
或其他地方?
到目前为止,这是我的代码:
ViewSet.py 分页:
class MyViewSet(viewsets.ModelViewSet):
queryset = MyModel.Property.objects.filter(status)\
.prefetch_related('owner__user')\
.prefetch_related('host__user')\
.prefetch_related('category__user')
serializer_class = MySerializer
pagination_class = StandardResultsSetPagination
filterset_class = MyFilterSet
filter_backends = [filters.OrderingFilter, dj_filters.DjangoFilterBackend]
def get_queryset(self):
today = str(datetime.now())
start_date = self.request.query_params.get('start_date')
end_date = self.request.query_params.get('end_date')
'''
validate start_date and end_date
'''
return self.queryset
ViewSet.py 排序:
class MyViewSet(viewsets.ModelViewSet):
queryset = MyModel.Property.objects.filter(status)\
.prefetch_related('owner__user')\
.prefetch_related('host__user')\
.prefetch_related('category__user')
serializer_class = MySerializer
# pagination_class = StandardResultsSetPagination
filterset_class = MyFilterSet
filter_backends = [filters.OrderingFilter, dj_filters.DjangoFilterBackend]
def get_queryset(self):
today = str(datetime.now())
start_date = self.request.query_params.get('start_date')
end_date = self.request.query_params.get('end_date')
'''
validate start_date and end_date
'''
return self.queryset
def list(self, request, *args, **kwargs):
response = super().list(request, args, kwargs)
ordering = request.query_params.get('ordering')
if ordering:
response.data = sorted(
response.data,
key=operator.itemgetter(ordering.replace('-', ''),)
)
if "-" in ordering:
response.data = sorted(
response.data,
key=lambda k: (k[ordering.replace('-', '')], ),
reverse=True
)
else:
response.data = sorted(
response.data,
key=lambda k: (k[ordering], )
)
return Response(response.data)
Serializer.py
class MySerializer(serializers.ModelSerializer):
monthly_price = serializers.SerializerMethodField()
total_price = serializers.SerializerMethodField()
class Meta:
model = MyModel.Property
def get_monthly_price(self, instance):
start_date = self.context['request'].query_params['start_date']
end_date = self.context['request'].query_params['end_date']
months=helper.calc_months(start_date, end_date)
'''
calc months between start_date and end_date
'''
prices = MyModel.Category_month_price.objects.filter(
category=instance.category_location.id,
month__in=months)
installments = helper.calc_installments(dates, start_date, prices)
'''
installments is a list of dicts with installments due dates and values like:
[{
"instalment": 0,
"value": 1000,
"due_date": 2022-01-01,
}, ...]
'''
total_price = sum([x['value'] for x in installments])
self.total_price = total_price
return installments
def get_total_price(self, instance):
return self.total_price
还在 list Method
尝试了分页器,但没有找到构建下一页和上一页的方法 link
from django.core.paginator import Paginator
from django.core.paginator import EmptyPage
from django.core.paginator import PageNotAnInteger
def list(self, ...) :
"""
...
"""
paginator = Paginator(response.data, 25)
page = self.request.GET.get('page')
try:
result = paginator.page(page)
except PageNotAnInteger:
result = paginator.page(1)
except EmptyPage:
result = paginator.page(paginator.num_pages)
return Response({
"count": "",
"next": "",
"previous": "",
"results": []
)
您尝试过在查询集中使用 order_by 吗?
我认为这是解决此问题的更好方法
class MyViewSet(viewsets.ModelViewSet):
queryset = MyModel.Property.objects.filter(status)\
.prefetch_related('owner__user')\
.prefetch_related('host__user')\
.prefetch_related('category__user')
serializer_class = MySerializer
# pagination_class = StandardResultsSetPagination
filterset_class = MyFilterSet
filter_backends = [filters.OrderingFilter, dj_filters.DjangoFilterBackend]
...
def list(self, request, *args, **kwargs):
response = super().list(request, args, kwargs)
ordering = request.query_params.get('ordering')
if ordering:
response.data = self.queryset.order_by('category__user')
return Response(response.data)
我尝试使用 order_by
向 return 数据显示表格也许你稍微改变一下你的模型,使查询更容易
class MyModel(models.Model):
category= models.ForeignKey(User, on_delete=models.CASCADE)
# ...
class Meta:
ordering = ['category']
参考:https://docs.djangoproject.com/en/4.0/ref/models/options/
我找到了一个简单的解决方案:
class MyViewSet(viewsets.ModelViewSet):
queryset = MyModel.Property.objects.filter(status)\
.prefetch_related('owner__user')\
.prefetch_related('host__user')\
.prefetch_related('category__user')
serializer_class = MySerializer
filterset_class = MyFilterSet
filter_backends = [filters.OrderingFilter, dj_filters.DjangoFilterBackend]
def get_queryset(self):
today = str(datetime.now())
start_date = self.request.query_params.get('start_date')
end_date = self.request.query_params.get('end_date')
'''
validate start_date and end_date
'''
return self.queryset
def list(self, request, *args, **kwargs):
response = super().list(request, args, kwargs)
response.request = self.request
ordering = request.query_params.get('ordering')
# ORDERING WORKING
if ordering:
response.data = sorted(
response.data,
key=operator.itemgetter(ordering.replace('-', ''),)
)
if "-" in ordering:
response.data = sorted(
response.data,
key=lambda k: (k[ordering.replace('-', '')], ),
reverse=True
)
else:
response.data = sorted(
response.data,
key=lambda k: (k[ordering], )
)
# HERE IS ALL I NEED TO DO TO GET PAGINATION WORK
paginator = StandardResultsSetPagination()
result_page = paginator.paginate_queryset(response.data, request)
return paginator.get_paginated_response(result_page)