How to get highest bid in an auction bidding site Django

current_bid = Auction.objects.get('current_bid)

Possible?

I suggest you try that in the Django shell to see what you get.

Didn’t work, got

ValueError at /make_bid/2

too many values to unpack (expected 2)

What didn’t work? (You obviously jumped way ahead of where I was with my last response, so I don’t know what you just tried.)

Tried this

    def clean_new_bid(self):
        new_bid = self.cleaned_data['new_bid']
        current_bid = Auction.objects.get('current_bid')

Ok, I suggested you try out that query in the Django shell, not by updating your view with it.

Had you tried just that query in the shell, you would have seen that it would not retrieve the information that you need.

Back in your post at How to get highest bid in an auction bidding site Django - #19 by Theresa-o you show a query on the Auction model.

The query you need to write needs to get the proper instance of that model, and then access the current_bid field of that model.

If necessary, review the docs at:

Along with the examples at Writing your first Django app, part 3 | Django documentation | Django.

And if you’re not familiar with the Django shell, you should become familiar with it - see Writing your first Django app, part 2 | Django documentation | Django.

1 Like

current_bid = Auction.objects.get(current_bid=current_bid)

On my windows shell ‘NameError: name ‘current_bid’ is not defined’

I don’t know what to do at this point.

Tried .filter() but still same

In your view, you have:

So in this case, you’re retrieving a specific Auction object - the one for which you want to submit a bid - and likely the one from which you want to access the current bid.

Question 1: If you have an object of type Auction named auction, how do you access the current_bid value in that instance?

Now, you have access to the listing_id in the view, which doesn’t help you in the form. You still need to pass either the listing_id, or the Auction object itself, into the form.

You have a couple different ways of accomplishing that. My personal preference for that would be to pass it as a named parameter to the form.

In the view:

bid_form = BidForm(request.POST, auction=auction)

This passes a new named parameter into the form named auction.

So then you need to retrieve this parameter in the form. This can be done by adding an __init__ method to the form.
Example:

def __init__(self, *args, **kwargs):
    self.auction = kwargs.pop('auction', None)
    super().__init__(*args, **kwargs)

You then have a variable named auction (accessed as self.auction in that class) that accesses whatever value was passed into the form constructor.

1 Like

Hi Ken, thank you for the help so far.

I have been reading up on the __init__ method (this is my first time seeing it),

So, I searched online for how to access the starting_bid using ‘self.auction’ and I have so far tried this (referencing this python - get request data in Django form - Stack Overflow);

    def clean_new_bid(self):
        new_bid = self.cleaned_data['new_bid']
        starting_bid = self.auction.starting_bid

This throws the below error

AttributeError at /make_bid/2

‘NoneType’ object has no attribute ‘starting_bid’

I also saw a different post on how to reference an init variable Django ModelForms __init__ kwargs create and update - Stack Overflow and Pass data to django form's field clean method - Stack Overflow

    def clean_new_bid(self):
        new_bid = self.cleaned_data['new_bid']
        starting_bid = Auction.objects.get(starting_bid = self.auction)

This threw this error

TypeError at /make_bid/2

Field ‘starting_bid’ expected a number but got <Auction: Brain/Cloud>.

Brain/Cloud is the name/title of my post.

How do I reference self.auction in this scenario to get the starting_bid to use in the clean function to compare with new_bid?

I really did try to research in order to come up with a solution by myself but I’ve been stuck since and would appreciate your input. Thank you.

I need to see your current view and form for this. I know there’s a lot of work that has been done, and I can’t piece together what the current code looks like from the various responses here.

These two parts (view and form) work together. The view needs to pass either the auction object, or the id for the auction object to the form. What the view passes to the form will determine what the form needs to do with it.

The NoneType error is an indication that you are not passing (or retrieving, depending) the auction object to the form from the view.

The TypeError is telling you that you’re trying to assign the complete Auction object to starting_bid, not the numeric field. (Also, I have a feeling that your query is wrong - you want to do a get based on the key, not the value of the bid. It appears to me that you’re mixing up the process of retrieving an object and getting the values from that object.)

1 Like

This is the what I have so far

VIEWS.PY

def make_bid(request, listing_id):
    auction = Auction.objects.get(pk=listing_id)
    bid_item = Bids.objects.filter(auction=auction).order_by('-new_bid').first()
    

#if theres no bid available, use starting bid, if available, update to the latest bid

    if bid_item == None:
        bid = auction.starting_bid
    else:
        bid = bid_item.new_bid

    if request.method == 'POST':
        bid_form = BidForm(request.POST, auction=auction)
        if bid_form.is_valid():
            user = request.user 
            bid_count = Bids.objects.filter(auction=listing_id).count()
            new_bid = request.POST['new_bid']
            Bids.objects.create(auction=auction, user = user, new_bid = new_bid)
            messages.success(request, 'Successfully added your bid')
            return HttpResponseRedirect(reverse("listing_detail", listing_id=listing_id))


        else:
            bid_form = BidForm(request.POST)
            context = {
                "bid_form": bid_form,
                "bid": bid,
                "bid_item": bid_item,
                "auction": auction
                } 

        return render(request, 'auctions/details.html', context)
             
                
    return render(request, 'auctions/details.html', bid_form = BidForm())  

FORMS.PY

class AuctionForm(forms.ModelForm):
    
    class Meta:
        model = Auction
        fields = ['title', 'description', 'starting_bid']

        widgets = {
            'title': forms.TextInput(attrs={'class': 'form-control'}),
            'description': forms.Textarea(attrs={'class': 'form-control'}),
            'starting_bid': forms.NumberInput(attrs={'class': 'form-control'}),
        }

class BidForm(forms.ModelForm):

    class Meta:
        model = Bids
        fields = ['new_bid']
        labels = {
            'new_bid': ('Bid'),
        }

    def __init__(self, *args, **kwargs):
        self.auction = kwargs.pop('auction', None)
        super().__init__(*args, **kwargs)


    def clean_new_bid(self):
        new_bid = self.cleaned_data['new_bid']
        starting_bid = Auction.objects.get(starting_bid = self.auction)   

        if new_bid <= starting_bid:
            raise ValidationError("New bid must be greater than the previous bid")
                
        return new_bid

From this line in the view:

you’re passing an actual Auction object into the form.

If I have an object of type Auction named auction, how do I access the starting_bid field of auction?

Once you understand that, then you can change:

to the appropriate syntax.

(If necessary, review page 2 of the tutorial, including the section at Playing with the API.

Also, from your view:

The three lines marked with *** are unnecessary and can be removed.

The general flow is:

if form.is_valid():
    new_object = form.save(commit=False)
    new_object.field = "some updated value"
    new_object.save()

Also see Creating forms from models | Django documentation | Django

1 Like

Reviewing this line in the page

# Access model field values via Python attributes.
>>> q.question_text
"What's new?"

I adjusted my code to

    def clean_new_bid(self):
        new_bid = self.cleaned_data['new_bid']
        starting_bid = self.auction.starting_bid 

Just want to explain my logic, using the first statement about accessing auction through ‘self.auction’ and this line ‘>>> q.question_text’ which accesses the field ‘question_text’ through the variable ‘q’.

Unfortunately, still missing it because I get the Nonetype error still.

Thank you, this has been updated

        if bid_form.is_valid():
            accepted_bid = bid_form.save(commit=False)
            accepted_bid.new_bid = bid_form.cleaned_data['new_bid']
            accepted_bid.user = request.user 
            accepted_bid.save()

Ok, I’m missing something here regarding your auction object. I see no reason at the moment how/why you would be getting a NoneType error with it.

Can you post the complete error?

Regarding your new view:

The marked line is unnecessary - that’s the data that is coming in from the form. You don’t need to do anything with it. The reason you have the accepted_bid.user line following it is because user is not a field in the form.

AttributeError at /make_bid/2

‘NoneType’ object has no attribute ‘starting_bid’

Request Method: POST
Request URL: http://127.0.0.1:8000/make_bid/2
Django Version: 4.0.2
Exception Type: AttributeError
Exception Value: ‘NoneType’ object has no attribute ‘starting_bid’

Traceback Switch to copy-and-paste view

  • C:\Users\USER\AppData\Local\Programs\Python\Python310\lib\site-packages\django\core\handlers\exception.py , line 47, in inner

    1. response = get_response(request)

…

Local vars

  • C:\Users\USER\AppData\Local\Programs\Python\Python310\lib\site-packages\django\core\handlers\base.py , line 181, in _get_response

    1. response = wrapped_callback(request, *callback_args, **callback_kwargs)

…

Local vars

  • C:\Users\USER\AppData\Local\Programs\Python\Python310\lib\site-packages\django\contrib\auth\decorators.py , line 21, in _wrapped_view

    1. return view_func(request, *args, **kwargs)

…

Local vars

  • C:\Users\USER\Downloads\commerce\commerce\auctions\views.py , line 146, in make_bid

    1. return render(request, ‘auctions/details.html’, context)

O, that makes sense. Thanks

It’s generally more helpful to get the error message from the console in which this is being run than what’s displayed on the web page, but we can try to work with this.

I’m guessing from context that line 146 from the referenced error message is the marked line here:

The situation causing that issue is here:

You should not be recreating the form again. You’re creating your instance of the form before the is_valid call, which makes it available on both sides of the condition. (The actual error is that, in recreating it here, you’re not passing the auction variable to the constructor. But you shouldn’t be recreating it at all.)

1 Like

I have one final problem bugging me, the bid price is not updating on the page when the user types it in

                            <hr>
                            <p>Current price: ${{ listing.bid }}</p>
                            <hr>

Ok

Context please? I don’t see that in any of the previous templates you’ve posted nor a reference to it in the view you’ve been working on.

The new bid amount that the user inputs should show on the homepage, so I’m trying to pass in the bid amount into my index.html file

INDEX.HTML

                  <h5>{{ listing.title|truncatewords:3 }}</h5>
                  <h6>${{ listing.bid }}</h6>
                  <small>{{ listing.description|truncatewords:6 }}</small>

bid is gotten from this VIEW

I need to see the view that is rendering the index.html template.