Django REST框架:由于大型相关表格,可浏览的UI速度慢

时间:2013-09-03 08:42:50

标签: user-interface django-rest-framework browsable

我的API中有一个模型,它有一个包含数万条记录的表的外键。当我在可浏览的UI中浏览到该模型的详细信息页面时,页面加载将永远占用,因为它正在尝试使用PUT命令的HTML表单的数万个条目填充外键下拉列表。

有没有解决这个问题?我认为我最好的解决方案是让可浏览的UI不显示此字段,从而防止缓慢加载。人们仍然可以通过实际的PUT api请求直接更新字段。

感谢。

4 个答案:

答案 0 :(得分:4)

查看使用自动填充小部件,或者使用简单的文本字段小部件。

此处的自动填充文档:http://www.django-rest-framework.org/topics/browsable-api/#autocomplete

答案 1 :(得分:1)

您可以使用简单的强制使用TextInput:

from django.forms import widgets
...
class YourSerializer(serializers.ModelSerializer):
    param = serializers.PrimaryKeyRelatedField(
        widget=widgets.TextInput
    )

或者在正确的autocomplete_light配置之后:

import autocomplete_light
...
class YourSerializer(serializers.ModelSerializer):
    paramOne = serializers.PrimaryKeyRelatedField(
        widget=autocomplete_light.ChoiceWidget('RelatedModelAutocomplete')
    )
    paramMany = serializers.PrimaryKeyRelatedField(
        widget=autocomplete_light.MultipleChoiceWidget('RelatedModelAutocomplete')
    )

过滤掉autocomplete_light this part of documentation返回的结果。

答案 2 :(得分:1)

请注意,您可以禁用HTML表单,并使用以下命令保留原始数据json条目:

class BrowsableAPIRendererWithoutForms(BrowsableAPIRenderer):
    """Renders the browsable api, but excludes the forms."""
    def get_rendered_html_form(self, data, view, method, request):
        return None

和settings.py:

REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES': (
        'rest_framework.renderers.JSONRenderer',
        'application.api.renderers.BrowsableAPIRendererWithoutForms',
    ),
}

这将加快速度,您仍然可以从可浏览的ui中发布。

答案 3 :(得分:0)

这是一个很好的问题,没有明显的问题。您在学习Django时会迷上的错误假设,以及在阅读官方文档时与之相关的插件DRF的错误假设,将创建一个概念模型,但这并不是真的。我在这里谈论的是,Django是为关系数据库显式设计的,并不能立即使用!

问题

ORM世界中查询包含关系(例如一对多)的模型时,Django / DRF变慢的原因被称为 N + 1问题N+1N+1),当ORM使用lazy loading时,它的特殊性很明显-Django使用延迟加载!!!

示例

我们假设您的模型如下所示:阅读器具有许多书籍。现在,您想获取所有由'hardcore'读者阅读的书籍'title'。在Django中,您可以通过以这种方式与ORM交互来执行此操作。

# First Query: Assume this one query returns 100 readers.
> readers = Reader.objects.filter(type='hardcore')

# Constitutive Queries
> titles = [reader.book.title for reader in readers]

引擎盖下。第一条语句Reader.objects.filter(type='hardcore')将创建一个看起来与此类似的SQL查询。我们假设它将返回100条记录。

SELECT * FROM "reader" WHERE "reader"."type" = "hardcore";

接下来,您将为每个读者[reader.book.title for reader in readers]获取相关书籍。在SQL中看起来与此类似。

SELECT * FROM "book" WHERE "book"."id" = 1;
SELECT * FROM "book" WHERE "book"."id" = 2;
...
SELECT * FROM "book" WHERE "book"."id" = N;

剩下的是, 1 选择以获取100个读者,而 N 选择获取书籍-其中 N 是书籍数量。因此,您总共有 N + 1 个针对数据库的查询。

此行为的结果是针对数据库的 101 查询,最终导致极少量的数据加载时间以及Django变慢的结果!

解决方案

解决方案很简单,但并不明显。遵循DjangoDRF的官方文档并不能突出问题。最后,您将遵循最佳实践,最终会导致应用缓慢。

要解决加载缓慢的问题,您将必须eager load在Django中使用数据。通常,这意味着使用适当的prefetch_related()select_related()方法在模型/表上构造SQL INNER JOIN,并仅在2个查询中而不是101中获取所有数据。

相关阅读