Как в 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, и все будет в порядке.
Но я думаю, что это будет немного толстый код. Или это может быть хорошей практикой
Дайте мне знать, прежде чем я проверю ответ.