You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django报错Account无orderproduct属性 商品下载预览功能异常排查

Django已购商品下载预览功能报错修复

问题表现

  • 服务运行抛出异常:'Account' object has no attribute 'orderproduct'
  • 商品详情页的下载、预览按钮无法正常显示
  • 预期逻辑:用户完成商品购买、生成对应OrderProduct记录后,可对已购图片执行下载、预览操作

错误原因

  1. Django外键反向关联名使用错误:OrderProduct模型通过user外键关联Account模型,且未自定义related_name时,1对多关系的默认反向关联名为orderproduct_set,不存在直接的orderproduct属性,因此触发属性不存在报错。
  2. 关联查询逻辑错误:即使写对反向关联名,直接调用request.user.orderproduct.product_set.all()也不符合ORM查询规则——一个用户对应多条订单商品记录,需要从订单商品记录集合中跨关系匹配对应商品,同时缺少支付状态校验,会导致未支付的购物车商品也被判定为已购。

修复方案

1. 修复下载视图权限校验逻辑

首先确保视图文件已导入OrderProduct模型,将原视图中错误的购买判断逻辑替换为精准的存在性查询,修复后完整视图代码如下:

class ProductDownloadView(MultiSlugMixin, DetailView):
    model = Product
    
    def get(self, request, *args, **kwargs):
        obj = self.get_object()
        # 校验当前用户是否已完成当前商品的支付
        has_purchased = OrderProduct.objects.filter(
            user=request.user,
            product=obj,
            ordered=True
        ).exists()
        if has_purchased:
            filepath = Path.joinpath(settings.PROTECTED_UPLOADS, obj.media.path)
            print(filepath)
            guessed_type = guess_type(filepath)[0]
            wrapper = FileWrapper(open(filepath, "rb"))
            mimetype = "application/force-download"
            if guessed_type:
                mimetype = guessed_type
            response = HttpResponse(wrapper, content_type=mimetype)
            if not request.GET.get("preview"):
                response["Content-Disposition"] = "attachment; file=%s"%(obj.media.name)
            response['X-SendFile'] = str(obj.media.name)
            return response
        else:
            raise Http404

2. 修复前端模板权限判断

将模板中错误的关联判断替换为正确的反向查询,修复后模板代码如下:

{% if request.user.is_authenticated and object.media and request.user.orderproduct_set.filter(product=object, ordered=True).exists %}
     <p>
         <a href="{{ object.get_download }}">Download</a>
         <a href="{{ object.get_download }}?preview=True">Preview</a>
     </p>
 {% else %}
     <a class="btn btn-warning btn-xl js-scroll-trigger p-3"
        href="{% url 'cart:add_cart' product.id %}">ADD TO CART</a>
 {% endif %}

提示:模板中使用.exists做判断不需要加括号,Django模板引擎会自动执行对应方法,查询性能远高于拉取全量数据再做包含判断。

3. 可选优化:自定义反向关联名

如果觉得默认的orderproduct_set写法冗余,可以修改OrderProduct模型的user外键字段,添加自定义related_name:

class OrderProduct(models.Model):
    order = models.ForeignKey(Order, on_delete=models.CASCADE)
    payment = models.ForeignKey(Payment, on_delete=models.SET_NULL, blank=True, null=True)
    # 新增related_name自定义反向关联名
    user = models.ForeignKey(Account, on_delete=models.CASCADE, related_name="order_records")
    product = models.ForeignKey(Product, on_delete=models.CASCADE)
    ordered = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.user.username

修改完成后执行以下命令同步数据库结构:
python manage.py makemigrations
python manage.py migrate
后续代码中就可以用request.user.order_records代替request.user.orderproduct_set,减少拼写错误概率。

注意:所有已购权限判断必须保留ordered=True过滤条件,否则未支付的购物车项、已取消订单关联的商品会被误判为已购,产生越权下载风险。

内容的提问来源于stack exchange,提问作者Meshach Akakah

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 22:15:48