#sql #django #many-to-many
Вопрос:
в джанго, если есть таблица со многими-многими полями, как это :
class Category(models.Model):
name = models.CharField(unique=True, max_length=255)
icon = models.ImageField(upload_to='images')
sport = models.ManyToManyField(Sport)
когда вы пытаетесь получить все объекты из этой таблицы, вы делаете это :
Category.objects.all()
и результат будет таким же после сериализации :
"data": [
{
"id": 1,
"name": '..'
"icon": '...'
"sport": [
1,
2
]
]
}
итак, мой вопрос здесь, как достичь того же результата с помощью чистого sql
Я попробовал это :
sql = '''select a.*,b.sport_id,array_agg(b.sport_id) as sport from category a join category_sport b on b.category_id=a.id group by a.id,b.sport_id ;'''
но результат был таким :
"data": [
{
"id": 1,
"name": '..'
"icon": '...'
"sport": [
1,
]
},
{
"id": 1,
"name": '..'
"icon": '...'
"sport": [
2
]
]
}
Комментарии:
1. я не понял вашего вопроса не будете ли вы, пожалуйста, более конкретны
2. я предполагаю, что вы хотите отобразить данные из базы данных в своем шаблоне
3. речь идет вовсе не о шаблоне django, а о sql, поэтому я хочу просто получить результат objects.all() в django в sql с множеством полей
Ответ №1:
Насколько я понимаю, если вам нужно добиться того , что вам просто нужно удалить b.sport_id
, вы хотите объединить sport_id
в массив для каждой категории, поэтому нет причин группироваться по этому полю, ваш SQL должен быть:
sql = '''select a.id, array_agg(b.sport_id) as sport from category a join category_sport b on b.category_id=a.id group by a.id;'''
Помните, что все неагрегированные выбранные поля должны отображаться в разделе «Группировать по».
Другой альтернативой, если вы используете PostgreSQL, может быть использование ArrayAgg
, что-то вроде:
from django.contrib.postgres.aggregates import ArrayAgg
Category.objects.aggregate(sport=ArrayAgg('sport__id'))
Кроме того, вы можете увидеть этот очень простой пример PostgreSQL 13:
Схема:
CREATE TABLE a (
id INT,
title VARCHAR(128)
);
CREATE TABLE b (
id INT
);
CREATE TABLE ab (
id INT,
aid INT,
bid INT
);
INSERT INTO a (id, title) VALUES (1, 'title_a');
INSERT INTO a (id, title) VALUES (2, 'title_b');
INSERT INTO b (id) VALUES (1);
INSERT INTO b (id) VALUES (2);
INSERT INTO b (id) VALUES (3);
INSERT INTO b (id) VALUES (4);
INSERT INTO b (id) VALUES (5);
INSERT INTO b (id) VALUES (6);
INSERT INTO ab (id, aid, bid) VALUES(1, 1, 1);
INSERT INTO ab (id, aid, bid) VALUES(2, 1, 3);
INSERT INTO ab (id, aid, bid) VALUES(3, 1, 5);
INSERT INTO ab (id, aid, bid) VALUES(4, 2, 2);
INSERT INTO ab (id, aid, bid) VALUES(5, 2, 4);
INSERT INTO ab (id, aid, bid) VALUES(6, 2, 6);
Запрос:
SELECT a.id, a.title, array_agg(ab.bid) AS bs FROM a JOIN ab ON a.id=ab.aid GROUP BY a.id, a.title;
Результат:
| id | title | bs |
| --- | ------- | ------- |
| 2 | title_b | [2,4,6] |
| 1 | title_a | [1,3,5] |
Вы можете протестировать его на https://www.db-fiddle.com/
Комментарии:
1. Вы не можете этого сделать, и вы получите ошибку с «b.sport_id» должен появиться в ГРУППЕ BY. Я думаю, что этого невозможно достичь всего за один sql-запрос
2. Вы правы! но это только потому, что вы включаете
b.sport_id
также в свой выбор, поэтому просто удалите его из выбора и group_by.Я обновил свой ответ, потому что я не понимал, что вы включаете это поле в первую очередь, я обновил свой ответ.3. но есть проблема, если мы не вставляли элементы в таблицу ab, что происходит, когда мы создаем новую запись в таблице a с нулевым значением в поле manytomany, поэтому в этом случае, когда мы выполняем запрос, мы не получим записи с нулевым значением в поле manytomany
4. а также я обнаружил, что выполнение запроса занимает больше времени, чем должно, поэтому, как вы думаете, есть другой способ повысить производительность, например, использовать массив вместо array_agg
5. Не уверен, хорошо ли я понял, но нет причин устанавливать нулевое значение в поле ManyToManyField, помните, что это не реальное поле в базе данных, это менеджер , аналогичный
.objects
для моделей, вы можете использовать его для выполнения операций, таких как запросы или обновления в промежуточной таблице, вы можете увидеть здесь очень хорошие примеры