如何使用跨越关系和聚合函数的字段查找来筛选Django模型



我有一组四个代表出租房产的模型。

属性模型存储属性的名称和创建者。

class Property(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
creator = models.ForeignKey(User, related_name='creator', on_delete=models.PROTECT)
name = models.CharField(max_length=100)

通过ForeignKey关系,属性可以有许多区域实例。区域也通过ForeignKey关系具有AreaType,并且通过ManyToManyField具有许多便利设施。

class Area(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
type = models.ForeignKey(AreaType, on_delete=models.PROTECT)
property = models.ForeignKey(Property, on_delete=models.PROTECT)
amenities = models.ManyToManyField(Amenity, null=True, blank=True)
class AreaType(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
name = models.CharField(max_length=100)

class Amenity(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
name = models.CharField(max_length=100)

我熟悉跨越关系和不跨越关系的字段查找,以及通过kwargs进行筛选。

我不熟悉的是将字段查找和聚合函数结合起来。因此,鉴于上述模型,我想:

  1. 检索所有至少有3个名为"电视"的设施的房产
  2. 检索所有至少有2个AreaType名称为"Bathroom"的区域的属性

使用名称为Television:的弹药检索所有属性

非常感谢@Waldemar Podsiadło,我误解了这个问题。他为数电视节目提供了正确答案
Property.objects.filter(area_set__amenities__name="Television")
.annotate(tv_count=models.Count('area_set__amenities')
.filter(tv_count__gte=3))

应检索与名为Television且计数大于或等于3的弹药有关的所有财产。

您没有定义related_name,因此区域的反面将是area_set。从那时起,您就可以将属性作为字段使用,因此您可以继续从中进行筛选。

要访问查询中的字段,请用双下划线__将其隔开,这也适用于表达式,如__count__sum(大多数情况下(。

您可以计算出可以与一起使用的查询表达式:

Property._meta.fields.get_field('your_field_name').get_lookups()

指向django文档的链接,以了解有关查询集和过滤的更多信息。

指向django文档的链接,以了解有关过滤的更多信息。

编辑:Kwargs示例:

def filter_my_queryset(tv_count: dict, *args, **kwargs):
Property.objects.filter(**kwargs)
.annotate(tv_count=models.Count(*args)
.filter(**tv_count)) # Need a second dict, unless you always need a gte filter. Then you can dynamically do it.

或者,您可以通过将表达式的最后一部分与模型上的字段进行比较来动态地获得便利设施;如果不是田地,就把它砍掉。在这个例子中,应该去掉area_set__amenities__namearea_set__amenities

您可以使用以下工具从模型中获取字段列表:

field_name_list = []
for i in mymodel._meta.fields:
field_name_list.append(i.name)

或者作为替代;使用hasattr()检查字段是否存在。不过,这可能会给出误报,因为任何属性都会计入其中

旁注:

阅读这篇文档,我想如果你需要将大量查询打包到函数中,你会发现它非常有用。自定义查询集很棒!

https://docs.djangoproject.com/en/4.1/topics/db/managers/

@nigel239我认为在过滤反向关系时不需要使用_set,只需要使用模型名称即可。此外,您还可以使用Django ORM制作更多内容。对于第一个问题,它是这样的:

Property.objects.filter(area__amenities__name="Television")
.annotate(tv_ocurance=Count('area__amenities', filter=Q(area__amenities__name="Television")))
.filter(tv_ocurance__gte=3)

您可以对第二个查询执行完全相同的操作。

更新:

正如@nigel239所建议的,可以这样做:

Property.objects.filter(area__amenities__name="Television")
.annotate(tv_count=Count('area__amenities')
.filter(tv_count__gte=3)

或者我的方式:

Property.objects
.annotate(tv_ocurance=Count('area__amenities', filter=Q(area__amenities__name="Television")))
.filter(tv_ocurance__gte=3)

最新更新