You can override the .objects manager to the soring, so:
class ModelAManager(models.Manager):
def get_queryset(self, *args, **kwargs):
return super().get_queryset(*args, **kwargs).annotate(
count_b = Count('modelb')
).order_by('count_b')
and then register this as the manager of ModelA:
class ModelA(models.Model):
# …
objects = ModelAManager()
That being said, I would order by the count in the views where you need this, and not in general, here you make a LEFT OUTER JOIN, but it means that if you make extra joins through filtering, annotating, etc. the two (or more) JOINs will interact with each other, and thus act as a multiplier to the other LEFT OUTER JOINs.
It thus might be better to simply order by the number of related ModelBs when necessary.
We can also add a method to the Queryset that makes it possible to do the ordering by count_b through a method:
class ModelAQueryset(models.QuerySet):
def order_by_b(self):
return self.annotate(
count_b=Count('modelb')
).order_by('count_b')
then we define a manager based on the ModelAQueryset:
class ModelA(models.Model):
# …
objects = ModelAQueryset.as_manager()
then you can order on b with:
ModelA.objects.order_by_b()
You thus can call .order_by_b() in the queryset at any part in the chain where you construct the queryset.