Issue
I have the following models:
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)
On the front-end, I have multiple select boxes where users can select multiple Symptoms to find disease and that will pass to Disease model as symptoms_selected params.
I have the following get_queryset on 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
serializer_class = DiseaseSerializer
I am using Django REST API to pass the result data.
class DiseaseSerializer(serializers.ModelSerializer):
class Meta:
model = Disease
fields = ('id', 'name', 'symptoms')
For eg: Disease Data:
Disease A: Got Symptoms: A, B, C, D
Disease B: Got Symptoms: A, D, P, Q
Disease C: Got Symptoms: A, Q, X, Y
Currently, if Users select 3 symptoms: A, D, Y. It returns Null. Instead of Null, I want to show users some similar matches with 2 and 1 symptoms.
I want queryset will return:
Exact match diseases i.e. 3 Symptoms matched diseases
2 Symptoms matched diseases
1 symptom matched diseases
Can anyone tell me how can I achieve this?
Solution
A single __in
filter will get all diseases that have at least one matching symptom
Then you can annotate the query with the count of matches and then order by this annotation to get the diseases with the most matches first
from django.db.models import Count
Disease.objects.filter(
symptoms__id__in=query_symptoms
).annotate(
matches=Count('symptoms')
).order_by('-matches')
The regroup tag can be used to group the results by number of matches in your template
{% 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>
Use a SerializerMethodField to add a field to your serializer to access the annotation
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)
Answered By - Iain Shelvington
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.