Каков наиболее эффективный способ получения кверисета django с наибольшим количеством постов для связанного имени?

В настоящее время я работаю над сайтом, на котором будут размещаться объявления о продаже и аренде автомобилей. Я хотел бы получить набор запросов, который выделяет только одну марку автомобиля (, т.е. Audi), которая имеет наибольшее количество сообщений для соответствующего model. Пример:

enter image description here

Отображение марки Audi, поскольку она имеет наибольшее количество связанных сообщений.

Мой вопрос в том, какой самый эффективный способ сделать это? Я проделал некоторую работу здесь, но я уверен, что это не самый эффективный способ. У меня есть следующее:

# Algorithm that is currently retrieving the name of the brand and the number of related posts it has.
def top_brand_ads():
     queryset = Advertisement.objects.filter(status__iexact="Published", owner__payment_made="True").order_by('-publish', 'name')

     result = {}
     for ad in queryset:
          # Try to update an existing key-value pair
          try:
               count = result[ad.brand.name.title()]
               result[ad.brand.name.title()] = count + 1

          except KeyError:
               # If the key doesn't exist then create it
               result[ad.brand.name.title()] = 1


     # Getting the brand with the highest number of posts from the result dictionary
     top_brand = max(result, key=lambda x: result[x])  # Returns for i.e. (Mercedes Benz)

     context = {
          top_brand: result[top_brand]  # Retrieving the value for the top_brand from the result dict.
     }
     
     print(context)  # {'Mercedes Benz': 7} -> Mercedes Benz has seven (7) related posts.
     return context

Есть ли способ вернуть queryset вместо этого, не делая того, что я сделал здесь, или это может быть намного более эффективным?

Если необходимы соответствующие модели, пожалуйста, смотрите ниже:

models.py

# Brand
class Brand(models.Model):
     name = models.CharField(max_length=255, unique=True)
     image = models.ImageField(upload_to='brand_logos/', null=True, blank=True)
     slug = models.SlugField(max_length=250, unique=True)
     ...
     # Methods


# Owner
class Owner(models.Model):
     user = models.ForeignKey(User, on_delete=models.CASCADE)
     telephone = models.CharField(max_length=30, blank=True, null=True)
     alternate_telephone = models.CharField(max_length=30, blank=True, null=True)
     user_type = models.CharField(max_length=50, blank=True, null=True)
     payment_made = models.BooleanField(default=False)
     expiring = models.DateTimeField(default=timezone.now)
     ...
     # Methods


# Advertisement (Post)
class Advertisement(models.Model):
     STATUS_CHOICES = (
         ('Draft', 'Draft'),
         ('Published', 'Published'),
     )

     owner = models.ForeignKey(Owner, on_delete=models.CASCADE, blank=True, null=True)
     name = models.CharField(max_length=150, blank=True, null=True)
     brand = models.ForeignKey(Brand, on_delete=models.CASCADE, blank=True, null=True)
     publish = models.DateTimeField(default=timezone.now)
     status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='Draft')
     ...
     # Other fields & methods

Любая помощь будет очень признательна.

Пробовали ли вы использовать метод count?

from django.db.models import Count

Car.objects.annotate(num_views=Count('car_posts_related_name')).order_by('num_views')

Поскольку вам нужны бренды, давайте сделаем запрос по модели бренда:

Brand.objects.filter(advertisement_set__status__iexact="Published").\
              filter(advertisement_set__owner__payment_made=True).\
              annotate(published_ads=Count('advertisement_set')).\
              order_by('-published_ads')

Однако, даже в предложенном вами решении можно немного улучшить:

  1. Удалите метод order_by из вашего набора запросов. Он не влияет на конечный результат, но добавляет некоторые накладные расходы, особенно если ваша модель Advertisement не индексирована по этим полям.
  2. Каждый раз, когда вы вызываете ad.brand, вы обращаетесь к базе данных. Это называется проблемой N+1. Находясь в цикле из n, вы совершаете n дополнительных обращений к БД. Вы можете использовать select_related, чтобы избежать таких проблем. В вашем случае: Advertisement.objects.select_related('brand')...
Вернуться на верх