Django: get only objects with max foreignkey count

Question:

The question is quite simple but possibly unsolvable with Django.

For example I have a model

class MyModel(models.Model)
   field_a = models.IntegerField()
   field_b = models.CharField()
   field_c = models.ForegnKey(MyOtherModel)

The question is how to select only objects that have a maximal count of relations with MyOtherModel and preferably(almost mandatory) with only a single query set?

Lets say, we have 100 entries all together, 50 pcs. point to field_c_id=1, 40 pcs. to field_c_id=2 and rest 10 pcs. entries to field_c_id = 3.
I need only those which point to field_c_id=1? as 50 would be maximal count.

Thanks…

Asked By: Aleksei Khatkevich

||

Answers:

Ok first you need a related_name on your MyModel model

in models.py

class MyOtherModel(models.Model)
   field_a = models.IntegerField()


class MyModel(models.Model)
   field_a = models.IntegerField()
   field_b = models.CharField()
   field_c = models.ForeignKey(MyOtherModel, on_delete=models.CASCADE, related_name="my_other_model")

Than you can get most used MyOtherModel

in views.py


most_used = MyOtherModel.objects.annotate(my_other_model_count=Count('my_other_model')).order_by('-my_other_model_count')[:1]

put [:1] if you need 1 if you need more you can set any quantity or remove it

Or

And here is another solution which is better than this

First we have to add one field to your MyOtherModel which keeps count of MyModel

class MyOtherModel(models.Model)
   field_a = models.IntegerField()
   my_model_count = models.IntegerField()

And we have to update count when you add, update or delete object to MyModel for that I recommend to use django signals

in your models.py

from django.dispatch import receiver
from django.db.models.signals import pre_save, pre_delete


#at the bottom of your model

@receiver(pre_save, sender=MyModel)
def products_reputation_count(sender, instance, *args, **kwargs):
    if instance.pk:
        old_instance = MyOtherModel.objects.get(id=instance.pk)
        old_instance.reputation.product_count -= 1
        instance.reputation.product_count += 1
    else:
        instance.reputation.product_count += 1


@receiver(pre_save, sender= MyModel)
def products_reputation_count(sender, instance, *args, **kwargs):
    instance.reputation.product_count += 1

and in your views.py

most_used = MyOtherModel.objects.order_by("-my_model_count")[:1]

I think this would be helpful. If you have any question related this answer feel free to ask more. Have a good day.

Answered By: Abduvahob Kaxarov
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.