Compoot
Compoot

Reputation: 2387

jinja2 nested for loops two lists

I have the following code that is not rendering the way I would like on the html front end.

{% for image in images %}
    {% for title in titles %}
        <div class="card" style="width: 18rem;">
          <img src="{{image}}" class="card-img-top" alt="...">
          <div class="card-body">
        
            <h5 class="card-title">{{title}}</h5>
            <p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
            <a href="#" class="btn btn-primary">Go somewhere</a>

            
</div>
    {% endfor %}
{% endfor %}

Essentially, images and titles are both lists of URLS. The images works correctly when on it's own to redner the HTML with all the images.

However, when I try to add titles, which is basically the image URL sliced to give only part of the URL text, in the above format, it messes up the formatting, displaying only one image.

I have tried various different ways but it doesn't work.

I want the code to display all the images in the cards, and in the TITLE field, display the corresponding title, and title in this case is the sliced string (or so I think)

The python (Flask) route, function that scrapes the images URLS and slicing code is shown here:

@app.route('/') #this is what we type into our browser to go to pages. we create these using routes
@app.route('/home')
def home():
    images=imagescrape()
    titles=(images[58:])
    #gettitles=gettitle()
    #titles=(gettitles[58:93])
    return render_template('home.html',images=images,titles=titles)


def imagescrape():
    result_images=[]
    #html = urlopen('https://en.wikipedia.org/wiki/Prince_Harry,_Duke_of_Sussex')
    html = urlopen('https://en.wikipedia.org/wiki/Rembrandt')
    bs = BeautifulSoup(html, 'html.parser')
    images = bs.find_all('img', {'src':re.compile('.jpg')})
    for image in images:
        result_images.append("https:"+image['src']+'\n') #concatenation!
    return result_images

UPDATE:

I've got this which NEARLY Works but not quite. It displays an image, then ALL the image urls text, instead of the corresponding one.

{% for image in images %}

        <div class="card" style="width: 18rem;">
          <img src="{{image}}" class="card-img-top" alt="...">
          <div class="card-body">
            {% for title in titles %}
            <h5 class="card-title">{{title}}</h5>
            {% endfor %}
            <p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
            <a href="#" class="btn btn-primary">Go somewhere</a>

            
          </div>
        </div>
{% endfor %}

I think only a small change is needed for it to work but I don't know what.

Upvotes: 1

Views: 1129

Answers (1)

Detlef
Detlef

Reputation: 8562

Combine the two lists of images and titles one by one.
For the iteration, unpack the two variables of the respective tuple.

import re                          # regular expressions used to match strings 
from bs4 import BeautifulSoup      # web scraping library
from urllib.request import urlopen # open a url connection 
from urllib.parse import unquote   # decode special url characters

@app.route('/')
@app.route('/home')
def home():
    images=imagescrape()
    # Iterate over all sources and extract the title from the URL
    titles=(titleextract(src) for src in images)
    
    # zip combines two lists into one.
    # It goes through all elements and takes one element from the first
    # and one element from the second list, combines them into a tuple 
    # and adds them to a sequence / generator.
    images_titles = zip(images, titles)
    return render_template('home.html', image_titles=images_titles)

def imagescrape():
    result_images=[]
    #html = urlopen('https://en.wikipedia.org/wiki/Prince_Harry,_Duke_of_Sussex')
    html = urlopen('https://en.wikipedia.org/wiki/Rembrandt')
    bs = BeautifulSoup(html, 'html.parser')
    images = bs.find_all('img', {'src':re.compile('.jpg')})
    for image in images:
        result_images.append("https:"+image['src']+'\n') #concatenation!
    return result_images

def titleextract(url):
    # Extract the part of the string between the last two "/" characters
    # Decode special URL characters and cut off the suffix
    # Replace all "_" with spaces
    return unquote(url[58:url.rindex("/", 58)-4]).replace('_', ' ')
{% for image, title in images_titles %}
    <div class="card" style="width: 18rem;">
      <img src="{{image}}" class="card-img-top" alt="...">
      <div class="card-body">
        <h5 class="card-title">{{title}}</h5>
        <p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
        <a href="#" class="btn btn-primary">Go somewhere</a>
      </div>
    </div>
{% endfor %}

Upvotes: 2

Related Questions