Skip to main content
 首页 » 编程设计

python之具有复杂外键遍历的棘手 Django QuerySet

2025年05月04日26birdshome

我正在改编来自 Beginning Django E-Commerce 的 Jim McGaw 的电子商务网站供我的客户使用。我的客户销售由一组定制部件组成的计算机。进入系统的部分会发生变化。例如,两年后售出的 Super Samurai 系统与我们明天售出的系统将有不同的部件。因此,当我们销售每个 Super Samurai 系统时,我们需要了解在销售时哪些部件进入了 Super Samurai。

我在使用 QuerySet 时遇到问题,它从将零件映射到构建的表中复制所有零件(即进入 Super Samurai 的零件)...

class Build(models.Model): 
    build = models.ForeignKey(PartModel, related_name='+') 
    part = models.ForeignKey(PartModel, related_name='+') 
    quantity = models.PositiveSmallIntegerField(default=1) 
 
    class Meta: 
        abstract = True 
        unique_together = ('build', 'part') 
 
    def __unicode__(self): 
        return self.build.name + ' with ' + str(self.quantity) + ' * ' + \ 
               self.part.family.make.name + ' ' + self.part.name 
 
class BuildPart(Build): 
    pass 
 
    class Meta: 
        verbose_name = "Build Part" 

我需要将构建部件从 BuildPart 表复制到 OrderBuildPart 表中...

class OrderBuildPart(Build): 
    orderItem = models.ForeignKey(OrderItem, unique=False) 
 
    class Meta: 
        verbose_name = "Ordered Build Part" 

...以便将来我们知道哪些部分进入了某某的构建。

McGaw 的电子商务网站不允许商品与其他商品捆绑在一起。因此,与其为构建及其部件创建两个不同表(和两个系列的 SKU)的噩梦场景,我希望构建与任何其他部件一样......

class PartModel(models.Model): 
    family = models.ForeignKey(PartFamily) 
    name = models.CharField("Model Name", max_length=50, unique=True) 
    slug = models.SlugField(help_text="http://www.Knowele.com/<b>*slug*</b>", 
                            unique=True) 
    vpn = models.CharField("VPN", help_text="Vendor's Part Number", 
                               max_length=30, blank=True, null=True) 
    url = models.URLField("URL", blank=True, null=True) 
    costurl = models.URLField("Cost URL", blank=True, null=True) 
    cost = models.DecimalField(help_text="How much knowele.com pays", max_digits=9, decimal_places=2, blank=True, null=True) 
    price = models.DecimalField(help_text="How much a customer pays", max_digits=9, decimal_places=2, blank=True, null=True) 
    isActive = models.BooleanField(default=True) 
    isBestseller = models.BooleanField(default=False) 
    isFeatured = models.BooleanField(default=False) 
    isBuild = models.BooleanField(default=False) 
    description = models.TextField(blank=True, null=True) 
    updated = models.DateTimeField(auto_now=True) 
    created = models.DateTimeField(auto_now_add=True) 
    buildpart = models.ManyToManyField('self', through='BuildPart', 
                                symmetrical=False, related_name='+') 
 
    class Meta: 
        ordering = ['name'] 
        verbose_name = "Product Model" 
 
    def __unicode__(self): 
        return self.name 
 
    def get_absolute_url(self): 
        from django.core.urlresolvers import reverse 
        return reverse('productdetail', args=[self.slug]) 

buildpart 字段引用 ManyToMany BuildPart 表,该表允许构建具有多个部分,并且一个部分与多个构建相关联。

通过调整 McGaw 的代码,我得到了几乎所有我需要的东西,直到我完成 PayPal 付款并尝试记录在销售的确切时刻哪些部件进入了已售出的构建...

def payment(request): 
    token = request.POST['token'] 
    payer = request.POST['payer'] 
 
    result = paypal.do_express_checkout_payment(request, token, payer) 
 
    if result['ACK'][0] in ['Success', 'SuccessWithWarning']: 
        cart = Cart.objects.get(cart_id=get_cart_id(request)) 
        finalOrder = Order() 
        finalOrder.cart_id = get_cart_id(request) 
        finalOrder.token = token 
        finalOrder.corID = result['CORRELATIONID'][0] 
        finalOrder.payerID = payer 
        finalOrder.ipAddress = request.META['REMOTE_ADDR'] 
        finalOrder.first = cart.first 
        finalOrder.last = cart.last 
        finalOrder.address = cart.address 
        finalOrder.email = cart.email 
        finalOrder.transactionID = result['PAYMENTINFO_0_TRANSACTIONID'][0] 
        finalOrder.status = 'f' 
        finalOrder.save() 
 
        for item in get_cart_items(request): 
            oi = OrderItem() 
            oi.cart_id = item.cart_id 
            oi.quantity = item.quantity 
            oi.product = item.product 
            oi.price = item.price() 
            oi.save() 
 
            if item.product.isBuild: 
                for part in get_build_parts(request, item): 
                    bp = OrderBuildPart() 
                    bp.build = part.build 
                    bp.part = part.part 
                    bp.quantity = part.quantity 
                    bp.orderItem = oi 
                    bp.save() 
 
        empty_cart(request) 
 
        return render(request, 'payment.html', locals()) 

在我们点击 get_build_parts 函数之前一切似乎都很好......

def get_build_parts(request, part): 
    return BuildPart.objects.filter(build__id=part__product__pk) 

...Django 的事后分析提示“NameError at/payment/global name 'part__product__pk' is not defined”

我如何遍历这些复杂的关系,以便我的老板可以查看每个客户的构建中使用了哪些部件?

请您参考如下方法:

查找的值(value)方面并不像您想象的那样工作。双下划线仅用于左侧:实际上,这是绕过 Python 语法要求的一种技巧。在右侧,您传递一个普通表达式,它可以使用标准点语法遵循对象关系:

return BuildPart.objects.filter(build__id=part.product.pk)