Модели Django - Как агрегировать связанные объекты в массивы "многие-ко-многим"?

Так как я недавно начал работать с Django, у меня возникла ситуация с отношениями "многие-ко-многим" между книгой и автором - в качестве простого примера - у книги может быть несколько авторов.

class Book(models.Model):
    title = models.CharField(max_length=100)
    authors = models.ManyToManyField('Author', related_name='books')

    def __str__(self):
        return self.title

class Author(models.Model):
    name = models.CharField(max_length=50)

    def __str__(self):
        return self.name

Моя цель - получить данные о книге, но проблема в том, что когда у книги несколько авторов, я получаю одну и ту же строку книги для каждого из ее авторов, вопрос в том могу ли я иметь строку книги со всеми ее авторами, сохраненными в списке? вместо строки для каждого автора.

Пример получения данных в формате JSON:

[
    {
        "id": 1,
        "title": "Good Omens",
        "authors__name": "Neil Gaiman"
    },
    {
        "id": 1,
        "title": "Good Omens",
        "authors__name": "Terry Pratchett"
    },
    {
        "id": 2,
        "title": "The Lord of the Rings",
        "authors__name": "J.R.R. Tolkien"
    }
]

Так что вместо этого я хочу что-то вроде этого:

    {
        "id": 1,
        "title": "Good Omens",
        "authors__name": ["Neil Gaiman", "Terry Pratchett"]
    },

Хотя это легко реализовать с помощью кода на python, я хочу получить решение с использованием модельных запросов или методов ORM, чтобы выиграть немного производительности и времени, поскольку данные будут очень большими.

В следующем коде показан метод, отвечающий за получение данных, а также за их упорядочивание и фильтрацию, причем все это с помощью запросов:

def __model_query(self, model_class, fields=None, enum_fields=None, order_by=None, search=None, field_search=None):
        queryset = model_class.objects.all()

        # Apply ordering if specified
        if order_by:
            field_name, sort_order = order_by
            if sort_order == "desc":
                queryset = queryset.order_by(f"-{field_name}")
            elif sort_order == "asc":
                queryset = queryset.order_by(field_name)

        # Apply general search if specified
        if search:
            search_filters = Q()
            for field in fields:
                search_filters |= Q(**{f"{field}__icontains": search})
            
            queryset = queryset.filter(search_filters)

        # Apply field-specific search if specified
        if field_search:
            search_filters = Q()
            for field, value in field_search.items():
                if enum_fields and field in enum_fields:
                    search_filters |= Q(**{f"{field}__exact": value})
                else:
                    search_filters |= Q(**{f"{field}__icontains": value})
            
            queryset = queryset.filter(search_filters)

        return queryset

В настоящее время я использую sqlite3 db, поэтому я не могу использовать любой метод, связанный с Postgres db.

Я очень благодарен тем, кто может помочь мне с этим, я надеюсь, что предоставил все необходимые детали.

Я не пробовал много решений, только те, которые предоставил ChatGPT, но все они оказались неудачными.

Вы используете db.sqlite3, который не поддерживает ArrayAgg, поэтому вы можете достичь ожидаемых результатов, используя следующее:enter image description here

    books = Book.objects.values('id', 'title', 'authors__name')

    book_dict = {}
    for book in books:
        book_id = book['id']
        if book_id not in book_dict:
            book_dict[book_id] = {
                'id': book_id,
                'title': book['title'],
                'authors__name': []
            }
        if book['authors__name']:
            book_dict[book_id]['authors__name'].append(book['authors__name'])

    books_list = list(book_dict.values())
Вернуться на верх