Как сделать поле модели равным результату кверисета

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

Я сталкиваюсь с некоторыми трудностями при структурировании моделей.

Вот 2 модели всех моделей

class Listing(models.Model):
    title = models.CharField(max_length=64)
    image = models.URLField(null=True, blank=True)
    description = models.CharField(max_length=64)
    starting_price = models.DecimalField(max_digits=7, decimal_places=2)    

    current_price = #highest bid price
    bids_count = #count of bids

    lister = models.ForeignKey(User, on_delete=models.CASCADE, related_name="listings")

    def __str__(self):
        return f"Title: {self.title} ({self.id}), Lister: {self.lister.username}"


class Bid(models.Model):
    listing = models.ForeignKey(Listing, on_delete=models.CASCADE, related_name="bids")
    bidder = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, related_name="bids")
    amount = models.DecimalField(max_digits=7, decimal_places=2)
    time = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f"Bid by {self.bidder.username} on {self.listing.title} ({self.listing.id}) for an amount of {self.amount}"

Я пытаюсь сделать так, чтобы текущая цена была равна самой высокой ставке этого объявления

current_price = Listing.bid.objects.all().aggregate(Max("amount"))

И подсчитать количество заявок

bids_count = Listing.bid.count()

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

Наверняка есть какой-то способ обойти это, но я просто не могу понять его.

Как вы сказали, вы не можете поместить эти поля "как есть" в вашу модель. Самым простым/быстрым способом решения этой проблемы было бы использование property:

class Listing(models.Model):
    # ... rest of the class

    @property
    def bids_count(self):
        return self.bids.count()

    @property
    def current_price(self):
        return self.bids.all().aggregate(Max("amount"))

    # ... rest of the class

Теперь имейте в виду, что это будет хорошо работать, когда вы работаете с одним экземпляром. Это не будет эффективным, если вы перебираете список экземпляров Listing и отображаете их свойства, потому что это вызовет новый запрос к базе данных каждый раз, когда вы обращаетесь к этим свойствам (поэтому эти значения извлекаются ленивым способом)

Лучшим обходным решением, на мой взгляд, будет использование пользовательского manager, как показано ниже:

class ListingQuerySet(models.QuerySet):

    def with_bids_count(self):
        return self.annotate(bids_count=Count('bids'))

    def with_current_price(self):
        return self.annotate(current_price=Subquery(Bid.objects.filter(listing=OuterRef('pk')).annotate(max=Max('amount')).values('max')[:1]))

class Listing(models.Model):
    
    objects = ListingQuerySet.as_manager()

    # ... rest of the class

# Use it like this in your code

for listing in Listing.objects.with_bids_count().with_current_price():
    print(listing.current_price)

Предыдущий метод является более продвинутым для новичков в Django/кодировании (особенно с подзапросом). Вы сможете прочитать больше обо всем этом в документации:

Обратите внимание, что я не пробовал код

Вернуться на верх