Django 查询集字段替换为相关表字段

时间:2021-06-16 11:23:53

标签: sql django postgresql django-models

我有 2 个表(Product 和 CustomerProduct)

CustomerProduct 是 Customer 和 Product 之间的中间表。它为某些产品分配客户特定的定价。

产品型号

class Product(models.Model):
    name = models.CharField(max_length=200)
    price = models.DecimalField(max_digits=12, decimal_places=2)

样本数据

<头>
id 姓名 价格
1 橙色 1.5
2 苹果 2.2
3 猕猴桃 3.5

客户产品模型

class CustomerProduct(models.Model):
    customer = models.ForeignKey(
        "Customer",
        on_delete=models.CASCADE,
        related_name="customer_customerproducts",
    )
    product = models.ForeignKey(
        "Product",
        on_delete=models.CASCADE,
        related_name="product_customerproducts",
    )
    price = models.DecimalField(
        max_digits=12,
        decimal_places=2,
    )

样本数据

<头>
id customer_id product_id 价格
1 233 1 1.2
2 233 2 2.0

预期结果

我想查询所有产品,但如果存在相关字段,则根据 CustomerProduct.price 调整 Product.price。预期数据(json 中的示例但需要查询集):

[
    {
        id: 1
        name: "orange"
        price: 1.2 // The price adjusted according to customer price
    }
    {
        id: 2
        name: "apple"
        price: 2.0 // The price adjusted according to customer price
    }
    {
        id: 3
        name: "kiwi"
        price: 3.5 // Price remain original because CustomerProduct not exists.
    }
]

方法

我完全不知道如何在 Django 中实现这一点。如何做到这一点?

1 个答案:

答案 0 :(得分:0)

您可以将 Coalesce [Django docs]Subquery expressions [Django docs] 结合使用来注释查询集上的值,这将是您想要的价格。 Coalesce 给出传递给它的表达式的第一个非空值,因此我们将向它传递一个子查询,该子查询将从 CustomerProduct 或字段 price 本身按该顺序获取相关价格实现你想要的:

from django.db.models import F, OuterRef, Subquery
from django.db.models.functions import Coalesce


customer = Customer.objects.get(pk=some_pk)  # get the customer for which you want the results

# Last related price, will use it as a subquery
related_price = CustomerProduct.objects.filter(
    product=OuterRef('pk'),
    customer=customer,
).order_by('-pk').values('price')[:1]

products = Product.objects.annotate(
    price_offered=Coalesce(
        Subquery(related_price),  # If there is a matching object in the subquery use it
        F('price'),  # Else use the price of the Product itself
    )
)

for product in products:
    print(product.pk, product.name, product.price_offered)