如何通过完整的ManyToManyField出现过滤查询集?

时间:2018-12-13 09:52:56

标签: django orm django-queryset manytomanyfield

我有一个带有ManyToManyField的模特

class CustomUser(AbstractBaseUser, PermissionsMixin):
    email = models.EmailField(unique=True)

class Skill(models.Model):

    def __str__(self):
        en_translation = self.translations.filter(language__contains='en').first()
        if en_translation:
            return en_translation.name
        return "No english translations"


class SkillTranslation(models.Model):
    language = models.CharField(max_length=5)
    name = models.CharField(max_length=100)
    skill = models.ForeignKey(Skill, on_delete=models.CASCADE, related_name='translations')

    def __str__(self):
        return self.name


class SkillOwnership(models.Model):
    skill = models.ForeignKey(Skill, on_delete=models.CASCADE, related_name='skill_ownerships')
    owner = models.ForeignKey(CustomUser, on_delete=models.CASCADE, related_name='skill_ownerships')

    def __str__(self):
        return f'{str(self.owner.email)} < {str(self.skill)}'

    class Meta:
        unique_together = ['skill', 'owner']

并通过非优化的方式对其进行搜索:

def get_queryset(self):
    result = CustomUser.objects.all()
    if 'skills' in self.request.query_params:
        skill_ids = [int(sid) for sid in self.request.query_params.get('skills').split(',')]
        for skill_id in skill_ids:
            result = result.filter(skill_ownerships__skill_id=skill_id).distinct()
    return result.exclude(pk=self.request.user.pk)

我需要通过对数据库的一次查询来获得相同的结果。我需要找到skill_ids数组中所有技能的所有用户。

UPD 我的get_queryet方法现在看起来像

def get_queryset(self):
    not_reviewed = bool(int(self.request.query_params.get('not_reviewed', '0')))
    result = self.request.user.find_in_distance(int(self.request.query_params.get('distance', '0')))
    if not_reviewed:
        result = result.exclude(income_likes__like_from__id=self.request.user.id)
    if 'skills' in self.request.query_params:
        skill_ids = [int(sid) for sid in self.request.query_params.get('skills').split(',')]
        skills_ownerships = SkillOwnership.objects.filter(skill_id__in=skill_ids).annotate(skill_count=Count('*')).values('owner').distinct()
        result = result.annotate(user_skill_count=Subquery(skills_ownerships)).filter(user_skill_count=len(skill_ids))
    return result.exclude(pk=self.request.user.pk).exclude(income_reports__author=self.request.user)

return result的查询之前,如下所示:

SELECT "accounts_customuser"."id", "accounts_customuser"."password", "accounts_customuser"."last_login", "accounts_customuser"."is_superuser", "accounts_customuser"."email", "accounts_customuser"."username", "accounts_customuser"."first_name", "accounts_customuser"."last_name", "accounts_customuser"."company", "accounts_customuser"."job", "accounts_customuser"."linkedin_username", "accounts_customuser"."twitter_username", "accounts_customuser"."facebook_username", "accounts_customuser"."vk_username", "accounts_customuser"."instagram_username", "accounts_customuser"."website", "accounts_customuser"."about_me", "accounts_customuser"."location"::bytea, "accounts_customuser"."preferred_distance", "accounts_customuser"."fcm_token", "accounts_customuser"."is_active", "accounts_customuser"."is_staff", (SELECT DISTINCT U0."owner_id" FROM "pro_skillownership" U0 WHERE U0."skill_id" IN (80, 32) GROUP BY U0."id") AS "user_skill_count" FROM "accounts_customuser" WHERE (SELECT DISTINCT U0."owner_id" FROM "pro_skillownership" U0 WHERE U0."skill_id" IN (80, 32) GROUP BY U0."id") = 2

1 个答案:

答案 0 :(得分:0)

skills = SkillOwnership.objects.filter(
    skill_id__in=skill_ids, owner_id=OuterRef('pk')).values('skill_id')
users = CustomUser.objects.annotate(user_skill_count=Count(Subquery(skills ))).\
    filter(user_skill_count=len(skill_ids))