У меня есть модель с 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