【问题标题】:TypeError & AssertionError when clicking on buttons in Django单击 Django 中的按钮时出现 TypeError 和 AssertionError
【发布时间】:2021-01-01 14:46:44
【问题描述】:

为这篇长文道歉,我正在尝试实现一个简单的按钮,可以在关注列表中添加或删除项目。

虽然我最初设法适当地实现了“addwatchlist”功能,但我修改了我的代码几个小时,但我有点无法再次理解它。

这是我按下 “添加到关注列表” 按钮时收到的错误:

TypeError at /addwatchlist/10

Field 'id' expected a number but got <Listing: "Gloss + Repair Spray">.

Request Method:     GET
Request URL:    http://127.0.0.1:8000/addwatchlist/10
Django Version:     3.1.1
Exception Type:     TypeError
Exception Value:    

Field 'id' expected a number but got <Listing: "Gloss + Repair Spray">.

追溯:

watchlist.save()

▼ Local vars
Variable    Value
id          10

request     <WSGIRequest: GET '/addwatchlist/10'>

watchlist   Error in formatting: TypeError: Field 'id' expected a number but got <Listing: "Gloss + Repair Spray">.

这是我按下 “从监视列表中删除” 按钮时收到的错误,请注意,这与我最初收到的错误完全相同,这反过来又迫使我尝试调整方式“添加到关注列表”功能:

AssertionError at /removewatchlist/1

Watchlist object can't be deleted because its id attribute is set to None.

Request Method:     GET
Request URL:    http://127.0.0.1:8000/removewatchlist/1
Django Version:     3.1.1
Exception Type:     AssertionError
Exception Value:    

Watchlist object can't be deleted because its id attribute is set to None.

追溯:

watchlist.delete()

▼ Local vars
Variable    Value
id          1

request     <WSGIRequest: GET '/removewatchlist/1'>

watchlist   <Watchlist: 1 Watchlist ID# None : admin watchlisted : "Iphone 11 Pro">

Models.py

class Listing(models.Model):
    owner = models.CharField(max_length=64)
    title = models.CharField(max_length=64)
    description = models.TextField(max_length=1024)
    startingBid = models.IntegerField()
    link = models.URLField(blank=True)
    category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name="category_id")
    time = models.DateTimeField(auto_now_add=True)
    active = models.BooleanField(null=False, default='True')

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

class Watchlist(models.Model):
    user = models.CharField(max_length=64)
    watchlist_listingid = models.ForeignKey(Listing, on_delete=models.CASCADE, related_name="watchlist_listingid", null=True)

    def __str__(self):
        return f'{self.watchlist_listingid_id} Watchlist ID# {self.id} : {self.user} watchlisted : {self.watchlist_listingid}'

Views.py

@login_required
def watchlist(request):
    if request.user.username:
        return render(request, "auctions/watchlist.html", {
        "items" : Watchlist.objects.filter(user=request.user.username),
        "watchlist_listingid" : Watchlist.objects.values_list('watchlist_listingid', flat=True).filter(user=request.user.username),
        "watchlist_count" : len(Watchlist.objects.filter(user=request.user.username))
    })

def addwatchlist(request, id):

    if request.user.username:
        watchlist = Watchlist(user=request.user.username,watchlist_listingid_id=Listing.objects.filter(id=id).first())
        watchlist.save()
        return redirect('listing', id=id)

    else:
        return render('auctions/watchlist.html', {})

def removewatchlist(request, id):
        if request.user.username:
        # all_entries = Watchlist.objects.values_list('watchlist_listingid', flat=True).filter(user='admin')
        # for i in all_entries:
        #     if i == id:
        #         removeMe = i
        #     else:
        #         removeMe = 'TEST ERROR'
            watchlist = Watchlist(user=request.user.username,watchlist_listingid_id=id)
            watchlist.delete()
            return redirect('listing', id=id)

Urls.py

path("listing/<int:id>", views.listing, name="listing"),
path("watchlist", views.watchlist, name="watchlist"),
path("addwatchlist/<int:id>", views.addwatchlist, name="addwatchlist"),
path("removewatchlist/<int:id>", views.removewatchlist, name="removewatchlist")

添加/删除按钮托管在 listing.html 上:

{% if user.is_authenticated %}
        <p>
            {% if watchlisted %}
            <a href="{% url 'removewatchlist' item.id %}"><button class="btn btn-danger">Remove from watchlist</button></a>
            {% else %}
            <a href="{% url 'addwatchlist' item.id %}"><button class="btn btn-success">Add to watchlist</button></a>
            {% endif %}
          </p>
    {% endif %}

