instalar - Django REST Framework: configuración de la recuperación previa para los serializadores anidados
django rest framework with django 2 (1)
Mi aplicación Django con una API DRF funciona bien, pero he comenzado a tener problemas de rendimiento a medida que la base de datos se llena con los datos reales. Hice algunos perfiles con Django Debug Toolbar y descubrí que muchos de mis endpoints emiten decenas o cientos de consultas en el curso de la devolución de sus datos.
Esperé esto, ya que no había optimizado previamente nada con respecto a las consultas de la base de datos. Ahora que estoy configurando la captación previa, sin embargo, tengo problemas para utilizar los datos del serializador previamente extraído correctamente cuando ese serializador está anidado en un serializador diferente. He estado utilizando esta increíble publicación como una guía sobre cómo pensar sobre las diferentes formas de captación previa.
Actualmente, mi serializador ReadingGroup
realiza una ReadingGroup
adecuada cuando /api/readinggroups/
endpoint. Mi problema es /api/userbookstats/
endpoint, que devuelve todos los objetos UserBookStats
. El serializador relacionado, UserBookStatsSerializer
, tiene un ReadingGroupSerializer
anidado.
Los modelos, serializadores y conjuntos de vistas son los siguientes:
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
la ReadingGroup
para el ReadingGroup
final de ReadingGroup
(en realidad ReadingGroup
sobre la eliminación de consultas duplicadas para ese punto final aquí ), y ahora estoy trabajando en el UserBookStats
final UserBookStats
.
El problema que estoy teniendo es que, con mi setup_eager_loading
actual en el UserBookStatsSerializer
, no parece utilizar la configuración de captación previa por el método de carga ansioso en ReadingGroupSerializer
. Todavía estoy un poco confuso con la sintaxis del objeto Prefetch
. Me inspiré en esta excelente respuesta para probar ese enfoque.
Obviamente, el método get_queryset
de UserBookStatsViewset
no llama a setup_eager_loading
para los objetos ReadingGroup
, pero estoy seguro de que hay una manera de lograr la misma setup_eager_loading
ReadingGroup
.
prefetch_related()
admite la captación previa de relaciones internas mediante el uso de la sintaxis de subrayado doble:
queryset = queryset.prefetch_related(''reading_group'', ''reading_group__users'', ''reading_group__owner'')
No creo que Django REST proporcione soluciones elegantes listas para usar para buscar todos los campos necesarios automáticamente.