我是一个反应前端,django后端(用作REST后退)。我继承了应用程序,并使用许多模型和序列化加载所有用户数据。它加载速度很慢。它使用过滤器来查询单个成员,然后将其传递给Serializer:
found_account = Accounts.objects.get(id='customer_id')
AccountDetailsSerializer(member, context={'request': request}).data
然后有很多种嵌套的Serializers:
AccountDetailsSerializers(serializers.ModelSerializer):
Invoices = InvoiceSerializer(many=True)
Orders = OrderSerializer(many=True)
....
通过查看日志,看起来ORM会发出如此多的查询,这很疯狂,对于某些端点,我们最终会得到50-60个查询。
没有详细查看代码(和分析结果)就没有银弹。
唯一明智的做法是在模型和数据库中强制执行关系。这可以防止出现大量的错误,鼓励使用标准化的高性能访问(而不是在现场调制SQL,而这通常是错误和缓慢的),并使您的代码更短,更易读。
除此之外,50-60个查询可以是很多(如果你可以做一两个相同的工作)或者它可能是正确的 - 它取决于你用它们实现了什么。
使用prefetch_related
和select_related
很重要,是的 - 但只有正确使用才能使用;否则它会减慢你的速度,而不是加速你。
如果您需要数据,嵌套序列化程序是正确的方法 - 但如果您希望它们快速,则需要在视图集中正确设置查询集。
计算慢速视图的主要部分,检查发送的SQL查询并检查是否确实需要返回的所有数据。
然后你可以看看疼痛部位,并在重要的地方获得时间。通过完整的代码示例询问有关SO的具体问题也可以帮助您快速实现。
如果您只有一个顶级对象,则可以优化@jensmtg提供的方法,在该级别执行所需的所有预取,然后使用访问预取对象的ModelSerializer
s(而不是SerializerMethodField
s)执行较低级别的预取。查看允许嵌套预取的Prefetch对象。
但要注意prefetch_related
不是免费的,它涉及Python中的一些处理;你可能最好使用values()
和values_list
进行平面(类似db-view)的连接查询。
我建议你最初看看你用prefetch_related
得到的效果。它可能会对加载时间产生重大影响,并且实现起来相当简单。通过上面的示例,可以单独减少加载时间:
AccountDetailsSerializers(serializers.ModelSerializer):
class Meta:
model = AccountDetails
fields = (
'invoices',
'orders',
)
invoices = serializers.SerializerMethodField()
orders = serializers.SerializerMethodField()
def get_invoices(self, obj):
qs = obj.invoices.all()\
.prefetch_related('invoice_sub_object_1')\
.prefetch_related('invoice_sub_object_2')
return InvoiceSerializer(qs, many=True, read_only=True).data
def get_orders(self, obj):
qs = obj.orders.all()\
.prefetch_related('orders_sub_object_1')\
.prefetch_related('orders_sub_object_2')
return OrderSerializer(qs, many=True, read_only=True).data
至于你的架构问题,我认为还有很多其他因素会影响你是否以及在何种程度上重构代码库。一般来说,如果你与Django和DRF结婚,如果你能够接受这些框架的习语和模式,而不是试图用自己的修复程序购买它们,那么你将获得更好的开发者体验。