问题:

  • 我使用 Watchlist.watchlist_listingid 作为 上市的外键?我看过很多其他帖子 人们往往根本不使用外键,尽管我相信 可能没有优化。
  • 我最终可能会使用表单来封装这些按钮,但我 我的listing.html 页面中已经有一个表单(添加cmets)。 尽管我没有处理表单中的数据可能会更容易 设法找出如何在 一个 HTML 页面。您认为最佳实践是什么?表格或直接 链接召唤功能?
  • 对于“添加到关注列表”,为什么我获得def __str__(self):return f'"{self.title}"'而不是简单地在我的 关注列表?
  • 最后,对于“从监视列表中删除”,当我知道在我的示例中,为什么我会收到 "id attribute is set to None" “Iphone 11 Pro”的 Listing.id 是 1,从技术上讲, Watchlist.id (在我的模型中根本没有显示)是 11 基于 Django 管理页面。甚至硬编码 11 以强制删除它 Watchlist 表仍然返回此“无”错误。

期待您的回复!

【问题讨论】:

    标签: python django django-models foreign-keys


    【解决方案1】:

    根据您的具体错误,问题出在这一行,从错误消息中应该可以清楚地看到:

    watchlist = Watchlist(user=request.user.username,watchlist_listingid_id=Listing.objects.filter(id=id).first())
    

    id 需要一个 int(模型实例的 id),但实际上您是在向它传递一个列表模型的对象实例; filter() 返回一个查询集,然后 .first() 返回该查询集中的第一个对象。

    这就是为什么错误告诉您“字段 id”,它需要一个 int,而是得到“

    ”——这是您的列表模型的实际实例。

    解决方案: 只需在 .first() 之后添加 .id 即可解决您的问题,因此您实际上是将该对象的 id 传递给 id 字段:

    watchlist = Watchlist(user=request.user.username,watchlist_listingid_id=Listing.objects.filter(id=id).first().id)
    

    这应该可以解决您的具体问题;话虽如此,但我强烈建议您为这些按钮使用表单。目前,您允许以不安全的方式修改数据库的内容。任何修改数据库内容的活动都应作为 POST 请求进行传递。即使您没有使用任何实际的输入字段,您也可以很容易地使用表单来做到这一点。该按钮会将空表单作为 POST 请求提交,您可以在模板中的表单标签中包含一个 csrf 令牌,并且数据库将进行相应修改。如果您真的不想使用表单,另一种选择是使用 AJAX 调用(通过 jQuery),它可以在不使用表单的情况下作为 POST 请求发送。解释如何做到这一点超出了本回复的范围,但这里有大量关于 SO 的信息。

    最后,如果关注列表与用户相关联,您可能应该考虑完全不同的数据库架构来获取结果 - 即,将用户模型实际绑定到他们添加到关注列表中的项目(这是可能的) ,但是,我在这里误解了您的意图,也许您不希望那样)。您可以通过多对多关系连接用户模型和列表模型,因为用户可以“关注”许多列表,并且列表可以被许多用户“关注”。

    【讨论】:

    • 非常感谢您的解决方案。这按预期工作。尽管我知道条目是使用自动增量 ID 正确保存的,但我仍然遇到 removewatchlist 函数的错误。例如:ID:11 / User:admin / watchlist_listingid : 1. 你知道属性 id 应该设置为 none,而这次似乎是 11? (每次我在监视列表中添加某些内容时,Django 自然会自动增加 watchlist.id,尽管我的模型中没有显示)
    • Swift 更新:我设法通过调整生成监视列表变量的方式使“删除监视列表”功能工作。 watchlist = Watchlist.objects.get(user=request.user.username, watchlist_listingid=id) watchlist.delete() return redirect('listing', id=id) 因为它现在可以工作,我不会为它实现一个表单。但我以后会考虑这个。再次感谢您的支持:)
    【解决方案2】:

    修复:“字段 'id' 需要一个数字,但得到 。”

    只需在 .first() 之后添加 .id

    watchlist = Watchlist(user=request.user.username,watchlist_listingid_id=Listing.objects.filter(id=id).first() = <Listing: "Gloss + Repair Spray">
    
    watchlist = Watchlist(user=request.user.username,watchlist_listingid_id=Listing.objects.filter(id=id).first().id) = 6 (which is the ID in my database)
    

    修复:无法删除监视列表对象,因为它的 id 属性设置为 None。

    watchlist = Watchlist.objects.get(user=request.user.username, watchlist_listingid=id)
    

    在使用 delete() 之前获取表中的整行并将其分配给变量

    而不是故障:

    watchlist = Watchlist(user=request.user.username,watchlist_listingid_id=id)
    

    最后,我发现要在同一个 HTML 页面中使用不同的 Django 表单,您可以将表单的操作设置为 URLS.PY 中的路径 url,然后在 Views.py 中触发特殊功能。

    【讨论】:

      猜你喜欢
      • 2023-03-11
      • 2020-03-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-27
      • 2020-11-29
      • 2023-03-23
      • 1970-01-01
      相关资源
      最近更新 更多