Эффективный метод, чтобы получить все много ко многим объектам из набора запросов - PullRequest
1 голос
/ 31 октября 2019

У меня есть модели, подобные приведенным ниже:

class Tag(models.Model):
    text = models.CharField(max_length=30)

class Post(models.Model):
    title = models.CharField(max_length=30)
    tags = models.ManyToManyField(Tag)

A Post может иметь много Tags, а Tags может быть связано со многими Posts.

ЧтоМне нужно, чтобы получить список всех сообщений вместе со всеми тегами, связанными с каждым сообщением. Затем я создаю Pandas DataFrame из этих данных. Вот как я это делаю в настоящее время:

qs = Post.objects.all().prefetch_related('tags')

tag_df = pd.DataFrame(columns=["post_id", "tags"])
for q in qs:
    tag_df = tag_df.append(
        {
            "post_id": q.pk,
            "tags": list(q.tags.all().values_list("text", flat=True)),
        },
        ignore_index=True,
    )

post_df = pd.DataFrame(qs.values("id", "title"))
final_df = post_df.merge(tag_df, left_on="id", right_on="post_id")

Результат является верным с точки зрения данных, которые мне требуются. Проблема в том, насколько это неэффективно и сколько запросов выполняется, хотя я использую prefetch_related. Похоже, что запрос попадает в базу данных для каждой итерации цикла.

Есть ли лучший, более эффективный способ сделать это (возможно, без циклов)? В конце концов, все, что мне нужно, это информационный фрейм, который содержит все посты и столбец со списком тегов для каждого поста.

1 Ответ

1 голос
/ 31 октября 2019

Используя .values_list(..), вы будете делать дополнительный запрос на каждую итерацию. Так что это не очень эффективно. Вы можете просто использовать уже выбранные объекты Tag и получить атрибуты .text:

qs = Post.objects.prefetch_related('tags')

tag_df = pd.DataFrame(columns=['post_id', 'tags'])
for q in qs:
    tag_df = tag_df.append(
        {
            'post_id': q.pk,
            'tags': <b>[t.text for t in q.tags.all()]</b>,
        },
        ignore_index=True,
    )

post_df = pd.DataFrame(qs.values('id', 'title'))
final_df = post_df.merge(tag_df, left_on='id', right_on='post_id')

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

qs = Post.objects.prefetch_related('tags')

data = [
    {'id': q.pk, 'title': q.title, 'tags': [t.text for t in q.tags.all()]}
    for q in qs
]
final_df= <b>pd.DataFrame(data, columns=['id', 'title', 'tags'])</b>

Обратите внимание, что использование .values(..) или .values_list(..) не очень хорошая идея. Только в определенных случаях, таких как создание GROUP BY для определенного значения, это хорошая идея. Обычно лучше использовать объекты модели, так как они добавляют дополнительный уровень логики.

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