用于完全匹配和相似匹配的 Django ManytoMany 字段查询集

Django ManytoMany Field Queryset for Exact and Similar Match

我有以下型号:

class Disease(models.Model):
    name = CICharField("Disease Name", max_length=200, unique=True)
    symptoms = models.ManyToManyField(Symptom, through='DiseaseSymptom', related_name='diseases')


class Symptom(models.Model):
    name = CICharField("Symptom Name", max_length=200, unique=True)

在前端,我有多个 select 框,用户可以在其中 select 多个症状来查找疾病,并将作为 symptoms_selected 参数传递给疾病模型。

我有以下关于疾病的 get_queryset > views.py


def get_queryset(self):
        params = self.request.query_params
        query_symptoms = self.request.GET.getlist('symptoms_selected')
        if query_symptoms:
            i = 0
            queryset = Disease.objects.all()
            while i < (len(query_symptoms)):
                symptom = [query_symptoms[i]]
                queryset = queryset.filter(symptoms__id__in=symptom)
                i=i+1
        else:
            queryset = Disease.objects.all()
    return queryset
serializer_class = DiseaseSerializer

我正在使用 Django REST API 来传递结果数据。

class DiseaseSerializer(serializers.ModelSerializer):

    class Meta:
        model = Disease
        fields = ('id', 'name', 'symptoms')

例如:疾病数据:

疾病A: 出现症状:A、B、C、D

疾病B: 出现症状:A、D、P、Q

疾病 C: 有症状:A、Q、X、Y

目前,如果用户 select 3 种症状:A、D、Y。它 return 为空。我想向用户展示一些具有 2 和 1 症状的类似匹配项,而不是 Null。

我想要查询集 return:

精确匹配疾病,即 3 种症状匹配的疾病

2 种症状匹配的疾病

1 种症状匹配的疾病

谁能告诉我怎样才能做到这一点?

单个 __in 过滤器将获取具有至少一种匹配症状的所有疾病

然后你可以用匹配的计数注释查询,然后根据这个注释排序,首先得到匹配最多的疾病

from django.db.models import Count

Disease.objects.filter(
    symptoms__id__in=query_symptoms
).annotate(
    matches=Count('symptoms')
).order_by('-matches')

regroup 标签可用于根据模板中的匹配项数量对结果进行分组

{% regroup diseases by matches as diseases_grouped %}
<ul>
{% for match_count in diseases_grouped %}
    <li>{{ match_count.grouper }} matches
    <ul>
        {% for disease in match_count.list %}
          <li>{{ disease }}</li>
        {% endfor %}
    </ul>
    </li>
{% endfor %}
</ul>

使用 SerializerMethodField 将字段添加到序列化程序以访问注释

class DiseaseSerializer(serializers.ModelSerializer):

    num_matches = serializers.SerializerMethodField()

    class Meta:
        model = Disease
        fields = ('id', 'name', 'symptoms')

    def get_num_matches(self, obj):
        return getattr(obj, 'matches', None)