-1

I am trying to create a gallery.html which changes picture behaviour by image id e.g id="lightbox-1", id="lightbox-2", id="lightbox-3" and the gallery behaviour alters according to css, this works well using file paths as sample code highlight below:

 <div id="gallery">
      <div><img src="images/12.jpg"/><a href="#lightbox-1">512</a></div>
      <div><img src="images/13.jpg"/><a href="#lightbox-2">513</a></div>
      ...

and

<div class="lightbox" id="lightbox-1">
      <div class="content"><img src="images/12b.jpg"/>
        <div class="title">No. <b>512</b> from Picsum</div><a class="close" href="#gallery"></a>
      </div>
    </div>

However I am getting my images from sql-alchemy database and i am using this method to get images from DB, The images are getting pulled successfully but the arrangement is haphazard and i figured the id number is not increasing, hence I am trying to programatically increase the ID number. Here is my code:

<h5>Gallery</h5>
    {% set n = 1 %}
    {% for image in image_list %}
    <div id="gallery">
      <div>
        <img src="data:;base64,{{ image }}"/>
        <a href="#lightbox-{{n}}">{{n}}</a>
 
      </div>

    </div>

    <div class="lightbox" id="lightbox-{{n}}">
      <div class="content"><img src="data:;base64,{{ image }}"/>
      </div>
    </div>
    {% set n = n+1 %}
    {% endfor %}

In simple terms I want to mimic this behaviour and design using flask to serve the html and sql-alchemy for the images. At the moment, I can serve, and get the images, but cant get replicate the css behaviour Thanks in advance

1 Answers1

2

I think you're iterating in the wrong place and getting a different structure that doesn't match the style sheet.

To get the current index within the running iteration I recommend you to use loop.index. Creating and incrementing your own variable is therefore unnecessary.

Flask (./app.py)
from flask import (
    Flask, 
    current_app, 
    redirect, 
    render_template, 
    request, 
    url_for
)
from flask_sqlalchemy import SQLAlchemy
import base64

app = Flask(__name__)
app.config.from_mapping(
    SQLALCHEMY_DATABASE_URI='sqlite:///example.db',
    SQLALCHEMY_TRACK_MODIFICATIONS=False,
    UPLOAD_EXTENSIONS=('jpg',)
)
db = SQLAlchemy(app)

