如何使模型字段等于查询集结果
How to make a model field equal to a queryset result
我对 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()
我知道我们不能像我那样在模型字段中放置查询集,但我这样做是为了证明我的问题。
当然有办法解决这个问题,但我就是想不通。
如您所说,您不能将这些字段“as-is”放入您的模型中。 easiest/quickest 的解决方法是使用 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/coding 的新手(尤其是子查询)来说,前面的方法更高级。您将能够在文档中阅读有关所有这些的更多信息:
请注意,我没有尝试代码
我对 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()
我知道我们不能像我那样在模型字段中放置查询集,但我这样做是为了证明我的问题。
当然有办法解决这个问题,但我就是想不通。
如您所说,您不能将这些字段“as-is”放入您的模型中。 easiest/quickest 的解决方法是使用 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/coding 的新手(尤其是子查询)来说,前面的方法更高级。您将能够在文档中阅读有关所有这些的更多信息:
请注意,我没有尝试代码