Как подсчитать элементы, содержащие определенное значение, в отношениях "многие ко многим" в Django
У меня есть приложение django, используемое для аннотирования, где я сохраняю информацию об аннотациях изображений в модели BiomarkerAnnotations
через отношения "многие-ко-многим". Я отслеживаю, какие пользователи завершили аннотацию изображения с помощью поля annotated
.
from django.contrib.auth.models import User
class Biomarker(models.Model):
name = models.CharField(max_length=256)
description = models.CharField(max_length=1024)
class Image(models.Model):
number = models.IntegerField(default=0)
absolute_path = models.CharField(max_length=2560)
biomarkers = models.ManyToManyField(Biomarker, through='BiomarkerAnnotations', related_name="biomarker_annotations")
annotated = models.ManyToManyField(User, related_name="annotated_by")
class BiomarkerAnnotations(models.Model):
_image = models.ForeignKey(Image, on_delete=models.CASCADE)
biomarker = models.ForeignKey(Biomarker, on_delete=models.CASCADE)
user = models.ForeignKey(User, null=True,on_delete=models.SET_DEFAULT, default=1)
Я хотел бы создать представление, которое возвращает для конкретного пользователя (того, кто отправляет запрос), сколько изображений он аннотировал и сколько осталось аннотировать. Пока что я достиг этой точки, но, похоже, это не работает: общее количество, возвращаемое запросом, больше, чем количество изображений.
class AnnotStatsSerializer(serializers.ModelSerializer):
annotations = serializers.IntegerField()
count = serializers.IntegerField()
class Meta:
model = Image
fields = ("count", "annotations")
class AnnotatedStatsViewSet(viewsets.ReadOnlyModelViewSet):
queryset = Image.objects.all()
serializer_class = AnnotStatsSerializer
def get_queryset(self):
queryset = self.queryset
user_object = self.request.user
project_id = self.request.query_params.get('project', None)
if project_id is None: raise Http404
queryset = queryset.annotate(annotations=Case(When(annotated__id__exact=user_object.id, then=Value(1)), default=Value(0), output_field=IntegerField())) \
.values('annotations').annotate(count=Count('annotations')).order_by('annotations') \
.values('count', 'annotations')
return queryset
Любая помощь приветствуется.
Вы можете работать с:
annotated_images = Image.objects.filter(
biomarkerannotations__user=user_object
).count()
images_todo = Image.objects.exclude(
biomarkerannotations__user=user_object
).count()
для получения числа annotated_images
и images_todo
.
или если вы работаете с отношением annotated
многие-ко-многим:
annotated_images = Image.objects.filter(
annotated=user_object
).count()
images_todo = Image.objects.exclude(
annotated=user_object
).count()
Мы можем позволить этому работать с набором пользователей с:
from django.db.models import Count, OuterRef, Subquery, Value
User.objects.annotate(
tagged=Count('annotated_by'),
not_tagged=Subquery(
Image.objects.exclude(
annotated=OuterRef('pk'),
).values(foo=Value(None)).values(
total=Count('pk')
).order_by(Value(None))
)
)
В результате получается запрос, который выглядит следующим образом:
SELECT auth_user.*,
COUNT(app_name_image_annotated.image_id) AS tagged
(
SELECT COUNT(V0.id) AS total
FROM app_name_image V0
WHERE NOT EXISTS
(
SELECT (1) AS a
FROM app_name_image_annotated U1
WHERE U1.user_id = auth_user.id AND U1.image_id = V0.id
LIMIT 1
)
) AS not_tagged FROM auth_user
LEFT OUTER JOIN app_name_image_annotated ON (auth_user.id = app_name_image_annotated.user_id)
GROUP BY auth_user.*