我的Django驱动的应用程序与DRF API工作正常,但我已经开始遇到性能问题,因为数据库填充了实际数据。我已经使用Django Debug Toolbar进行了一些分析,发现我的许多端点在返回数据的过程中会发出数十到数百个查询。
我预料到这一点,因为我之前没有针对数据库查询优化任何内容。但是,现在我已经设置了预取,但是当串行器嵌套在不同的串行器中时,我在使用正确预取的串行器数据时遇到了麻烦。我一直在使用此awesome post作为指导,了解如何考虑预取的不同方法。
目前,当我点击ReadingGroup
端点时,我的/api/readinggroups/
序列化程序会正确预取。我的问题是/api/userbookstats/
端点,它返回所有UserBookStats
个对象。相关的序列化程序UserBookStatsSerializer
具有嵌套的ReadingGroupSerializer
。
模型,序列化程序和视图集如下:
models.py
class ReadingGroup(models.model):
owner = models.ForeignKeyField(settings.AUTH_USER_MODEL)
users = models.ManyToManyField(settings.AUTH_USER_MODEL)
book_type = models.ForeignKeyField(BookType)
....
<other group related fields>
def __str__(self):
return '%s group: %s' % (self.name, self.book_type)
class UserBookStats(models.Model):
reading_group = models.ForeignKey(ReadingGroup)
user = models.ForeignKey(settings.AUTH_USER_MODEL)
alias = models.CharField()
total_books_read = models.IntegerField(default=0)
num_books_owned = models.IntegerField(default=0)
fastest_read_time = models.IntegerField(default=0)
average_read_time = models.IntegerField(default=0)
serializers.py
class ReadingGroupSerializer(serializers.ModelSerializer):
users = UserSerializer(many = True,read_only=True)
owner = UserSerializer(read_only=True)
class Meta:
model = ReadingGroup
fields = ('url', 'id','owner', 'users')
@staticmethod
def setup_eager_loading(queryset):
#select_related for 'to-one' relationships
queryset = queryset.select_related('owner')
#prefetch_related for 'to-many' relationships
queryset = queryset.prefetch_related('users')
return queryset
class UserBookStatsSerializer(serializers.HyperlinkedModelSerializer):
reading_group = ReadingGroupSerializer()
user = UserSerializer()
awards = AwardSerializer(source='award_set', many=True)
class Meta:
model = UserBookStats
fields = ('url', 'id', 'alias', 'total_books_read', 'num_books_owned',
'average_read_time', 'fastest_read_time', 'awards')
@staticmethod
def setup_eager_loading(queryset):
#select_related for 'to-one' relationships
queryset = queryset.select_related('user')
#prefetch_related for 'to-many' relationships
queryset = queryset.prefetch_related('awards_set')
#setup prefetching for nested serializers
groups = Prefetch('reading_group', queryset ReadingGroup.objects.prefetch_related('userbookstats_set'))
queryset = queryset.prefetch_related(groups)
return queryset
views.py
class ReadingGroupViewset(views.ModelViewset):
def get_queryset(self):
qs = ReadingGroup.objects.all()
qs = self.get_serializer_class().setup_eager_loading(qs)
return qs
class UserBookStatsViewset(views.ModelViewset):
def get_queryset(self):
qs = UserBookStats.objects.all()
qs = self.get_serializer_class().setup_eager_loading(qs)
return qs
我已经优化了ReadingGroup
端点的预取(我实际发布了关于消除该端点的重复查询here),现在我正在处理{{1端点。
我遇到的问题是,UserBookStats
中的当前setup_eager_loading
,似乎没有使用预先设置的预先设置的预取功能。 UserBookStatsSerializer
。我对ReadingGroupSerializer
对象的语法仍然有点朦胧 - 我受到了this尝试这种方法的出色答案的启发。
显然Prefetch
get_queryset
方法UserBookStatsViewset
没有为setup_eager_loading
对象调用ReadingGroup
,但我确定这样做的方式很明显完成相同的预取。
答案 0 :(得分:12)
prefetch_related()
支持使用双下划线语法预取内部关系:
queryset = queryset.prefetch_related('reading_group', 'reading_group__users', 'reading_group__owner')
我认为Django REST不提供任何优雅的解决方案,可以自动获取所有必要的字段。
答案 1 :(得分:0)
作为手动预取所有嵌套关系的替代方法,还有一个名为django-auto-prefetching
的软件包,它将自动遍历模型和序列化程序上的相关字段,以找到y
中需要提及的所有模型和prefetch_related
个呼叫。您需要做的就是将select_related
添加到您的ViewSet中:
AutoPrefetchViewSetMixin
可以在ViewSet的from django_auto_prefetching import AutoPrefetchViewSetMixin
class ReadingGroupViewset(AutoPrefetchViewSetMixin, views.ModelViewset):
def get_queryset(self):
qs = ReadingGroup.objects.all()
return qs
class UserBookStatsViewset(AutoPrefetchViewSetMixin, views.ModelViewset):
def get_queryset(self):
qs = UserBookStats.objects.all()
return qs
方法中添加具有更复杂的Prefetch
对象的任何其他预取。