【问题标题】:Django models and orm and foreign key problemsDjango 模型和 orm 以及外键问题
【发布时间】:2023-02-24 09:51:16
【问题描述】:

我想得到所有相关模型的数据,但我只得到了一些相关数据。为了避免 N+1 问题,我使用了 select_related() 和 prefetch_related() 方法。

起初,我有这些模型:

class OrderList(models.Model):
    order_id=models.CharField(max_length=100)
    order_status=models.CharField(max_length=100)

class ProductInOrder(models.Model):
    order_key=models.ForeignKey(OrderList, on_delete=models.CASCADE, related_name="order_key")
    
    product_id=models.CharField(max_length=100)
    product_price=models.CharField(max_length=100)

class MemosInProduct(models.Model):
    product_key=models.ForeignKey(ProductInOrder, on_delete=models.CASCADE, related_name="product_key")

    memo=models.CharField(max_length=100)
    blahblah some codes...

这个模型的简短解释,一个 OrderList 有很多 ProductInOrder(一对多) 一个 ProductInOrder 有很多 MemosInProduct(一对多)

然后,我在 django shell 中运行这段代码:

order_list=OrderList.object.select_related("order_key", "product_key").all()

我接受了所有有序列表数据以及与之相关的所有数据(产品、备忘录):除外

order_list[0].order_key[0].product_key[0].memo order_list[0].order_key[0].product_key[1].memo order_list[0].order_key[1].product_key[0].memo ...

但我得到了:输出

select_related 中给出的字段名称无效:“order_key”、“product_key”。选项是:(无)

我也试过这个:

order_list=MemosInProduct.object.select_related("order_key", "product_key").all()

但输出不匹配我除外。

【问题讨论】:

    标签: python django django-models orm


    【解决方案1】:

    如果模型中存在外键,则从Django Documentationselect_related获取相关对象(外键关系)。这意味着它获取与转发相关的对象。

    例如:

    def OrderList(models.Model):
        order_id=models.CharField(max_length=100)
        order_status=models.CharField(max_length=100))
    

    您不能使用select_related,因为此模型中没有前向关系。因此,Django 会引发错误。

    select_related 中给出的字段名称无效:“order_key”、“product_key”。选项是:(无)

    但是,您可以将select_related用于ProductInOrderMemosInProduct,因为对于ProductInOrderMemosInProduct,存在正向关系,分别为order_keyproduct_key

    对于后向关系(查询模型中不存在外键),您应该使用prefetch_related

    所以,在你的情况下,查询应该是

    OrderList.objects.prefetch_related("order_key", "order_key__product_key").all()
    

    在这里,你可以看到我也将product_key更改为order_key__product_key,因为product_key不是OrderList的相关对象,它是ProductInOrder的相关对象

    这是理论。也许您需要在查询中使用Prefetch。一个例子

    OrderList.objects.prefetch_related(Prefetch('order_key', queryset=ProductInOrder.objects.all(), to_attr='order_keys')).all()
    

    但是,请阅读 Django 文档了解详细信息。

    【讨论】:

      【解决方案2】:

      我会通过相反的方向来解决这个问题,并检索 MemosInProduct 对象及其相关对象。例如,要使用 order_status = 'overdue' 获取与 OrderList 对象相关的所有内容(我猜):

      queryset = MemosInProduct.objects.order_by(
                     'product_key__order__key__order_id',
                     'product_key__product_id'
                  ).select_related(
                     'product_key', 'product_key__order_key'
                  ).filter(
                     product_key__order__key__order_status='overdue'
                  )      
      

      在检索到的对象的循环中,如果您跟踪循环中的前一个项目,您可以知道何时进入不同的 ProductInOrder 或 OrderList

      last_object = None
      for memo in queryset:
          product = memo.product_key
          order = product.order_key
          if ( last_object is None or 
               last_object.product_key.order_id != order.id ):
             # do stuff for a changed order compared to last (or the first loop)
          if ( last_object is None or 
               last_object.product_key_id != product.pk) :
             # do stuff for a changed product compared to last
      
          # do stuff for every memo
          last_object = memo
      

      或者,您可以构造一个列表列表的列表,当在要呈现的上下文中传递时,这在模板语言中很容易遍历。

      请记住,Python 赋值是名称绑定,而不是对象复制,所以这种事情很有效。在循环中,select_related 预取了产品和订单。

      【讨论】:

      • 谢谢,伙计,多么好的答案。但 Almabud 的回答更适合我的情况。但真的谢谢你。
      猜你喜欢
      • 2012-08-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-03
      • 2020-11-13
      • 2019-06-30
      • 2017-04-29
      相关资源
      最近更新 更多