直接在模板中预取替代项与过滤器
Prefetch alternative vs Filter directly in template
我在模板中使用以下 for 循环显示数据
{% for soproduct in list_soproduct %}
{% for bomversion in soproduct.product.material.default_active_bomversions %}
{% for bom in bomversion.bom_set.all %}
{% for production_order in bom.production_order_set.all %}
{% endfor %}
{% endfor %}
{% endfor %}
{% endfor %}
数据来自以下模型
class SOproduct(models.Model):
product = models.ForeignKey(Product, on_delete=models.PROTECT)
so = models.ForeignKey(SO, on_delete=models.PROTECT)
quantity = models.DecimalField(max_digits=13, decimal_places=3, default = 0)
class Product(models.Model):
version = IntegerVersionField( )
GTIN = models.CharField(max_length=30)
name = models.CharField(max_length=30)
description = models.TextField(null=True, blank=True)
creation_time = models.DateTimeField(auto_now_add=True, blank=True)
material = models.ForeignKey(Material, on_delete=models.PROTECT)
class BOMVersion(models.Model):
name = models.CharField(max_length=200,null=True, blank=True)
description = models.TextField(null=True, blank=True)
material = models.ForeignKey(Material)
is_active = models.BooleanField(default=False)
is_default = models.BooleanField(default=False)
class BOMVersion_default_active_Manager(models.Manager):
def get_queryset(self):
return super(BOMVersion_default_active, self).get_queryset().filter(is_default=True,is_active=True)
class BOMVersionQuerySet(models.QuerySet):
def active(self):
return self.filter(is_active=True)
def default(self):
return self.filter(is_default=True)
class BOM(models.Model):
bomversion = models.ForeignKey(BOMVersion)
material = models.ForeignKey(Material)
quantity = models.DecimalField(default =0 ,max_digits=19, decimal_places=3)
class Production_order(models.Model):
BOM = models.ForeignKey(BOM, on_delete=models.PROTECT)
soproduct = models.ForeignKey(SOproduct, on_delete=models.PROTECT)
quantity_order = models.DecimalField(max_digits=19, decimal_places=3)
鉴于我使用以下预取对象
soproduct = SOproduct.objects.select_related('product__material').prefetch_related(
Prefetch(
'product__material__bomversion_set',
queryset=BOMVersion.objects.default().active(),
to_attr='default_active_bomversions'
)
)
我的问题是在最深的 for 循环中
for production_order in bom.production_order_set.all
我想按 soproduct 进行过滤,所以我想知道如何修改现有的预取以满足此要求,如果可能的话?或者我唯一的选择是直接在模板中过滤>
(我不太了解 Prefetch 并且我尝试过的方法不起作用)
老实说,我不确定这是否有效,但请尝试一下:
soproduct = SOproduct.objects.select_related('product__material').prefetch_related(
Prefetch(
'product__material__bomversion_set',
queryset=BOMVersion.objects.default().active().prefetch_related(
Prefetch('bom_set', queryset=BOM.objects.all().prefetch_related('production_order_set'))
),
to_attr='default_active_bomversions'
)
)
然后在模板中,做一个简单的 if-check。
{% for soproduct in list_soproduct %}
{% for bomversion in soproduct.product.material.default_active_bomversions %}
{% for bom in bomversion.bom_set.all %}
{% for production_order in bom.production_order_set.all %}
{% if production_order.soproduct == soproduct %}
do my staff
{% endif %}
{% endfor %}
{% endfor %}
{% endfor %}
{% endfor %}
基本上 prefetch_related
所做的不是通过 SQL JOINs
获取相关数据(这是 select_related
所做的),而是通过单独查询,然后合并 python 中的结果。我认为 docs 很好地解释了这一点。
通过使用自定义 Prefetch
对象,我们可以做两件事
1) 改变 django 将使用的默认值 queryset
prefetch_related
(对过滤结果更有用) 和
2) 更改 django 将存储预取数据的属性(默认情况下将其放入 myobject.related_set.all()
这在我们进行额外的过滤时很有用,它会更改结果的上下文和 related_set.all()
不合适或会产生误导)。
我在模板中使用以下 for 循环显示数据
{% for soproduct in list_soproduct %}
{% for bomversion in soproduct.product.material.default_active_bomversions %}
{% for bom in bomversion.bom_set.all %}
{% for production_order in bom.production_order_set.all %}
{% endfor %}
{% endfor %}
{% endfor %}
{% endfor %}
数据来自以下模型
class SOproduct(models.Model):
product = models.ForeignKey(Product, on_delete=models.PROTECT)
so = models.ForeignKey(SO, on_delete=models.PROTECT)
quantity = models.DecimalField(max_digits=13, decimal_places=3, default = 0)
class Product(models.Model):
version = IntegerVersionField( )
GTIN = models.CharField(max_length=30)
name = models.CharField(max_length=30)
description = models.TextField(null=True, blank=True)
creation_time = models.DateTimeField(auto_now_add=True, blank=True)
material = models.ForeignKey(Material, on_delete=models.PROTECT)
class BOMVersion(models.Model):
name = models.CharField(max_length=200,null=True, blank=True)
description = models.TextField(null=True, blank=True)
material = models.ForeignKey(Material)
is_active = models.BooleanField(default=False)
is_default = models.BooleanField(default=False)
class BOMVersion_default_active_Manager(models.Manager):
def get_queryset(self):
return super(BOMVersion_default_active, self).get_queryset().filter(is_default=True,is_active=True)
class BOMVersionQuerySet(models.QuerySet):
def active(self):
return self.filter(is_active=True)
def default(self):
return self.filter(is_default=True)
class BOM(models.Model):
bomversion = models.ForeignKey(BOMVersion)
material = models.ForeignKey(Material)
quantity = models.DecimalField(default =0 ,max_digits=19, decimal_places=3)
class Production_order(models.Model):
BOM = models.ForeignKey(BOM, on_delete=models.PROTECT)
soproduct = models.ForeignKey(SOproduct, on_delete=models.PROTECT)
quantity_order = models.DecimalField(max_digits=19, decimal_places=3)
鉴于我使用以下预取对象
soproduct = SOproduct.objects.select_related('product__material').prefetch_related(
Prefetch(
'product__material__bomversion_set',
queryset=BOMVersion.objects.default().active(),
to_attr='default_active_bomversions'
)
)
我的问题是在最深的 for 循环中
for production_order in bom.production_order_set.all
我想按 soproduct 进行过滤,所以我想知道如何修改现有的预取以满足此要求,如果可能的话?或者我唯一的选择是直接在模板中过滤>
(我不太了解 Prefetch 并且我尝试过的方法不起作用)
老实说,我不确定这是否有效,但请尝试一下:
soproduct = SOproduct.objects.select_related('product__material').prefetch_related(
Prefetch(
'product__material__bomversion_set',
queryset=BOMVersion.objects.default().active().prefetch_related(
Prefetch('bom_set', queryset=BOM.objects.all().prefetch_related('production_order_set'))
),
to_attr='default_active_bomversions'
)
)
然后在模板中,做一个简单的 if-check。
{% for soproduct in list_soproduct %}
{% for bomversion in soproduct.product.material.default_active_bomversions %}
{% for bom in bomversion.bom_set.all %}
{% for production_order in bom.production_order_set.all %}
{% if production_order.soproduct == soproduct %}
do my staff
{% endif %}
{% endfor %}
{% endfor %}
{% endfor %}
{% endfor %}
基本上 prefetch_related
所做的不是通过 SQL JOINs
获取相关数据(这是 select_related
所做的),而是通过单独查询,然后合并 python 中的结果。我认为 docs 很好地解释了这一点。
通过使用自定义 Prefetch
对象,我们可以做两件事
1) 改变 django 将使用的默认值 queryset
prefetch_related
(对过滤结果更有用) 和
2) 更改 django 将存储预取数据的属性(默认情况下将其放入 myobject.related_set.all()
这在我们进行额外的过滤时很有用,它会更改结果的上下文和 related_set.all()
不合适或会产生误导)。