Запрос Django - как получить список словарей с отношением M2M? - PullRequest
1 голос
/ 17 ноября 2011

Допустим, у меня есть это простое приложение с двумя моделями - Tag и SomeModel

class Tag(models.Model):
  text = ...

class SomeModel(models.Model):
  tags = models.ManyToManyField(Tag, related_name='tags')

И я хочу получить что-то подобное из базы данных:

[{'id': 1, 'tags': [1, 4, 8, 10]}, {'id': 6, 'tags': []}, {'id': 8, 'tags': [1, 2]}]

Это список нескольких словарей SomeModel с идентификаторами SomeModel и идентификаторами тегов.

Как должен выглядеть запрос Django? Я попробовал это:

>>> SomeModel.objects.values('id', 'tags').filter(pk__in=[1,6,8])
[{'id': 1, 'tags': 1}, {'id': 1, 'tags': 4}, {'id': 1, 'tags': 8}, ...]

Это не то, что я хочу, поэтому я попробовал что-то вроде этого:

>>> SomeModel.objects.values_list('id', 'tags').filter(pk__in=[1,6,8])
[(1, 1), (1, 4), (1, 8), ...]

И моя последняя попытка была:

>>> SomeModel.objects.values_list('id', 'tags', flat=True).filter(pk__in=[1,6,8])
...
TypeError: 'flat' is not valid when values_list is called with more than one field.

-

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

[{'id': 1, 'tags': 1}, {'id': 1, 'tags': 4}, {'id': 1, 'tags': 8}, ...]

Есть ли какой-нибудь встроенный метод Python, который преобразует его в этот?

[{'id': 1, 'tags': [1, 4, 8, 10]}, {'id': 6, 'tags': []}, {'id': 8, 'tags': [1, 2]}]

- РЕДАКТИРОВАТЬ:

Если я напишу метод в SomeModel:

class SomeModel(models.Model):
  tags = models.ManyToManyField(Tag, related_name='tags')

  def get_tag_ids(self):
    aid = []
    for a in self.answers.all():
      aid.append(a.id)
    return aid

А затем позвоните:

>>> sm = SomeModel.objects.only('id', 'tags').filter(pk__in=[1,6,8])
# Hit database
>>> for s in sm:
...   s.get_tag_ids()
...
>>> # Hit database 3 times.

Это не работает, потому что это доступ к базе данных 4 раза. Мне нужен только один доступ.

Ответы [ 2 ]

1 голос
/ 17 ноября 2011

Как ArgsKwargs упоминается здесь в комментариях - я пишу свой собственный код, который упаковывает список:

>>> sm = SomeModel.objects.values('id', 'tags').filter(pk__in=[1,6,8])
>>> a = {}
>>> for s in sm:
...   if s['id'] not in a:
...     a[s['id']] = [s['tags'],]
...   else:
...     a[s['id']].append(s['tags'])
... 

Вывод этого кода именно то, что мне нужно, и он попал в базу данных только один раз. Но это не очень элегантно, мне не нравится этот код:)

Btw. лучше использовать pk или id в запросах? .values('id', 'tags') или .values('pk', 'tags')?

0 голосов
/ 17 ноября 2011

А как насчет пользовательского метода в модели, который возвращает список всех тегов

class Tag(models.Model):
  text = ...

class SomeModel(models.Model):
  tags = models.ManyToManyField(Tag, related_name='tags')

  def all_tags(self):
    return self.tags.values_list('pk',flat=True)

, а затем

SomeModel.objects.values('id', 'all_tags').filter(pk__in=[1,6,8])
...