LINUX.ORG.RU

В django orm вытащить объект вместе с последним объектом по внешнему ключу

 , ,


0

3

Есть Foo. У Foo есть лог с его статусами.

from django.db import models


class Foo(models.Model):
    name = models.CharField(max_length=32)


class FooStatus(models.Model):
    foo = models.ForeignKey(Foo, on_delete=models.CASCADE, related_name='statuses')
    timestamp = models.DateTimeField(auto_now_add=True)
    value = models.CharField(max_length=32)

Нужно вытащить все Foo вместе с их последними статусами.

Что есть:

In [16]: list(Foo.objects.all().annotate(timestamp=F('statuses__timestamp'), status=F('statuses__value')).values('name', 'status', 'timestamp'))
Out[16]: 
[{'name': 'Foo1',
  'status': 'valid',
  'timestamp': datetime.datetime(2016, 11, 7, 18, 42, 19, 548072, tzinfo=<UTC>)},
 {'name': 'Foo1',
  'status': 'invalid',
  'timestamp': datetime.datetime(2016, 11, 7, 18, 42, 25, 953250, tzinfo=<UTC>)},
 {'name': 'Foo2',
  'status': 'ok',
  'timestamp': datetime.datetime(2016, 11, 7, 18, 43, 54, 546427, tzinfo=<UTC>)},
 {'name': 'Foo2',
  'status': 'broken',
  'timestamp': datetime.datetime(2016, 11, 7, 18, 43, 59, 507559, tzinfo=<UTC>)}]

Что нужно:

[{'name': 'Foo1',
  'status': 'invalid',
  'timestamp': datetime.datetime(2016, 11, 7, 18, 42, 25, 953250, tzinfo=<UTC>)},
 {'name': 'Foo2',
  'status': 'broken',
  'timestamp': datetime.datetime(2016, 11, 7, 18, 43, 59, 507559, tzinfo=<UTC>)}]

Т. е. для каждого объекта нужен его статус с позднейшей датой. И всё это хозяйство хочется одним запросом к б. д.


Foo.objects.all().annotate(timestamp=Max('foostatus__timestamp'), status=F('foostatus__value')).values('name', 'status', 'timestamp')
anonymous
()
Ответ на: комментарий от anonymous

А если бы ещё читаемо было...

Foo.objects.all().annotate(
    timestamp = Max('foostatus__timestamp'),
    status = F('foostatus__value'),
).values(
    'name',
    'status',
    'timestamp',
)

Goury ★★★★★
()
Последнее исправление: Goury (всего исправлений: 2)
Ответ на: комментарий от Goury
In [18]: list(Foo.objects.all().annotate(
    ...:     timestamp = Max('statuses__timestamp'),
    ...:     status = F('statuses__value'),
    ...: ).values(
    ...:     'name',
    ...:     'status',
    ...:     'timestamp',
    ...: ))
Out[18]: 
[{'name': 'Foo1',
  'status': 'invalid',
  'timestamp': datetime.datetime(2016, 11, 7, 18, 42, 25, 953250, tzinfo=<UTC>)},
 {'name': 'Foo1',
  'status': 'valid',
  'timestamp': datetime.datetime(2016, 11, 7, 18, 42, 19, 548072, tzinfo=<UTC>)},
 {'name': 'Foo2',
  'status': 'broken',
  'timestamp': datetime.datetime(2016, 11, 7, 18, 43, 59, 507559, tzinfo=<UTC>)},
 {'name': 'Foo2',
  'status': 'ok',
  'timestamp': datetime.datetime(2016, 11, 7, 18, 43, 54, 546427, tzinfo=<UTC>)}]
suuaq
() автор топика
Ответ на: комментарий от anonymous

Читайте внимательнее моё решение. У вас нет модели statuses, у вас есть модель foostatus.

Ага, нет модели. Совсем нет. Читайте внимательно код.

In [4]: Foo.objects.all().annotate(timestamp=Max('foostatus__timestamp'), status
   ...: =F('foostatus__value')).values('name', 'status', 'timestamp')

...

FieldError: Cannot resolve keyword 'foostatus' into field. Choices are: id, name, statuses
anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.