Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/django/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Django REST框架:为嵌套序列化程序设置预取_Django_Django Rest Framework - Fatal编程技术网

Django REST框架:为嵌套序列化程序设置预取

Django REST框架:为嵌套序列化程序设置预取,django,django-rest-framework,Django,Django Rest Framework,我的Django驱动的带有DRFAPI的应用程序运行良好,但随着数据库中填充了实际数据,我开始遇到性能问题。我使用Django调试工具栏进行了一些评测,发现我的许多端点在返回数据的过程中会发出数十到数百个查询 我预料到了这一点,因为我以前没有对数据库查询进行过任何优化。但是,现在我正在设置预取,当序列化程序嵌套在不同的序列化程序中时,我很难使用正确预取的序列化程序数据。我一直在用它来指导如何思考不同的预取方法 目前,当我点击/api/readinggroups/端点时,我的ReadingGrou

我的Django驱动的带有DRFAPI的应用程序运行良好,但随着数据库中填充了实际数据,我开始遇到性能问题。我使用Django调试工具栏进行了一些评测,发现我的许多端点在返回数据的过程中会发出数十到数百个查询

我预料到了这一点,因为我以前没有对数据库查询进行过任何优化。但是,现在我正在设置预取,当序列化程序嵌套在不同的序列化程序中时,我很难使用正确预取的序列化程序数据。我一直在用它来指导如何思考不同的预取方法

目前,当我点击
/api/readinggroups/
端点时,我的
ReadingGroup
序列化程序确实正确预取了数据。我的问题是
/api/userbookstats/
端点,它返回所有
userbookstats
对象。相关的序列化程序,
UserBookStatsSerializer
,具有嵌套的
ReadingGroupSerializer

模型、序列化器和视图集如下所示:

型号.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)
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
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
视图.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)
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
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
端点的预取(实际上我发布了关于消除该端点重复查询的内容),现在我正在研究
UserBookStats
端点

我遇到的问题是,在
UserBookStatsSerializer
中当前的
设置\u eager\u加载
时,它似乎没有使用由
ReadingGroupSerializer
中的eager加载方法设置的预取。我对
预回迁
对象的语法仍然有点模糊,我是受了这个优秀答案的启发而尝试这种方法的

显然,
UserBookStatsViewset
get\u queryset
方法没有为
ReadingGroup
对象调用
setup\u eager\u loading
,但是我确信有一种方法可以完成相同的预取。

prefetch\u related()
支持使用双下划线语法预取内部关系:

queryset = queryset.prefetch_related('reading_group', 'reading_group__users', 'reading_group__owner') 

我不认为Django REST为自动获取所有必需字段提供了任何现成的解决方案。

手动预取所有嵌套关系的替代方案,还有一个名为的包,它将自动遍历模型和序列化程序上的相关字段,以查找所有需要在
prefetch\u related
select\u related
调用中提及的模型。您只需将
AutoPrefetchViewSetMixin
添加到您的视图集中:

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
对象的额外预取都可以添加到视图集中的
get\u queryset
方法中。

您是在询问优雅的解决方案还是其他解决方案
queryset=queryset.prefetch\u related('reading\u group'、'reading\u group\u users'、'reading\u group\u owner')
应该可以很好地工作。任何解决方案都会很好——哇,这太棒了。这很好,所以如果你把它作为答案提交,我会接受的。它取代了
Prefetch
对象,这正是我希望用于实现某种解耦预取的优雅解决方案的方法,但这确实很有效。谢谢