【问题标题】:Multiple left join and left join against the same model raw query convert to Django ORM针对同一模型原始查询的多个左连接和左连接转换为 Django ORM
【发布时间】:2019-05-26 21:22:37
【问题描述】:

使用 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。

这是设计使然。

我们的用例是这样的:用户尝试将特定报价的 LineItems 复制到不同的报价。

在用户的心目中,他们没有能力理解 SKU、LineItem 或产品之间的差异。

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

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

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

  • LineItem 中规定的数量
  • LineItem 中所述的产品相对于 SKU
  • 查找供应商为目的地报价销售的相应 SKU
  • 及其对应的单价

原始查询允许我在一个查询中提取所有 4 条信息。很有效率。

因此你看到我有别名 sku_sourcesku_dest

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

  1. 只会复制数量 > 0 的源行项目
  2. 只会复制来自特定来源报价单的 LineItems
  3. 仅针对特定供应商(在本例中为目标报价的供应商)的 SKU 将被调查

有可能:

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

  2. 目标供应商(即目标报价的供应商)不销售源供应商确实销售的特定产品。因此我使用左连接。这意味着该特定产品不会被复制。

我希望这可以解决问题。

【问题讨论】:

  • 感谢您的回复。所以你的观点是有时使用原始查询更好?是这样的吗?
  • @KimStacks Django ORM 是对特定数据库的抽象,但有助于了解您的实际数据库以帮助您。
  • 你能澄清一下你的模型吗?您的查询(您声称有效的查询)似乎将 SKU 表加入到自身中,而实际上什么也没做。即LEFT JOIN sku sku_dest 没有多大意义。除非您有多个具有完全相同外键的 SKU 对象。
  • @KimStacks 用文字(而不是查询)解释您想要实现的目标。 ORM 与对象一起工作,那么您需要什么样的对象和什么样的相关数据?

标签: python django orm


【解决方案1】:

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

您仍在尝试解释查询,而不是您需要的对象。使用 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
)

【讨论】:

  • 嗨,托多,感谢您的来信。你所说的智能对象和愚蠢的查询激起了我的兴趣所以你所说的>如果你向我展示一个未优化的面向对象版本,你需要如何/什么来制作这些副本,我可能会向你展示如何优化它。我如何“展示”这个?
  • 或者,如果我创建一些示例数据记录来帮助讨论,会有帮助吗?
  • 想象一下您无法编写自定义 SQL,您将如何解决这个问题? (您可以根据需要进行任意数量的查询)
猜你喜欢
  • 1970-01-01
  • 2018-06-26
  • 2018-08-17
  • 2021-12-03
  • 2017-06-30
  • 2019-03-28
  • 1970-01-01
  • 2020-05-18
  • 2019-01-07
相关资源
最近更新 更多