Django ManytoMany Field Queryset для точного и похожего соответствия
У меня есть следующие модели:
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)
На фронт-энде у меня есть несколько полей выбора, где пользователи могут выбрать несколько симптомов, чтобы найти заболевание, и это будет передано в модель Disease как symptoms_selected params.
У меня есть следующий get_queryset на Disease > 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
Например: Данные о заболевании:
Болезнь А: Симптомы: A, B, C, D
Болезнь В: Симптомы: A, D, P, Q
Болезнь С: Симптомы: A, Q, X, Y
В настоящее время, если пользователь выбирает 3 симптома: A, D, Y, то возвращается Null. Вместо Null я хочу показать пользователям несколько похожих совпадений с 2 и 1 симптомами.
Я хочу, чтобы queryset вернул:
Точное совпадение заболеваний, т.е. 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)