sqlite 在与表单相同的HTML页上返回数据时出现“业务不可迭代”错误

rryofs0p  于 2022-11-14  发布在  SQLite
关注(0)|答案(1)|浏览(144)

我可以从表单获取数据到数据库,并将其传递给视图,在那里它查询Yelp并将其放入JSON文件中,然后将JSON文件中的特定字段保存到数据库中。但我无法显示数据库数据。
当我应该将数据返回到页面时,我收到了搜索成功消息。我发现我的字典只是一条数据,而不是一行的所有字段,我不确定为什么:

{% for key, values in yelp_data.items %}
<tr>
    <td>{{key}}</td>
    {% for v in values %}
    <td>{{v}}</td>
    {% endfor %}
</tr>
    {% endfor %}

我得到“业务是不可迭代的”错误。
Index.html

...
<div class="row">
    <div class="col-md-4">
        <h2>Getting started</h2>
        <p>
            Click here to begin with Yelp!
        </p>
        <p><a class="btn btn-default" href="yelping">Yelp! &raquo;</a></p>
    </div>
...

Views.py

...
@csrf_exempt
def yelping(request):
    if request.method == "POST":
        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 render(request, 'app/yelp.html', {'form' : form})
        else:
            form = YelpForm()
        messages.error(request, "Unsuccessful Search. Invalid information.")
        form = YelpForm()
        
    assert isinstance(request, HttpRequest)
    return render(request, 'app/yelp.html')
...

Yelp.py

# -*- coding: utf-8 -*-

from __future__ import print_function

import json
import requests
import sys
import sqlite3
from urllib.parse import quote
from django.views.decorators.csrf import csrf_exempt
from django.shortcuts import render, redirect
from app.models import Business
from django.shortcuts import get_object_or_404

API_KEY = '*****************************************************************************'

# API constants, you shouldn't have to change these.
API_HOST = 'https://api.yelp.com'
SEARCH_PATH = '/v3/businesses/search'
BUSINESS_PATH = '/v3/businesses/'  # Business ID will come after slash.

# Defaults
DEFAULT_TERM = 'dinner'
DEFAULT_LOCATION = 'Glassboro, NJ'
SEARCH_LIMIT = 3
OFFSET = 0

def request(host, path, api_key, url_params=None):
    url_params = url_params or {}
    url = '{0}{1}'.format(host, quote(path.encode('utf8')))
    headers = {
        'Authorization': 'Bearer %s' % api_key,
    }

    print(u'Querying {0} ...'.format(url))

    response = requests.request('GET', url, headers=headers, params=url_params)

    return response.json()

def search(api_key, term, location):
    url_params = {
        'term': term.replace(' ', '+'),
        'location': location.replace(' ', '+'),
        'limit': SEARCH_LIMIT,
        'offset': OFFSET
    }
    return request(API_HOST, SEARCH_PATH, api_key, url_params=url_params)

def get_business(api_key, business_id):
    business_path = BUSINESS_PATH + business_id

    return request(API_HOST, business_path, api_key)

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)
    
    yelp_data = Business.objects.filter(business_id=business_id).order_by('-id').first()
    dic = {
        'yelp_data': yelp_data,
        }
    print(dic)
    return render(request, 'app/yelp.html', dic)

def db():
    
    with open('app/API/YelpAPI/yelp.json', 'r') as f:
        data = json.load(f)

    conn = sqlite3.connect('capstone/db.sqlite3')
    cur = conn.cursor()
        # Create the table if it doesn't exist.
    cur.execute(
        """CREATE TABLE IF NOT EXISTS Business(
                id INTEGER PRIMARY KEY,
                business_id varchar(100),
                business_name varchar(100),
                yelp_business_id varchar(254),
                phone varchar(15),
                city varchar(100),
                state varchar(20),
                address varchar(100),
                postal_code varchar(15),
                latitude float(100),
                longitude float(100),
                business_stars float(10),
                business_review_count integer(10),
                is_open integer(1)
            );"""
    )
    
    business_id = data[0]['id']
    business_name = data[0]['name']
    yelp_business_id = data[0]['alias']
    phone = data[0]['phone']
    city = data[0]['location']['city']
    state = data[0]['location']['state']
    address = data[0]['location']['address1']
    postal_code = data[0]['location']['zip_code']
    latitude = data[0]['coordinates']['latitude']
    longitude = data[0]['coordinates']['longitude']
    business_stars = data[0]['rating']
    business_review_count = data[0]['review_count']
    is_open = data[0]['hours'][0]['is_open_now']
        
    # Execute the command and replace '?' with the each value
    # in 'values'. DO NOT build a string and replace manually.
    # the sqlite3 library will handle non safe strings by doing this.
    sql = '''INSERT INTO Business (business_id, business_name, yelp_business_id, phone, city,  state, address, postal_code, latitude, longitude, business_stars, business_review_count, is_open) VALUES(
                ?,
                ?,
                ?,
                ?,
                ?,
                ?,
                ?,
                ?,
                ?,
                ?,
                ?,
                ?,
                ?
            );'''
    cur.execute(sql, (business_id, business_name, yelp_business_id, phone, city, state, address, postal_code, latitude, longitude, business_stars, business_review_count, is_open, ))

    conn.commit()
    conn.close()

@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 render(request, 'app/yelp.html')

Yelp.html

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

{% block content %}

<h2>{{ title }}</h2>
<form name='YelpForm' action="" class="post-form" method="post">
    {% csrf_token %}
    <label for="term">Term: </label>
    <input id="term" type="text" name="term" placeholder="Type..." value="{{ YelpForm.term }}">
    <label for="location">Location: </label>
    <input id="location" type="text" name="location" placeholder="Where..." value="{{ YelpForm.location }}">
    <input type="submit" value="OK">
