直接在模板中预取替代项与过滤器

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()不合适或会产生误导)。