如何根据列表中的出现频率对 Django 查询集进行排序并确保其余对象遵循?

How do I sort a Django queryset by frequency of occurance from a list and ensure the remaining objects follow?

我有一个 Django 应用程序 运行 Django 3+ 和 Python 3.8+。

我试图用当前登录用户的相关帐户填充我网站的首页。因此,如果用户 john_person 已登录,我希望他们看到与他们拥有相似资源的用户其次是拥有大量资源的帐户,其次是其余帐户。

models.py

class UserAccount(AbstractBaseUser, PermissionsMixin):
  email = models.EmailField(max_length=255, unique=True)
  first_name = models.CharField(max_length=255, default='Anonymous')
  last_name = models.CharField(max_length=255, default='User')
  user_name = models.CharField(max_length=255)
  is_private = models.BooleanField(default=False)

class Resource(models.Model):
  user_account=models.ForeignKey(UserAccount, on_delete=models.CASCADE, related_name='resources')
  resource_name = models.CharField(max_length=255)

class Tag(models.Model):
  resource = models.ForeignKey(Resource, on_delete=models.CASCADE, related_name='tags')
  name = models.CharField(max_length=255)

我有一个公共标签列表(属于登录用户),我们称之为:

common_tags=['tag1','tag2','tag3']

此列表的长度可能会有所不同。

我的模型亲子关系是多对一关系。

用户->资源->标签

用户有资源,资源有标签

我的目标:

我需要能够查询我的所有用户并以这样一种方式对其进行过滤,即我得到一个有序的查询集,其中拥有最多 common_tags 标签的用户排在最前面。这组人需要按谁拥有更多共同标签进行排序(不是基于他们拥有的每个标签的数量,而是基于登录用户的常见情况)。这个相同的查询集还应该包含没有任何公共标签的其余用户。最后,这个查询集不应该有任何重复。

到目前为止我有:

  relevant_accounts= User.objects.all()\
  .annotate(count_resources=Count('resources')).order_by('-count_resources')\
  .order_by(Case(When(resources__tags__name__in=common_tags, then=0),default=1, 
  output_field=FloatField()))

这似乎有效,但我得到了重复的值。

运行 .distinct() 没有解决问题。

编辑:示例 Input/Output

用户A:Tags = ['A','A','B','C'] 用户 B:Tags = ['A','B','B','C'] 用户 C:Tags = ['A','B','B','B']

示例用户 1:common_tags=['A','C']

Return:用户 A->B->C

示例用户 2:common_tags=['B','C']

Return:用户 B->C->A 或 C->B->A

编辑:也试过这个。我喜欢我得到的自定义级别,但仍然重复。

is_in_query = Q(resources__tags__name__icontains=common_tags)
    is_not_in_query = ~Q(resources__tags__name__in=common_tags)

    qs_simple = (User.objects.filter(is_private=False).annotate(count_resources=Count('resources'))
    .annotate(
    search_type_ordering=Case(
    When(Q(count_resources__gte=6) & is_in_query, then=1),
    When(Q(count_resources__gte=3) & Q(count_resources__lte=5) & is_in_query, then=2),
    When(Q(count_resources__gte=0) & Q(count_resources__lte=2) & is_in_query, then=3),
    When(Q(count_resources__gte=3) & is_not_in_query, then=4),
    When(Q(count_resources__gte=0) & Q(count_resources__lte=2) & is_not_in_query, then=5),
    default=7,
    output_field=IntegerField(),
    ))
    .order_by('-search_type_ordering',).distinct()
    )

这可以通过使用带有过滤器的 Count 注释来实现。首先用资源和公共标签的数量注释每个User,然后按注释排序

User.objects.annotate(
    count_resources=Count('resources', distinct=True), 
    count_common_tags=Count(
        'resources__tags__name',
        filter=Q(resources__tags__name__in=common_tags)
    )
).order_by(
    '-count_common_tags',
    '-count_resources'
)