Python 在Django REST ListAPI视图中分页原始SQL查询的最佳方法?
我有一个原始SQL查询,用于为Django REST ListAPI视图构建查询集。大致如下,请原谅那些毫无意义的名字:Python 在Django REST ListAPI视图中分页原始SQL查询的最佳方法?,python,sql,django,pagination,django-rest-framework,Python,Sql,Django,Pagination,Django Rest Framework,我有一个原始SQL查询,用于为Django REST ListAPI视图构建查询集。大致如下,请原谅那些毫无意义的名字: class MyView(ListAPIView): serializer_class = MySerializer paginate_by = 10 def get_queryset(self): params = { "uid": str(self.request.user.id),
class MyView(ListAPIView):
serializer_class = MySerializer
paginate_by = 10
def get_queryset(self):
params = {
"uid": str(self.request.user.id),
"param": str(self.kwargs['param'])
}
query = 'SELECT f.id ' \
'FROM myapp_foo f, myapp_bar b ' \
'WHERE b.foo_id = f.id AND ' \
'b.param >= %(param)s AND ' \
'f.dt_tm >= NOW() AND ' \
'(SELECT COUNT(*) FROM myapp_baz z ' \
'WHERE z.user_id = %(uid)s AND ' \
'z.qux_id = f.qux_id) = 0 ' \
'ORDER BY f.dt_tm;'
return Foo.objects.raw(query, params)
这会产生以下错误:
object of type 'RawQuerySet' has no len()
我希望使用类似的SQL查询计算计数,然后使用LIMIT和OFFSET参数进行分页。我读过一些建议,其中列表项被计数以获得len,但这似乎并不令人满意,因为除非查询中有一个小的限制,否则这将是低效的,在任何情况下都会破坏分页的目的
更新:
我刚刚注意到paginate_by正在等待弃用
首先,如何向返回的对象添加计数方法?如果在返回原始查询集之前将其强制转换为列表,则应防止“RawQuerySet”没有len错误 正如您所说,这将是低效的,因为它将加载整个queryset
可以编写一个自定义分页类,使用limit和offset有效地分页,并在视图中与属性一起使用。如果在返回原始查询集之前将其强制转换为列表,则应防止“RawQuerySet”没有len错误 正如您所说,这将是低效的,因为它将加载整个queryset
可以编写一个自定义分页类,使用limit和offset高效地分页,并在视图中使用该类的属性。比其他替代方法更有效的解决方案是编写自己的RawQuerySet替换。我正在显示下面的代码,但您也可以。绝对不能保证没有错误;尽管如此,我还是在Python3上的Django 1.11中使用它,并使用PostgreSQL作为数据库;也应该使用MySQL。简单地说,这个类将相应的LIMIT和OFFSET子句添加到原始SQL查询中。没有什么疯狂的,只是一些简单的字符串连接,所以请确保不要在原始SQL查询中包含这些子句 班级 自定义分页类 为了完成,我还包括一个分页类:
from rest_framework.pagination import PageNumberPagination
class MyModelResultsPagination(PageNumberPagination):
"""Fixed page-size pagination with 10 items."""
page_size = 10
max_page_size = 10
您的ListAPIView
一句警告的话
PaginatedRawQuerySet类虽然对我来说很有用,但还没有经过广泛的测试,但我相信它确实提供了一个解决方案的概念,该解决方案比为每次调用选择queryset中的所有项更有效
您可能会注意到,RawQuerySet中最初缺少一个自定义计数方法实现,它是通过调用self.model.objects.count来计算的。如果没有这种方法,paginator将评估lenyour_raw_queryset,这将对性能产生与其他答案相同的影响
这个类并不是RawQuerySet的一刀切的替代品,这意味着您应该添加自己的定制以使其适合您的需要
例如,如果您需要更复杂的内容,您可以向PaginatedRawQuerySet类添加另一个属性,称为raw_count_query,该属性随后将被称为inside count,而不是像现在这样对所有对象进行计数,这将在需要过滤的情况下使用;raw_count_查询将根据您的条件提供对子集进行计数的SQL。比其他替代方法更有效的解决方案是编写自己的RawQuerySet替换。我正在显示下面的代码,但您也可以。绝对不能保证没有错误;尽管如此,我还是在Python3上的Django 1.11中使用它,并使用PostgreSQL作为数据库;也应该使用MySQL。简单地说,这个类将相应的LIMIT和OFFSET子句添加到原始SQL查询中。没有什么疯狂的,只是一些简单的字符串连接,所以请确保不要在原始SQL查询中包含这些子句 班级 自定义分页类 为了完成,我还包括一个分页类:
from rest_framework.pagination import PageNumberPagination
class MyModelResultsPagination(PageNumberPagination):
"""Fixed page-size pagination with 10 items."""
page_size = 10
max_page_size = 10
您的ListAPIView
一句警告的话
PaginatedRawQuerySet类虽然对我来说很有用,但还没有经过广泛的测试,但我相信它确实提供了一个解决方案的概念,该解决方案比为每次调用选择queryset中的所有项更有效
您可能会注意到,RawQuerySet中最初缺少一个自定义计数方法实现,它是通过调用self.model.objects.count来计算的。如果没有这种方法,paginator将评估lenyour_raw_queryset,这将对性能产生与其他答案相同的影响
这个类并不是RawQuerySet的一刀切的替代品,这意味着您应该添加自己的定制以使其适合您的需要
例如,如果您需要更复杂的内容,您可以向PaginatedRawQuerySet类添加另一个属性,称为raw_count_query,该属性随后将被称为inside count,而不是像现在这样对所有对象进行计数,这将在需要过滤的情况下使用;拉乌公司
unt\U查询将提供SQL,以根据您的条件计算子集。我也有同样的问题,我刚刚发现使用raw可以使用额外的:
(...)
return Foo.objects.extra(where=query, params=params)
附加变量
where=['data->>"$.SOMETHING" = %s OR data->>"$.SOMETHING" = %s OR data->>"$.SOMETHING" = %s', 'data->>"$.GROUP" LIKE %s']
params=['EX1', 'EX2', 'EX3', '%EXEMPLE4%']
注意:主要问题是使用与QuerySet具有相同属性的RawQuerySet,如果可能的话,最好的方法是使用QuerySet API的额外部分 我也遇到了同样的问题,我只是发现使用raw可以使用额外的:
(...)
return Foo.objects.extra(where=query, params=params)
附加变量
where=['data->>"$.SOMETHING" = %s OR data->>"$.SOMETHING" = %s OR data->>"$.SOMETHING" = %s', 'data->>"$.GROUP" LIKE %s']
params=['EX1', 'EX2', 'EX3', '%EXEMPLE4%']
注意:主要问题是使用与QuerySet具有相同属性的RawQuerySet,如果可能的话,最好的方法是使用QuerySet API的额外部分 我认为使用上限会限制查询集的大小。到目前为止,我还没有找到一个使用原始SQL查询进行自定义分页的好例子,我想使用上限会限制查询集的大小。到目前为止,我还没有找到一个使用原始SQL查询进行自定义分页的好例子。非常详细,说明也很好!非常详细和指导!
where=['data->>"$.SOMETHING" = %s OR data->>"$.SOMETHING" = %s OR data->>"$.SOMETHING" = %s', 'data->>"$.GROUP" LIKE %s']
params=['EX1', 'EX2', 'EX3', '%EXEMPLE4%']