[Answered ]-"Business is not iterable" error returning data on same HTML page as form

1👍

Problem

I think the bottom line is that your first instinct that the page yelp.html was being overwritten was correct. Your yelping returns render(request, 'app/yelp.html'), which has no data in it, because no context has been given to it. Now this view function first calls yelp_main(request), which also returns render(request, 'app/yelp.html'), the page with no data again. But it does call def query_api(request, term, location):. This does return data with the page since it has a context, dic with it, render(request, 'app/yelp.html', dic). But query_api returns to yelp_main which overwrites it with the no data version of the page, and then the original calling function replaces that with another no data version of the page.

Solution

Instead of returning pages with render, could you just return an empty HttpResponse, and then only at the end send the dictionary:

# views.py

from django.http import HttpResponse

@csrf_exempt
def yelping(request):

    form = YelpForm(request.POST or None)
    
    if form.is_valid():
        form.save(commit=False)
        term = request.POST['term']
        location = request.POST['location']
        form.save()
        print("yelping", term, location)
        yelp_main(request)
        messages.success(request, "Search successful." )
        return redirect('app:yelping')
        #return render(request, 'app/yelp.html', {'form' : form})
        
    messages.error(request, "Unsuccessful Search. Invalid information.")
        
    assert isinstance(request, HttpRequest)

    # NOTE: I removed the .first() from the following line
    yelp_data = Business.objects.filter(business_id=business_id).order_by('-id')

    dic = {
        'yelp_data': yelp_data,
        }
    print(dic)
    
    return render(request, 'app/yelp.html', dic)

and

# yelp.py

from django.http import HttpResponse

@csrf_exempt
def yelp_main(request):
    #request.session._get_or_create_session_key()
    term = request.POST.get('term')
    location = request.POST.get('location')
    db()
    print("yelp_api", term, location)
    query_api(request, term, location)
    return HttpResponse("OK")
    #return render(request, 'app/yelp.html')


def query_api(request, term, location):
    
    print("query_api", term, location)
    response = search(API_KEY, term, location)

    businesses = response.get('businesses')

    if not businesses:
        print(u'No businesses for {0} in {1} found.'.format(term, location))
        return

    business_id = businesses[0]['id']

    response = get_business(API_KEY, business_id)

    write_to_file = json.dumps([response], indent=4)

    with open('app/API/YelpAPI/yelp.json', 'w') as f:
        f.write(write_to_file)
    
    return HttpResponse("OK")
    #return render(request, 'app/yelp.html', dic)

Edit

Why the dict is only printing one piece of data?
I may have misunderstood by what you meant by one piece of data.
Business.objects.filter(business_id=business_id).order_by('-id').first() will return the Business object, which includes all the fields of that class, but if you print it it will only show the business_name. Why? Because that’s what you asked Django to print when you defined

def __str__(self):
    return self.business_name

If, as I think you want to print out all the rows, then remove the .first() since that will return one Business object, which is obviously not something you can iterate through, giving you "Business is not iterable". Then in your HTML you should be able to do:

{% for y in yelp_data %}
    <td>{{ y.business_name }}</td>
    <td>{{ y.phone }}</td>
    <td>{{ y.city }}</td>
    ...
{% endfor %}

Leave a comment