1
\$\begingroup\$

I’m working with the Google Books API and I’m not so sure about the way I’m going to get the search data and return it to the user. I will describe the path here and I would like to know if you would do something different.

First, I receive a search request from the user via a form inside index.html:

<form action='/lookup' method="post">
  <input type="text" name="search" placeholder="Search by title, author, publisher, release, ISBN ...">
  <button type="submit">Buscar</button>
  {% block search %}{% endblock %}
</form>

So in an app.py I get this request like this:

@app.route("/lookup", methods=["GET", "POST"])
@login_required
def search():
    """Get book search."""
    if request.method == "POST":
        result_check = is_provided("search")
        if result_check is not None:
            return result_check
        search = request.form.get("search").upper()
        search = lookup(search)
        if search is None:
            return "Invalid Search"
        return render_template("search.html", search={
            'totalItems': search['totalItems'],
            'title': search['title'],
            'authors': search['authors']
        })
    else:
        return render_template("/")

Next, I access a lookup function in helpers.py:

def lookup(search):
    """Look up search for books."""
    # Contact API
    try:
        url = f'https://www.googleapis.com/books/v1/volumes?q={search}&key=MYAPIKEY'
        response = requests.get(url)
        response.raise_for_status()
    except requests.RequestException:
        return None

    # Parse response
    try:
        search = response.json()
        return {
            "totalItems": int(search["totalItems"]),
            "items": search["items"]
        }
    except (KeyError, TypeError, ValueError):
        return None

To return the results to the user I have a search.html that is still under construction:

{% extends "index.html" %}

{% block search %}

<p>Total Items: {{search['totalItems']}}</p>

<table>
  <tr>
    <td>
      <img src="{{search['items'][0]['volumeInfo']['imageLinks']['thumbnail']}}">
      <p>Título: {{search['items'][0]['volumeInfo']['title']}}</p>
      <p>Autor(a): {{search['items'][0]['volumeInfo']['authors']}}</p>
    </td>
    <td>
      <img src="{{search['items'][2]['volumeInfo']['imageLinks']['thumbnail']}}">
      <p>Título: {{search['items'][2]['volumeInfo']['title']}}</p>
      <p>Autor(a): {{search['items'][2]['volumeInfo']['authors']}}</p>
    </td>
  </tr>
</table>

{% endblock %}

Finally, my question: is this path I am following to access the API and return the data to the user the most recommended? Would you do anything other than that?

\$\endgroup\$
2
  • 1
    \$\begingroup\$ Could you clarify what Python framework you are using? \$\endgroup\$ May 27, 2021 at 5:49
  • 1
    \$\begingroup\$ Hi, It is pure Python. I am only using the package Flask. \$\endgroup\$
    – ARNON
    May 27, 2021 at 12:02

1 Answer 1

3
\$\begingroup\$

Split search() up into two functions, each accepting only one method, and dispose of your if.

There's a little bit of religion around this, but HTML5 accepts both "naive HTML" and XHTML-compatible tag closure, so this:

<input type="text" name="search">

is also accepted as

<input type="text" name="search" />

and the latter is (1) more explicit on where the tag ends and (2) is more broadly compatible.

This:

request.form.get("search").upper()

does not benefit from get, because you don't provide a default. Either allow the crash on a missing parameter, i.e. request.form['search'], or provide a default to get, likely the empty string.

This:

search = lookup(search)

is ill-advised, because it swaps types on the same symbol. The input is a string and the output is a dictionary.

This:

url = f'https://www.googleapis.com/books/v1/volumes?q={search}&key=MYAPIKEY'
    

first should not hard-code your API key directly into the string, and second should not format any query parameters into the string. Instead, pass params={...} into get().

This:

except requests.RequestException:
    return None

is fairly catastrophic for the application's ability to tell maintainers and users what went wrong. You're going to want to log the exception, and translate it into an appropriate failure message returned to your own caller.

This:

requests.get(url)

should use the response in a with.

Your search.html template is deeply bizarre. Where I would expect to see a for, you've only shown the first and third items?

Ensure that you're telling the browser that your content is in Spanish.

This:

action='/lookup'

needs to use double quotes.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Thanks @Reinderien! You helped me a lot!! As I told, the search.html is under construction. I really appreciate your help! \$\endgroup\$
    – ARNON
    May 26, 2021 at 23:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.