class GalleryImage(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    mime = db.Column(db.String, nullable=False)
    data = db.Column(db.LargeBinary(), nullable=False)

    @property
    def b64encoded(self):
        return base64.b64encode(self.data).decode('ascii')

with app.app_context():
    db.create_all()

@app.route('/')
def index():
    images = GalleryImage.query.all()
    return render_template('index.html', **locals())

def allowed_file(filename):
    allowed_extensions = current_app.config.get('UPLOAD_EXTENSIONS', [])
    return '.' in filename and \
        filename.rsplit('.', 1)[1].lower() in allowed_extensions

@app.route('/upload', methods=['GET', 'POST'])
def upload():
    if request.method == 'POST':
        files = request.files.getlist('file[]')
        for file in files: 
            if file.filename != '' and allowed_file(file.filename):
                image = GalleryImage(
                    mime=file.mimetype, 
                    data=file.read()
                )
                db.session.add(image)
        try:
            db.session.commit()
        except: 
            db.session.rollback()
        return redirect(url_for('.index'))
    return render_template('upload.html')
HTML Template (./templates/upload.html)
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Upload</title>
</head>
<body>
    <form method="post" enctype="multipart/form-data">
        <input type="file" name="file[]" accept="image/jpeg" multiple />
        <button type="submit">Upload</button>
    </form>
</body>
</html>
HTML Template (./templates/index.html)
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Index</title>
    <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/main.css') }}">
</head>
<body>

    <h5>Gallery</h5>
    
    <div id="gallery">
        {% for img in images -%}
        <div id="gallery-thumb-{{loop.index}}">
            <img src="data:;base64,{{ img.b64encoded }}" />
            <a href="#lightbox-{{ loop.index }}">{{ loop.index }}</a>
        </div>
        {% endfor -%}
    </div>

    {% for img in images -%}
    <div class="lightbox" id="lightbox-{{ loop.index }}">
        <div class="content">
            <img src="data:;base64,{{ img.b64encoded }}" />
            <a class="close" href="#gallery-thumb-{{loop.index}}"></a>
        </div>
    </div> 
    {% endfor -%}

</body>
</html>
CSS (./static/css/main.css)
* {
    box-sizing: border-box;
}
body {
    margin: 5px;
    position: relative;
}
#gallery {
    display: grid;
    /*  height: calc(100vh - 10px);*/ /* !!! */
    grid-template: repeat(6, 1fr) / repeat(6, 1fr);
    grid-gap: 0.5em;
}
@media (max-width: 800px) {
    #gallery {
        display: flex;
        align-items: flex-start;
        flex-wrap: wrap;
        justify-content: center;
    }
    #gallery > div {
        width: 48%;
        margin: 1%;
    }
}
@media (max-width: 800px) and (max-width: 350px) {
    #gallery > div {
        width: 98%;
    }
}
#gallery > div:nth-child(6n + 1) {
    grid-column: span 2;
    grid-row: span 2;
}
#gallery > div:nth-child(2) {
    grid-column: span 3;
    grid-row: span 3;
}
#gallery > div:nth-child(4) {
    grid-column: span 1;
    grid-row: span 2;
}
#gallery > div > a {
    opacity: 0;
    position: absolute;
    color: #000;
    background-color: #000;
    font: bold 4em "Helvetica";
    text-shadow: 0 -1px 5px #fff, -1px 0px 5px #fff, 0 1px 5px #fff, 1px 0px 5px #fff;
    padding: 2rem;
    mix-blend-mode: difference;
    width: 100%;
    height: 100%;
    transition: all ease 1s;
}
#gallery > div > img {
    width: 100%;
    min-height: 100%;
    transition: all ease 1s;
    object-fit: cover; /* !!! */
}
#gallery > div:hover img {
    filter: blur(4px);
}
#gallery > div:hover a {
    opacity: 1;
}
#gallery > div {
    overflow: hidden;
    position: relative;
    box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.2), 0 3px 20px 0 rgba(0, 0, 0, 0.19);
}
#gallery div, #gallery a {
    display: flex;
    justify-content: center;
    align-items: center;
    text-decoration: none;
}
[id^="lightbox-"] {
    position: fixed;
    top: 0;
    left: 0;
    width: 100vw;
    height: 100vh;
    background-color: rgba(0, 0, 0, 0.5);
    display: flex;
    opacity: 0;
    transition: opacity 450ms ease-in-out;
    align-items: center;
    justify-content: center;
    pointer-events: none;
}
[id^="lightbox-"]:target {
    opacity: 1;
    pointer-events: inherit;
}
[id^="lightbox-"]:target img {
    filter: blur(0);
}
[id^="lightbox-"] .content {
    max-width: 90%;
    position: relative;
    color: #fff;
}
[id^="lightbox-"] .content:hover > a.close {
    opacity: 1;
    transform: scale(1, 1);
}
[id^="lightbox-"] .content:hover > .title {
    opacity: 1;
    transform: translateY(-3px);
}
[id^="lightbox-"] .content:hover > .title::after {
    opacity: 1;
}
[id^="lightbox-"] .content > * {
    transition: all 450ms ease-in-out;
}
[id^="lightbox-"] .title {
    display: block;
    margin: 0;
    padding: 1em;
    position: absolute;
    bottom: 0;
    width: 100%;
    transform: translateY(50%);
    font-size: 1.5em;
    opacity: 0;
}
[id^="lightbox-"] .title::after {
    content: ' ';
    background-color: rgba(0, 0, 0, 0.4);
    bottom: 0;
    left: 0;
    height: 100%;
    width: 100%;
    position: absolute;
    transition: all 350ms ease-in-out 250ms;
    opacity: 0;
    transform-origin: bottom;
    mix-blend-mode: soft-light;
}
[id^="lightbox-"] img {
    max-height: 90vh;
    max-width: 100%;
    margin: 0;
    padding: 0;
    filter: blur(50px);
}
[id^="lightbox-"] a.close {
    width: 2em;
    height: 2em;
    position: absolute;
    right: 0;
    top: 0;
    background-color: rgba(0, 0, 0, 0.5);
    display: flex;
    align-items: center;
    justify-content: center;
    transform: scale(0, 0);
    opacity: 0;
    transform-origin: right top;
    text-decoration: none;
    color: #fff;
}
[id^="lightbox-"] a.close::after {
    content: "×";
}
Detlef
  • 6,137
  • 2
  • 6
  • 24
  • Thank you, with your code, I managed to get the images from DB to show. It was my first time reading on loop.index I was trying to pass a list and use a nested for loop but it was not coming out. – user15316630 Jan 19 '23 at 14:05
  • However, I do have another problem, kindly assist when you have a moment, the pop out function is only working for the first image. I tried checking the CSS handling that but to no avail `image_text` makes the image pop out, the other images are not responding to that – user15316630 Jan 19 '23 at 15:21
  • Unfortunately, I can't say why it isn't working properly for you. I tested it and it works for me. As long as the href attribute points to an identical id attribute of another element, the inpage anchor works. The rest is controlled by the style sheet rules, which start with `[id^="lightbox-"]` and should apply identically to all overlays. – Detlef Jan 19 '23 at 16:33
  • I am trying to read on `[id^="lightbox-"]` its new syntax as well but I think I am still failing to implement it. I am going to give it one more try, May you kindly share your code on github as well, please – user15316630 Jan 20 '23 at 07:52
  • 1
    I've added all the example code to the answer. That should work. The rule `[id^="lightbox-"]` applies to all elements whose id attribute starts with "lightbox-". – Detlef Jan 20 '23 at 17:06
  • Genius! The functionality is flawless. Thank you for sharing, you really got the ins and outs of the technology. Given I will be working a lot with websites and python, what resources would you suggest for me to get some level of appreciation of linking js, css and python – user15316630 Jan 21 '23 at 05:06
  • 1
    For Flask, I would recommend you to read [Miguel Grinberg's tutorial](https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-i-hello-world). For HTML, CSS and JS, I would recommend taking a look at the [Mozilla Developer Network](https://developer.mozilla.org/en-US/docs/Web) documentation. Under [CSS Tricks](https://css-tricks.com/guides/) you will find individual articles for layouts and specific problems. I don't know if that helps you, but I find it difficult to give a recommendation on these topics. – Detlef Jan 21 '23 at 12:43