Field 'id' expected a number but got 'bidding'

Viewed 118

I'm building a html page from a model. Added to the page is a a form that somehow seems to interfere with the model I'm using to build the page.

I get this error message: Exception Type: ValueError at /bidding Exception Value: Field 'id' expected a number but got 'bidding'.

HTML (listing.html)

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

    <h3>{{listing.0.listingName}}</h3>
    <p><img class="listingImage" src="{{listing.0.listingImage}}"></p>
    <p>{{listing.0.listingDesc}}</p>
    <h5>Price: $ TODO</h5>

    {% if user.is_authenticated %}
        <form action="{% url 'bidding' %}" method="POST">
            {% csrf_token %}    
            <input type="number" name="amount" placeholder="Bid">
            <input class="btn btn-primary" type="submit" value="Bid">
        </form>
    {% endif %}

    <h5>Details</h5>
    <ul>
        <li>Listed by: {{listing.0.listingPoster}}</li>
        <li>Category: {{listing.0.listingCategory}}</li>
    </ul>
    <p>Comments:</p>
    <p>TODO</p>

{% endblock %}

Views.py

def listing(request, listing):
    return render(request, "auctions/listing.html",{
        "listing" : Listing.objects.filter(id=listing)
    })

def bidding(request):
    if request.method == "POST":
        bidAmount = int(request.POST["amount"])
        ### logic comes here
        return HttpResponseRedirect(reverse("listing"), args=5 )
        ### args is hardcoded for testing only

models.py

class Listing(models.Model):
    CATEGORIES = (
        ('fashion','fashion'),
        ('electronics','electronics'),
        ('sports','sports'),
        ('home','home'),
        ('motors','motors'),
        ('art','art'),
        ('business','business'),
        ('media','media'),
        ('others','others')
    )

    listingPoster = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="Poster")
    listingName = models.CharField(max_length=128, verbose_name="Auction item")
    listingDesc = models.TextField(blank=True, verbose_name="Item description")
    listingActive = models.BooleanField(default=True, verbose_name="Active listing")
    listingImage = models.URLField(blank=True, verbose_name="Link to image")
    listingCategory = models.CharField(max_length=64, choices=CATEGORIES, default='others', verbose_name="Category")
    listingFirstBid = models.DecimalField(max_digits=8, decimal_places=2, default=1.0, verbose_name="Starting bid")
    listingCreated = models.DateTimeField(auto_now_add=True, null=True)
    watchers = models.ManyToManyField(User, blank=True, related_name="watchedListings", verbose_name="Watchers")
    ### requires the Pillow library to be installed

    def __str__(self):
        return f"{self.listingPoster} {self.listingName} {self.listingCategory} {self.listingActive}"

urls.py

urlpatterns = [
    path("<str:listing>", views.listing, name="listing"),
    path("bidding", views.bidding, name="bidding")
]
3 Answers

You should swap the patterns, such that if you access bidding/, then it first will look at the path for the bidding view, and only if the path does not match look further, so:

urlpatterns = [
    #       ↓ first bidding
    path('bidding/', views.bidding, name='bidding'),
    path('<str:listing>/', views.listing, name='listing'),
]

since your listing parameter is supposed to make use of primary keys that are integers, you can also specify the path to only accept a sequence of digits with the int path converter:

urlpatterns = [
    path('bidding/', views.bidding, name='bidding'),
    #       ↓ int path converter
    path('<int:listing>/', views.listing, name='listing'),
]
  def listing(request, listing):
  return render(request, "auctions/listing.html",{
       "listing" : Listing.objects.filter(id=listing)
  })

This view function takes a second parameter, listing which is delivered by the URL:

path("<str:listing>", views.listing, name="listing")

In this path you're declaring that the URL returns the variable listing which is declared as a string to your view function. Thus, when your filter function is run on your database, the parameter id is given a string represented by the variable listing

urlpatterns = [
  path("<str:listing>", views.listing, name="listing"),//This line is the culprit
  path("bidding", views.bidding, name="bidding")
]

Change the first path to look as follows, which shall coerce the input to an integer.

path("<int:listing>", views.listing, name="listing")
Related