Фильтрация вложенных связанных объектов в Django - PullRequest
0 голосов
/ 31 января 2019

У меня довольно простые отношения с 3 моделями, и у меня возникают проблемы при построении набора запросов, который возвращает результаты так, как они мне нужны.

Модели:


class Book(models.Model):
    name = models.CharField()

class CategoryLabel(models.Model):
    name = models.CharField()

class Review(models.Model):
    user = models.ForeignKey(User)
    book = models.ForeignKey(Book)
    labels = models.ManyToManyField(CategoryLabel)
    rating = models.IntegerField()

Теперь результаты набора запросовдолжен быть список книг, о которых конкретный пользователь написал рецензию.

Это легко достижимо с помощью books = Book.objects.filter(review__user=user)

Сложность в том, что мне нужно вывести (через сериализатор django rest framework)) список книг, каждая из которых содержит список отзывов только для указанного пользователя, агрегированный средний рейтинг (опять же, только для указанного пользователя) и набор отдельных меток, с которыми были отмечены отзывы.

Итак, что-то вроде этого в JSON:

[
    "name": "Book1",
    "reviews": [
        "review_id1", "review_id2"
    ],
    "average_rating": 3,
    "labels": [
        "label_id1", "label_id2"
    ]
]

Что я пробовал до сих пор:

Book.objects.filter(reviews__user=user)
            .annotate(average_rating=Avg("reviews__rating"))
            .annotate(labels=ArrayAgg("reviews__labels", distinct=True))

Это дает нужный мне формат, ноон объединяет средний рейтинг по всем обзорам, не только по user и той же истории с ярлыками, он просто включает все ярлыки, примененные во всех обзорах ... РЕДАКТИРОВАТЬ: это не такправда ..

Есть идеи, как этого добиться максимально эффективно?База данных - это PostgreSQL, поэтому можно использовать специальные функции pg.

Ответы [ 2 ]

0 голосов
/ 01 февраля 2019

Глупо, но код из оригинального вопроса действительно работает.У меня была другая проблема, не связанная с этим примером ...: /

В исходном вопросе было неверное утверждение:

This does produce the format I need but it aggregates average rating over all reviews, not only the ones by the user and the same story with labels, it just includes all labels applied in all reviews...

На самом деле этоне так это, кажется, работает правильно:

Book.objects.filter(reviews__user=user)
            .annotate(average_rating=Avg("reviews__rating"))
            .annotate(labels=ArrayAgg("reviews__labels", distinct=True))
0 голосов
/ 31 января 2019

В соответствии с вашими моделями и описанием вашей проблемы, звучит так, будто вы хотите групповые обзоры по книгам с отфильтрованными отзывами о конкретном пользователе:

Review.objects.filter(user=user).values('book__name').annotate(
    average_rating=Avg(F('rating'))
)

>>> <QuerySet [{'book__name': 'The ultimate code', 'average_rating': 7.0}]>

Извините, я не тестировал ArrayAgg, который являетсяСпециальная функция агрегации postgresSQL.

Приведенный выше код был выполнен для базы данных, содержащей 2 отзыва, один с рейтингом 10, а другой с 4.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...