Модели 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
, поэтому вы можете достичь ожидаемых результатов, используя следующее:
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())