6

I am building a python web app hosted on pythonanywhere following this tutorial loosely. I am modifying the resulting application to fit my own goal.

Here is my python code that I am using to pass variables to a HTML document in order for them to be added to a table using a for loop:

from flask import Flask, redirect, render_template, request, url_for

app = Flask(__name__)
app.config["DEBUG"] = True

productnames = []
reviews = []

@app.route("/", methods=["GET", "POST"])
def index():
if request.method == "GET":
    return render_template("main.html", reviews=reviews, productnames=productnames)

reviews.append(request.form["review"])
productnames.append(request.form["products"])
return redirect(url_for('index'))

Using the following code within my HTML, I am looping through that list and adding each item to the table:

{% for review in reviews %}
    <tr>
        <td></td>
        <td>{{ review }}</td>
        <td></td>
    </tr>
{% endfor %}

And this works, however, I am trying to iterate through multiple lists and found various statements saying that the zip function was what I was looking for so I changed my HTML code to the following segment and it no longer works.

{% for review, product in zip(reviews, productname) %}
    <tr>
        <td>{{ product }}</td>
        <td>{{ review }}</td>
    <td></td>
</tr>
{% endfor %}

From python anywhere, the error page says "Error code: Unhandled Exception", and the error log through the pythonanywhere dashboard says:

2018-04-24 12:57:23,957:   File "/home/FdScGroup/cloudapp/templates/main.html", line 43, in top-level template code
2018-04-24 12:57:23,957:     {% for review, product in zip(reviews, productnames) %}

How do I get this to work?

Any help appreciated, thank you.

Ronan
  • 77
  • 1
  • 8
  • Is there one review per product? – bphi Apr 24 '18 at 13:03
  • One recommendation I would make is to assign `zip(reviews, productname)` to a template variable (say `product_reviews`) and iterate on that rather than having this logic in the template. I'm not sure about Flask templates but the Django templating language is designed to minimise this kind of logic within the template. – benwad Apr 24 '18 at 13:07
  • @bphi Yeah, one review per line of table, along with the name of the product. If there are multiple reviews for a single product they will still be on different rows, just with the same product name in the column opposite. – Ronan Apr 24 '18 at 13:07
  • I agree with @benwad and would suggest you to use dictionary and then you can do `{% for key, value in product_reviews.iteritems() %}` this way you can have multiple reviews in value i.e value can be a list – moghya Apr 24 '18 at 13:09
  • @benwad I'm not sure I am clear on that, could you provide an explanation? I am new to Python/Flask. – Ronan Apr 24 '18 at 13:10
  • @Ronan in your call to `render_template` you're defining some template variables (`reviews=reviews, productnames=productnames`), and then using those in the template. A better practice would be 'preparing' the data as you would like it to be displayed on the page. So if you set a template variable like `product_reviews=zip(reviews, productnames)` then you wouldn't have to use `zip` in the template. It's a small thing in this case but is the widely accepted practice in template rendering (the benefits are bigger with more complex templates/applications). – benwad Apr 24 '18 at 13:19
  • @benwad Thank for the explanation, appreciated! – Ronan Apr 24 '18 at 13:20

1 Answers1

9

zip() is a python function, not a function to be executed in the template language of Flask (Jinja2).

So apply zip() in the view and pass the result to the template:

return render_template("main.html", reviews_products=zip(reviews, productnames))

Then apply this trick: how to iterate over a list of list in jinja in the template.

Davy
  • 1,720
  • 1
  • 19
  • 42
  • Much appreciated, I'll give it a test and get back to marking this as correct if all goes well! – Ronan Apr 24 '18 at 13:16