</form>
<h1>{{ yelp_data.business_name }}</h1>
{% endblock %}

App/urls.py

from django.urls import re_path, path
from app import views
from .API.YelpAPI.yelp import yelp_main, query_api

# SET THE NAMESPACE!
app_name = 'app'
# Be careful setting the name to just /login use userlogin instead!
urlpatterns=[
    path('yelp_main/', yelp_main, name='yelp_main' ),
    path('query_api/', query_api, name='query_api'),
    path('registration/', views.register_request, name='registration'),
    re_path(r'^user_login/$', views.user_login,name='user_login'),
]

Capstone/urls.py

from datetime import datetime
from django.urls import re_path, path, include
from django.contrib import admin
from django.contrib.auth.views import LoginView, LogoutView
from app import forms, views

urlpatterns = [
    path('home/', views.home, name='home'),
    path('contact/', views.contact, name='contact'),
    path('about/', views.about, name='about'),
    path('admin/', admin.site.urls),
    re_path(r'^$',views.index,name='index'),
    re_path(r'^special/',views.special,name='special'),
    re_path('', include('app.urls', namespace='app')),
    re_path(r'^logout/$', views.user_logout, name='logout'),
    path('foodie/', views.foodie, name='foodie'),
    path('yelping/', views.yelping, name='yelping'),
]

Models.py

...
class Business(models.Model):
    business_id = models.CharField(max_length=100)
    business_name = models.CharField(max_length=100)
    yelp_business_id = models.CharField(max_length=45, blank=True, null=True)
    phone = models.CharField(max_length=100)
    city = models.CharField(max_length=100)
    state = models.CharField(max_length=100)
    address = models.CharField(max_length=100, blank=True, null=True)
    postal_code = models.CharField(max_length=15, blank=True, null=True)
    latitude = models.DecimalField(max_digits=100, decimal_places=2, blank=True, null=True)
    longitude = models.DecimalField(max_digits=100, decimal_places=2, blank=True, null=True)
    business_stars = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=False)
    business_review_count = models.IntegerField(blank=True, null=True)
    is_open = models.IntegerField(blank=True, null=True)

    class Meta:
        managed = True
        db_table = 'business'
        ordering = ['business_name', 'business_stars', 'is_open']
        verbose_name = 'Business Information'
        verbose_name_plural = "Business's Information"

    def __str__(self):
        return self.business_name
...

class YelpInputModel(models.Model):
    term = models.CharField(blank=True, null=False, max_length=100)
    location = models.CharField(blank=True, null=False, max_length=100)

    class Meta:
        managed = True
        db_table = 'yelpinputmodel'
...

Python.exe

Performing system checks...

System check identified no issues (0 silenced).
November 11, 2022 - 10:20:42
Django version 4.1.3, using settings 'capstone.settings.development'
Starting development server at http://127.0.0.1:61098/
Quit the server with CTRL-BREAK.
[11/Nov/2022 10:20:43] "GET / HTTP/1.1" 200 2624
[11/Nov/2022 10:20:44] "GET /static/app/content/site.css HTTP/1.1" 200 723
[11/Nov/2022 10:20:44] "GET /static/app/content/bootstrap.min.css HTTP/1.1" 200 97968
[11/Nov/2022 10:20:44] "GET /static/app/scripts/modernizr-2.6.2.js HTTP/1.1" 200 52810
[11/Nov/2022 10:20:44] "GET /favicon.ico HTTP/1.1" 404 3796
[11/Nov/2022 10:20:50] "GET /yelping HTTP/1.1" 301 0
[11/Nov/2022 10:20:50] "GET /yelping/ HTTP/1.1" 200 2035
yelping dinner 08501
yelp_api dinner 08501
query_api dinner 08501
Querying https://api.yelp.com/v3/businesses/search ...
Querying https://api.yelp.com/v3/businesses/a0FEhgrOIk_M6_o0ie-NpQ ...
{'yelp_data': <Business: The Roost Restaurant>}
[11/Nov/2022 10:20:54] "POST /yelping/ HTTP/1.1" 200 2095
8ftvxx2r

8ftvxx2r1#

问题

我认为底线是,您认为页面yelp.html被覆盖的第一直觉是正确的。您的yelping返回render(request, 'app/yelp.html'),其中没有数据,因为没有为它提供任何上下文。现在,该view函数首先调用yelp_main(request),它也返回render(request, 'app/yelp.html'),也就是没有数据的页面。但它确实调用了def query_api(request, term, location):。这确实返回页面数据,因为它有一个上下文,dicrender(request, 'app/yelp.html', dic)。但是query_api返回到yelp_main,它用页面的无数据版本覆盖它,然后原始调用函数用页面的另一个无数据版本替换它。

解决方案

您是否可以只返回一个空的HttpResponse,然后只在最后发送字典,而不是使用Render返回页面:

# 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)

# 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)

编辑

为什么词典只打印一条数据?
我可能误解了你所说的一项数据的意思。
Business.objects.filter(business_id=business_id).order_by('-id').first()将返回BusinessObject,其中包括该类的所有字段,但如果print它将只显示business_name。为什么?因为这是您在定义时要求Django打印的

def __str__(self):
    return self.business_name

如果您想要打印出所有行,那么删除.first(),因为这将返回OneBusiness Object,这显然不是您可以遍历的东西,这会给您带来“Business is Not Iterable”。然后,在您的HTML中,您应该能够执行以下操作:

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

相关问题