Django запись запроса с самой большой версией в группе - PullRequest
2 голосов
/ 06 марта 2020

У меня есть очень простая Django модель, которая представляет собой версионный продукт:

class Product(models.Model):
    name = models.CharField(max_length=50)
    version = models.PositiveSmallIntegerField(null=True)

    class Meta:
        unique_together = ("name", "version")

    def __str__(self):
        return f"{self.name} ({f'v{self.version}' if self.version else 'DRAFT'})"

Я могу создать некоторые продукты в базе данных, используя следующий скрипт:

p1_v1, _ = Product.objects.get_or_create(name="One", version=1)
p1_v2, _ = Product.objects.get_or_create(name="One", version=2)
p1_v3, _ = Product.objects.get_or_create(name="One", version=3)
p1_draft, _ = Product.objects.get_or_create(name="One", version=None)

p2_v1, _ = Product.objects.get_or_create(name="Two", version=1)
p2_v2, _ = Product.objects.get_or_create(name="Two", version=2)

p3_draft, _ = Product.objects.get_or_create(name="Three", version=None)

I необходимо выполнить запрос, который возвращает последнюю версию каждого продукта (за исключением продуктов без версии). Таким образом, целевой результат: Queryset:

<QuerySet [<Product: One (v3)>, <Product: Two (v2)>]>

Следующее работает с PostgreSQL, но не работает с SQLite:

Product.objects.exclude(version__isnull=True).order_by("name", "-version").distinct("name")
# <QuerySet [<Product: One (v3)>, <Product: Two (v2)>]>

Я могу получить Максимальная версия для каждого имени здесь, но я не уверен, как получить фактические объекты из БД:

Product.objects.values("name").annotate(max_code_version=Max("version"))
# <QuerySet [{'name': 'One', 'max_code_version': 3}, {'name': 'Two', 'max_code_version': 2}]>

Как я могу выполнить этот запрос?

1 Ответ

2 голосов
/ 17 марта 2020

Для простоты использования вы должны изменить имя поля аннотации на version с max_code_version

import functools
import operator
from django.db.models import Max, Q

group_by_expression = Product.objects.values("name").annotate(version=Max("version"))
filter_qry = functools.reduce(operator.or_, [Q(**item) for item in group_by_expression])
qs = Product.objects.filter(filter_qry)

Пример

In [7]: group_by_expression = Product.objects.values("name").annotate(version=Max("version"))                                                                                                                      

In [8]: filter_qry = functools.reduce(operator.or_, [Q(**item) for item in group_by_expression])                                                                                                                   

In [9]: qs = Product.objects.filter(filter_qry)                                                                                                                                                                    

In [10]: qs                                                                                                                                                                                                        
Out[10]: <QuerySet [<Product: One (v3)>, <Product: Three (DRAFT)>, <Product: Two (v2)>]>

In [11]: group_by_expression                                                                                                                                                                                       
Out[11]: <QuerySet [{'name': 'One', 'version': 3}, {'name': 'Three', 'version': None}, {'name': 'Two', 'version': 2}]>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...