多个左连接和针对同一模型原始查询的左连接转换为Django ORM

时间:2018-12-29 10:56:40

标签: python django orm

使用Django 1.11

我有以下型号:

class Vendor(Model):

class Product(Model):

class Pricebook(Model):

class Quote(Model):
     vendor = models.ForeignKey(Vendor)

class SKU(Model):
     product = models.ForeignKey(Product)
     pricebook = models.ForeignKey(Pricebook)
     vendor    = models.ForeignKey(Vendor)

class SKUPrice(Model):
    sku = models.ForeignKey(SKU, related_name="prices")

class LineItem(Model):
    quote = models.ForeignKey(Quote, related_name="quote_line_items")
    sku = models.ForeignKey(SKU)

这是对我有用的原始查询。

SELECT 
  qli.quantity, 
  sku_source.product_id, 
  sku_dest.id as sku_dest_id, 
  sku_dest_price.id as sku_dest_price_id 
FROM lineitem qli 
INNER JOIN sku sku_source ON 
  qli.sku_id = sku_source.id 
LEFT JOIN sku sku_dest ON 
  sku_dest.pricebook_id = sku_source.pricebook_id AND
  sku_dest.product_id = sku_source.product_id 
LEFT JOIN skuprice sku_dest_price ON
  sku_dest_price.status = 'default' AND 
  sku_dest_price.sku_id = sku_dest.id
WHERE qli.quotation_id = 40 AND 
  qli.quantity > 0 AND 
  sku_dest.vendor_id = 38;

我尝试过的是:

(the_quote_with_id_as_40
.quotation_line_items
.filter(quantity__gt=0)
.select_related('sku__product')
.values('sku__product__id', 'quantity')
)

这将产生此查询

SELECT "sku"."product_id", "lineitem"."quantity"
FROM "lineitem" 
INNER JOIN "sku" ON ("lineitem"."sku_id" = "sku"."id") 
WHERE ("lineitem"."quotation_id" = 40 AND 
       "lineitem"."quantity" > 0)

这不是我想要的。

我当然可以使用原始查询。但是我想知道是否可以使用ORM。至少,这是为了扩展我的知识。

谢谢。

更新

由于需要澄清我的模型,因此我正在编写以下内容。

我有一个Quote对象。它允许许多LineItem对象。每个LineItem都有SKU和Quote一对多。

一个SKU属于价格手册,一个产品和一个供应商。这些关系的描述也可以从上面的代码中收集。

但是为了清楚起见,我将在这里重复。

这种安排使得不同供应商可以出售一个相同的产品,但它们将显示为不同的SKU。

这是设计使然。

我们的用例是这样的:用户尝试将特定报价的LineItem复制到其他报价。

在用户看来,他们并不了解SKU,LineItem或产品之间的区别。

这是他们心目中的所有产品。他们只是希望同一产品出现在带有相同数量的不同报价中。

挑战是这样。现在,我们有两个报价(要复制的源报价和要复制到的目标报价)。两个报价可能具有相同的供应商。或者他们可能不会。用户希望我的Django应用程序自动适应这两种情况。

所以这意味着我需要在复制之前找出以下内容。

  • 订单项中规定的数量
  • 相对于SKU的LineItem中所述的产品
  • 找到卖方为目的地报价单出售的相应SKU
  • 及其相应的单价

原始查询使我可以在单个查询中提取所有4条信息。效率很高。

因此,您看到我的别名为sku_sourcesku_dest

这就是为什么我的WHERE条件包含3条条件的原因:

  1. 仅复制数量大于0的源订单项
  2. 仅复制特定来源报价中的LineItems
  3. 仅调查特定供应商(在这种情况下为目标报价的供应商)的SKU

可能是:

  1. 同一产品(相对于SKU)的多个LineItem将出现在同一源报价中。

  2. 目标供应商(即目标报价的供应商)未销售源供应商确实销售的特定产品。因此,我使用LEFT JOIN。这意味着该特定产品将不会重复。

我希望这可以解决所有问题。

1 个答案:

答案 0 :(得分:0)

对我来说,解决问题的方式不正确。

您仍在尝试解释查询,而不是所需的对象。在使用ORM时,您需要停止考虑查询,开始考虑对象以及它们之间如何进行播放/交互。这样,您将把业务逻辑放在代码(模型,管理器等)中,而不是查询中。您不想在查询中加入业务逻辑,然后开始思考,好,现在如何创建此查询?带有哑查询的智能对象比带有哑代码(对象)的智能查询更容易被其他对象优化和理解。

从对象开始,编写代码,然后对其进行优化。

如果您向我展示了一个未优化的对象版本,以了解如何制作这些副本,那么我可能会向您展示如何对其进行优化。

直到那时,也不要完全偏离主题,尝试一下此查询,但是我不能保证您在寻找什么:

quote.quotation_line_items.filter(
    quantity__gt=0,
    sku__vendor__sku__vendor=38,
    sku__vendor__sku__product=models.F('sku__product'),
    sku__vendor__sku__pricebook=models.F('sku__pricebook'),
).values(
    'quantity',
    'sku__product_id', # sku source product_id
    'sku__vendor__sku__id', # sku dest id
    'sku__vendor__sku__skuprice__id' # sku dest skuprice id
)