web-dev-qa-db-fra.com

comment ajouter des données annotées dans les réponses à un ensemble de requêtes Django-rest-framework?

Je génère des agrégats pour chaque élément d'un QuerySet:

def get_queryset(self):
    from Django.db.models import Count
    queryset = Book.objects.annotate(Count('authors'))
    return queryset

Mais je n'obtiens pas le décompte dans la réponse JSON.

merci d'avance.

40
panchicore

L'ensemble de requêtes renvoyé par get_queryset fournit la liste des éléments qui passeront par le sérialiseur, qui contrôle la façon dont les objets seront représentés. Essayez d'ajouter un champ supplémentaire dans votre sérialiseur de livres, comme:

author_count = serializers.IntegerField(
    source='author_set.count', 
    read_only=True
)

Edit: Comme d'autres l'ont dit, ce n'est pas le moyen le plus efficace d'ajouter des décomptes pour les cas où de nombreux résultats sont retournés, car il frappera la base de données pour chaque instance. Voir la réponse de @ José pour une solution plus efficace.

30
Fiver

La solution acceptée frappera la base de données autant de fois que les résultats seront renvoyés. Pour chaque résultat, une requête count vers la base de données sera effectuée.

La question consiste à ajouter des annotations au sérialiseur, ce qui est beaucoup plus efficace que de faire une requête count pour chaque élément de la réponse.

Une solution pour cela:

models.py

class Author(models.Model):
    name = models.CharField(...)
    other_stuff = models...
    ...

class Book(models.Model):
    author = models.ForeignKey(Author)
    title = models.CharField(...)
    publication_year = models...
    ...

serializers.py

class BookSerializer(serializers.ModelSerializer):
    authors = serializers.IntegerField()

    class Meta:
        model = Book
        fields = ('id', 'title', 'authors')

views.py

class BookViewSet(viewsets.ModelViewSet):
    queryset = Book.objects.annotate(authors=Count('author'))
    serializer_class = BookSerializer
    ...

Cela fera le comptage au niveau de la base de données, en évitant de frapper la base de données pour récupérer le nombre d'auteurs pour chacun des éléments Book retournés.

62
José L. Patiño

La solution de Fiver atteindra la base de données pour chaque instance du jeu de requêtes, donc si vous avez un grand jeu de requêtes, sa solution créera beaucoup de requêtes.

Je remplacerais le to_representation de votre sérialiseur Book, il réutilise le résultat du ( annotation . Cela ressemblera à quelque chose comme:

class BookSerializer(serializers.ModelSerializer):
     def to_representation(self, instance):
        return {'id': instance.pk, 'num_authors': instance.authors__count}

    class Meta:
        model = Book
7
Tobias