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

Django竞价功能首次出价触发AttributeError问题求助

Django竞价功能首次出价AttributeError问题修复方案

问题背景

开发Django竞价项目时,已有出价可正常加价,但首次出价(此时Listing的price字段为None)会触发AttributeError,需修改models或views代码解决该问题。

相关代码

view.py

def article(request, id):
    if request.method == "POST":
        new_bid = request.POST["new_bid"]
        user_name = request.user
        get_article = Listing.objects.get(pk=id)

        try:
            if int(new_bid) < get_article.current_price:
                messages.error(request, "Your bid does't count. Bid is less than highest bid.")
                return HttpResponseRedirect(reverse("article", args=(id, )))
            elif get_article.price.bid is None:
                messages.error(request, "Entry is not a number or is none")
                return HttpResponseRedirect(reverse("article", args=(id, )))
        except AttributeError:
                messages.error(request, "Attribute Error")
                return HttpResponseRedirect(reverse("article", args=(id, )))

        
        if int(new_bid) > get_article.price.bid:
            bid_placement = Bid(
                bid = int(new_bid),
                user_name = user_name)
            bid_placement.save()
            get_article.price = bid_placement
            get_article.save()
        return HttpResponseRedirect(reverse("article", args=(id, )))
    else:
        get_article = Listing.objects.get(pk=id)
        listed = request.user in get_article.watchlist.all()

        return render(request, "auctions/article.html", {
            "get_article": get_article,
            "listed": listed
        })

model.py

class Bid(models.Model):
    bid = models.FloatField(default=0)
    user_name = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True, related_name="user_bid")

    def __str__(self):
        return f"{self.bid}"

class Listing(models.Model):
    title = models.CharField(max_length=20)
    description = models.CharField(max_length=500)
    developer_id = models.ForeignKey(Games, on_delete=models.CASCADE, blank=True, null=True, related_name="developer_id")
    current_price = models.IntegerField()
    price = models.ForeignKey(Bid, on_delete=models.CASCADE, blank=True, null=True, related_name="price")
    user_name = models.ForeignKey(User, on_delete=models.CASCADE, related_name="user")
    photo = models.URLField()
    watchlist = models.ManyToManyField(User, blank=True, null=True, related_name="user_watchlist")

    def __str__(self):
        return f"{self.title} {self.description} {self.developer_id} {self.current_price} {self.photo} {self.price}"

article.html

{% extends "auctions/layout.html" %}

{% block body %}
    <h2>Article</h2>

    <ul><a href="{% url 'index' %}"></a>
        <img src="{{ get_article.photo }}" alt="picture">
        <li>Game: {{ get_article.title }}<br>
            Description: {{ get_article.description }}<br>
            Developer: {{ get_article.developer_id }}<br>
            Current Price: {{ get_article.price }}€<br>

        </li>
    </ul>
    {% if user.is_authenticated %}
    My Bid: {{ get_article.price }} €<br>
    <form action="{% url 'article' id=get_article.id %}" method="post">
        {% csrf_token %}
        <label>Bid:</label>
        <input type="number" name="new_bid" min="0" placeholder="Bid" required><br>
        <input type="submit" value="Place Bid">
    </form>
    {% else %}
    <p>Please LogIn in to place a bid</p>
    {% endif %}
    {% for message in messages %}
    <p style="color:red" {% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</p>
    {% endfor %}

{% endblock %}

问题原因

首次出价时,Listing的price字段为None,代码中直接访问get_article.price.bid会触发AttributeError(因为None对象没有bid属性);同时价格字段类型不一致(Listing.current_price是IntegerField,Bid.bid是FloatField)也会导致潜在的类型转换问题。

修改方案

1. 修正models.py字段类型统一

将Listing的current_price字段改为FloatField,与Bid.bid类型保持一致:

# model.py - Listing类修改
current_price = models.FloatField()

2. 重构view.py逻辑,处理price为None的情况

优化出价判断逻辑,提前处理无出价的场景,避免AttributeError:

def article(request, id):
    if request.method == "POST":
        # 先处理输入合法性,捕获非数字输入
        try:
            new_bid = float(request.POST["new_bid"])
        except ValueError:
            messages.error(request, "请输入有效的数字出价")
            return HttpResponseRedirect(reverse("article", args=(id, )))
        
        user_name = request.user
        get_article = Listing.objects.get(pk=id)
        
        # 获取当前最高出价:有出价则取Bid的bid,无出价则用初始current_price
        current_highest = get_article.price.bid if get_article.price else get_article.current_price
        
        # 验证出价是否高于当前最高
        if new_bid <= current_highest:
            messages.error(request, f"出价必须高于当前价格:{current_highest}€")
            return HttpResponseRedirect(reverse("article", args=(id, )))
        
        # 创建新出价并关联到Listing
        bid_placement = Bid(bid=new_bid, user_name=user_name)
        bid_placement.save()
        get_article.price = bid_placement
        get_article.save()
        
        messages.success(request, "出价成功!")
        return HttpResponseRedirect(reverse("article", args=(id, )))
    else:
        get_article = Listing.objects.get(pk=id)
        listed = request.user in get_article.watchlist.all()
        # 计算显示价格,传递给模板
        display_price = get_article.price.bid if get_article.price else get_article.current_price
        return render(request, "auctions/article.html", {
            "get_article": get_article,
            "listed": listed,
            "display_price": display_price
        })

3. 优化article.html模板显示

使用传递的display_price变量,避免在模板中处理price为None的情况:

{% extends "auctions/layout.html" %}

{% block body %}
    <h2>Article</h2>

    <ul><a href="{% url 'index' %}"></a>
        <img src="{{ get_article.photo }}" alt="picture">
        <li>Game: {{ get_article.title }}<br>
            Description: {{ get_article.description }}<br>
            Developer: {{ get_article.developer_id }}<br>
            Current Price: {{ display_price }}€<br>

        </li>
    </ul>
    {% if user.is_authenticated %}
    当前最高出价: {{ display_price }} €<br>
    <form action="{% url 'article' id=get_article.id %}" method="post">
        {% csrf_token %}
        <label>我的出价:</label>
        <input type="number" name="new_bid" min="0" step="0.01" placeholder="输入出价" required><br>
        <input type="submit" value="提交出价">
    </form>
    {% else %}
    <p>请登录后出价</p>
    {% endif %}
    {% for message in messages %}
    <p style="color:red" {% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</p>
    {% endfor %}

{% endblock %}

修改说明

  • 统一价格字段类型:避免整数与浮点数混合使用导致的精度或比较问题
  • 提前处理输入合法性:捕获非数字输入,给出明确提示
  • 简化出价判断逻辑:直接计算当前最高出价,无需嵌套判断和异常捕获
  • 模板传递预处理后的价格:减少模板中的逻辑处理,代码更清晰

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 07:45:58