Как в Django queryset аннотировать True, если все BooleanField связанных объектов True, в противном случае False?

У меня есть модель, которая выглядит вот так :

class Test(models.Model):
    user = models.ForeignKey('users.CustomUser', models.CASCADE)
    name = models.CharField(max_length=64)


class TestVersion(models.Model):
    test = models.ForeignKey('Test', models.CASCADE)
    name = models.CharField(max_length=255)
    validation_1 = models.BooleanField(default=False, editable=False)
    validation_2 = models.BooleanField(default=False, editable=False)
    validation_3 = models.BooleanField(default=False, editable=False)
    validation_4 = models.BooleanField(default=False, editable=False)

Иногда у меня сотни TestVersion, связанных с Test.

А я хочу что-то вроде :

user_test = Test.objects.filter(
    user=request.user
).annotate(
    number_of_test=Count('testversion', distinct=True),
    all_validation_1="True or False ?", # if all testversion_set.all() of the current test are True, return True else False.
    all_validation_2="True or False ?", # same
    all_validation_3="True or False ?", # same
    all_validation_4="True or False ?", # same
).distinct()

# I Want for example :
test_1 = user_test.first()
test_1_validation_1 = test_1.testversion_set.all().count()
test_1_validation_1_true = test_1.testversion_set.filter(validation_1=True).count()
all_validation_1 = test_1_validation_1 == test_1_validation_true
test_1.all_validation_1 == all_validation_1 # True

# Or something like :
test_1 = user_test.first()
all_validation_1 = all(test_1.testversion_set.all().values_list('validation_1', flat=True))
test_1.all_validation_1 == all_validation_1 # True

Мне не удалось найти, какие методы были использованы для достижения такого уровня точности со связанными объектами в методе annotate.

Есть идеи?

Спасибо

Вы можете использовать условные выражения Django в сочетании с Q объектами .

Попробуйте

from django.db.models import Case, When, Value, BooleanField, Count, Q

test = Test.objects.annotate(
    number_of_test=Count("testversion", distinct=True)
).annotate(
    all_validated=Case(
        When(
            Q(testversion__validation_1=True)
            & Q(testversion__validation_2=True)
            & Q(testversion__validation_3=True)
            & Q(testversion__validation_4=True),
            then=Value(True),
        ),
        default=Value(False),
        output_field=BooleanField(),
    )
)

если все ваши проверки истинны, то значение выходного файла all_validated будет True, иначе False

Наконец-то я нашел другой ответ :

from test.models import Test, TestVersion
from django.db.models import Count, Case, When, Exists, OuterRef, Value, BooleanField

test = Test.objects.filter(
    user=request.user
).annotate(
    number_of_test=Count("testversion", distinct=True),
    all_validation_1=Case(
        When(
            Exists(TestVersion.objects.filter(test=OuterRef('pk'), validation_1=False)), 
            then=Value(False)
        ),
        default=Value(True),
        output_field=BooleanField()
    )
).distinct()

В данном случае :

>> test.first().all_validation_1 == all(test.first().testversion_set.all().values_list('validation_1', flat=True))
True

Так что мне просто нужно повторить то же самое для validation_2, 3 и 4, и все будет в порядке.

Но я думаю, что это будет немного толстый код. Или это может быть хорошей практикой

Дайте мне знать, прежде чем я проверю ответ.

Вернуться на верх