2015-09-17 2 views
2

Предположим, что у меня есть следующий класс Джанго:Можно создать Queryset Django для выполнения этого сложного запроса?

class MyModel(models.Model): 
    a = models.IntegerField() 
    created_ts = models.DateTimeField(default=datetime.utcnow, editable=False) 

    def __str__(self): 
     return "<%s %s>" % (
      self.__class__.__name__, 
      "; ".join(
       [ 
        "ID: %s" % self.pk, 
        "a: %s" % self.a, 
        "created_ts: %s" % self.created_ts, 
       ] 
      ) 
     ) 

Я хотел бы найти экземпляры MyModel с последним created_ts для каждого отдельного значения a. Могу ли я сделать это с помощью одного QuerySet? Если да, то что такое QuerySet? Если нет, то какой самый эффективный способ получить этот результат?

В конце концов, я хочу иметь пары Integer/MyModel-Instance. Ответ должен выглядеть примерно так:

{ 
    1: <MyModel ID: 1; a: 1; created_ts: 2004-11-08 06:01:00>, 
    5: <MyModel ID: 2; a: 5; created_ts: 2004-11-05 08:01:32>, 
    3: <MyModel ID: 3; a: 3; created_ts: 2004-11-04 11:01:42>, 
    0: <MyModel ID: 4; a: 0; created_ts: 2004-11-03 06:12:10>, 
} 
+0

Какую базу данных вы используете? MySQL не поддерживает отдельные поля (PostgreSQL поддерживает). Как я знаю, не существует «прямого» способа сделать то, что вы хотите, не используя необработанный SQL. – Makc

+0

Backend - это mysql. Да, он не поддерживает «отличную от поля». Каков обходной путь? В случае необходимости я предпочел бы делать несколько запросов вместо написания исходного SQL. –

ответ

2

Это будет работать, если вы находитесь на Postgres. Не будет работать, если вы используете MySQL или sqlite.

MyModel.objects.order_by('a', '-created_ts').distinct('a') 

Edit: Упс только что видели вы на MySQL.

Это поможет вам

from django.db.models import Count, Max 
MyModel.objects.values('a').annotate(count=Count("a"),latest_date=Max('created_ts')) 

данных в таблице

a    created_ts 
    -    ----------- 
    1 2015-09-08 20:05:51.144321+00:00 
    1 2015-09-08 20:08:40.687936+00:00 
    3 2015-09-08 20:08:58.472077+00:00 
    2 2015-09-08 20:09:08.493748+00:00 
    2 2015-09-08 20:10:20.906069+00:00 

Выход

[ 
    {'count': 2, 'latest_date': datetime.datetime(2015, 9, 8, 20, 8, 40, 687936, tzinfo=<UTC>), 'a': 1}, 
    {'count': 2, 'latest_date': datetime.datetime(2015, 9, 8, 20, 10, 20, 906069, tzinfo=<UTC>), 'a': 2}, 
    {'count': 1, 'latest_date': datetime.datetime(2015, 9, 8, 20, 8, 58, 472077, tzinfo=<UTC>), 'a': 3} 
] 
+0

Интересно, как это почти дубликат http://stackoverflow.com/a/32466750/356528 –

+0

Вот откуда я его получил. Если вы посмотрите там, вы увидите, что я сделал какую-то работу другим способом, используя python с грубой силой, пока не обнаружил этот путь. – electrometro

+0

Справедливо добавлять ссылку на исходное решение, когда вы это делаете. –

1

Не знаю, как сделать это с помощью одного QuerySet, но если вы нормально с делать больше запросов за счет использования не сырыми вы могли бы сделать

from django.db.models import Max 
max_ts_queryset = MyModel.objects.values('a').order_by('a').annotate(max_ts=Max('created_ts')) 

, чтобы получить максимум ц для каждого значения а, то просто петлю над, что с чем-то вроде

final_list = {} 
for obj in max_ts_queryset: 
    final_list[obj['a']] = MyModel.objects.get(a=obj['a'], created_ts=obj['max_ts'] 

return final_list 
3

не самый прямой ответ, но надеюсь, что это помогает:

Затем вы можете использовать raw queries

MyModel.objects.raw("SELECT * FROM mymodel main INNER JOIN (SELECT max(created_ts) as max_ts, a FROM mymodel GROUP BY a) sub on main.a = sub.a AND main.created_ts = sub.max_ts") 

Я не запустить запрос самого, поэтому он может иметь синтаксические ошибки, но вы получите идею.

0

Ваш вопрос является. почти точная копия предыдущего вопроса: How to make Django Queryset that selects records with max value within a group

я, таким образом, ответ, используя часть моего бывшего proposed solution:

MyClass.objects.filter(
    created_ts__in=MyClass.objects.values(
     "a" 
    ).annotate(
     created_ts=models.Max(
      "created_ts" 
     ) 
    ).values_list("created_ts", flat=True) 
) 

Обратите внимание, что это требует только один запрос SQL, как вы можете видеть, печатая LEN (django.db.con nection.queries) до и после запроса.

Однако обратите внимание, что последнее решение работает только в том случае, если ваш атрибут created_ts гарантированно уникален, что может и не быть вашим случаем.

Смежные вопросы