【问题标题】:What will be the most efficient way to filter the objects in django在 django 中过滤对象的最有效方法是什么
【发布时间】:2020-08-16 08:07:33
【问题描述】:

我正在开发一个基本的电子商务网站。我创建了一个超级用户页面,我想在其中查看所有订单以及订单详细信息,例如订购的客户、他们的地址等。这些是我的 models.py:

class ShippingAddress(models.Model):
    customer=models.ForeignKey(Customer,on_delete=models.SET_NULL,null=True)
    order=models.ForeignKey(Order,on_delete=models.SET_NULL,null=True)
    address=models.CharField(max_length=200,null=False)
    email=models.EmailField(null=False)

class Customer(models.Model):
    user=models.OneToOneField(MyUser,null=True,blank=True,on_delete=models.CASCADE)
    email=models.CharField(max_length=100)

class Order(models.Model):
    customer=models.ForeignKey(Customer,on_delete=models.SET_NULL,null=True,blank=True)
    complete=models.BooleanField(default=False,null=True,blank=False)

class OrderItem(models.Model):
    product=models.ForeignKey(Product,on_delete=models.SET_NULL,null=True)
    order=models.ForeignKey(Order,on_delete=models.SET_NULL,null=True,)

这是我的views.py:

def superuser(request):
    user=User.objects.all()
    customer=Customer.objects.all()
    order=Order.objects.filter(complete=True)
    items=OrderItem.objects.all()
    shipping=ShippingAddress.objects.all()
    return render(request,"superuser.html",{'order':order,'items':items,'customer':customer,'shipping':shipping})

目前在我的模板中,我无法遍历上述上下文,以便我可以获取所有订单,并且对于每个订单,我都可以打印他们的订单项以及运输详细信息以及客户详细信息。我尝试了一种非常有效的方式,即遍历所有订单,然后遍历所有订单项并检查 orderitem.order.id 是否等于 order.id 。请告诉我在上下文中传递对我的需要最有效的对象的最佳方法是什么。以及如何在我的模板中迭代它们。 谢谢

【问题讨论】:

  • OrderItem 引用了一个Product 模型,你能把那个也包括进来吗?为了更好地理解你想要做什么,我将尝试描述一个“伪对象”,看看它是否能满足你的需求:一个“订单”对象,你可以循环遍历每个“订单”的位置:1 a 'customer_details' 属性将保存客户详细信息,2 一个 'shipping_address' 属性和 3 一个属性 'order_items' 您可以循环访问以获取所有相关的订单项目。这个假设的“订单”对象会包含您需要的所有信息吗? :)
  • 会的。我的产品模型只是一个以产品名称和价格为字段的模型

标签: python django


【解决方案1】:

这个怎么样?

# view
from django.shortcuts import render
from .models import Order

def superuser(request):
    orders = Order.objects.select_related('customer__user')
    orders = orders.prefetch_related('shippingaddress_set')
    orders = orders.prefetch_related('orderitem_set')
    return render(request,"superuser.html",{'orders':orders})

您当然可以将.select_related.prefetch_related 调用链接到同一行,我在这里将它们分开以提高可读性。您可以在文档中阅读有关 select_relatedprefetch_related 的信息。您现在可以在这样的模板中使用“订单”查询集:

<!--template-->
{% if orders %}
<ul>
{% for order in orders %}
    <li>order id: {{order.id}}</li>
    <li>customer name: {{order.customer.id}}</li>
    <li>customer email: {{order.customer.email}}</li>
    {% if order.shippingaddress_set.all %}
    <li>Shipping addresses:
    <ul>
    {% for shipadd in order.shippingaddress_set.all %}
        <li>shipping address: {{shipadd.address}}</li>
    {% endfor %}
    </ul>
    </li>
    {% endif %}
    {% if order.orderitem_set.all %}
    <li>Order items:
    <ul>
    {% for item in order.orderitem_set.all %}
        <li>orderitem id: {{item.id}}</li>
    {% endfor %}
    </ul>
    </li>
    {% endif %}
{% endfor %}
</ul>
{% endif %}

使用Django Debug Toolbar 并转到上面的页面,我被告知进行了 3 个 SQL 查询:

  • 从“订单”表中提取数据,对“客户”表执行左外连接,并使用客户 ID。
  • 从“shippingaddress”表中提取数据,其中 WHERE 子句指定具有“order_id”IN 的行()。
  • 对“orderitem”表进行了类似的查询。

这样,Django 会抢先从数据库中获取所有需要的数据,而不是 e. G。为每次迭代执行另一个查询。

您可能会觉得奇怪,我在 HTML 模板中包含了送货地址的循环。然而,这是必要的,因为您设置模型的方式是从送货地址到订单的多对一关系。这对 IMO 没有多大意义,因此您可能需要重新定义 ShippingAddress 模型与其他模型的关系。

如果您想使用与每个订单相关的其他信息,当然可以在将 QuerySet 放入上下文之前添加更多 prefetch_related/select_related 调用。

【讨论】:

  • 我没想到会有这么精确的答案。它对我来说非常有用,所以谢谢。有些事情我想知道。首先是,如果我想过滤完整 = True 或任何其他过滤器的订单,那么我将如何做到这一点?另外,我正在按照教程进行操作,我猜想 Shipadd 的多对一关系。适用于当客户有多个订单并希望将它们交付到多个地址时。 (我是初学者,可能会错)。
  • 酷!对于过滤,使用方便的filter QuerySet 方法。因此,在我的示例中,您将添加以 orders = Order.objects.filter(complete=True) 开头(在其他可能是最好的之前)。关于关系:在现实世界中,一个订单应该绑定多少个地址?一,除非发生了非常奇怪的事情。这就是为什么多(地址)对一(订单)没有意义的原因。相反,多(订单)对一(地址)却可以,因为可能有多个订单应该发送到一个地址。
  • 好的,我会负责运输的。另外我在哪里过滤订单。我应该把你给的 stmt 放在 def 超级用户下面吗?
  • 完全正确,因此您还必须将 orders = Order.objects.select_related('customer__user') 替换为 orders = orders.select_related('customer__user'),因为您已经使用 QuerySet 初始化了 orders 变量,并且您希望继续链接方法调用 (而不是简单地用新的查询集覆盖orders)。
猜你喜欢
  • 1970-01-01
  • 2018-04-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-12
  • 2011-09-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多