Search code examples
pythonflaskflask-wtformsflask-admin

How to upload multiple images in flask Admin


I am creating a blog website using flask and init admin want to add multiple photos for each blog, since its only admin who wants to upload photos therefore i used flask admin to create new blog and do all he stuff an admin wants but i am stuck in a issue on how can i add this feature of adding multiple photo for each blog i have gone through flask admin documentation but couldn't fine anything for adding multiple images but i did find about adding a single photo and an sample of my code is here

But again its not what admin wants

Kindly help me out here, Thanks code:

import os
import os.path as op

from flask import Flask, url_for
from flask_sqlalchemy import SQLAlchemy
from wtforms import fields

from sqlalchemy.event import listens_for
from jinja2 import Markup

from flask_admin import Admin, form
from flask_admin.form import rules
from flask_admin.contrib import sqla


# Create application
app = Flask(__name__, static_folder='files')

app.config['FLASK_ADMIN_SWATCH'] = 'cerulean'

# Create dummy secrey key so we can use sessions
app.config['SECRET_KEY'] = '123456790'

# Create in-memory database
app.config['DATABASE_FILE'] = 'sample_db.sqlite'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + app.config['DATABASE_FILE']
app.config['SQLALCHEMY_ECHO'] = True
db = SQLAlchemy(app)


# Create directory for file fields to use
file_path = op.join(op.dirname(__file__), 'static/images')
try:
    os.mkdir(file_path)
except OSError:
    pass


class Blog(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.Unicode(64))
    path = db.Column(db.Unicode(128))

    def __unicode__(self):
        return self.name


@listens_for(Blog, 'after_delete')
def del_image(mapper, connection, target):
    if target.path:
        # Delete image
        try:
            os.remove(op.join(file_path, target.path))
        except OSError:
            pass

        # Delete thumbnail
        try:
            os.remove(op.join(file_path,
                              form.thumbgen_filename(target.path)))
        except OSError:
            pass

class ImageView(sqla.ModelView):
    def _list_thumbnail(view, context, model, name):
        if not model.path:
            return ''

        return Markup('<img src="%s">' % url_for('static',
                                                 filename=form.thumbgen_filename(model.path)))

    column_formatters = {
        'path': _list_thumbnail
    }

    # Alternative way to contribute field is to override it completely.
    # In this case, Flask-Admin won't attempt to merge various parameters for the field.
    form_extra_fields = {
        'path': form.ImageUploadField('Image',
                                      base_path=file_path,
                                      thumbnail_size=(200, 300, True))
    }


# Flask views
@app.route('/')
def index():
    return '<a href="/admin/">Click me to get to Admin!</a>'

# Create admin
admin = Admin(app, 'Example: Forms', template_mode='bootstrap3')

# Add views
admin.add_view(ImageView(Blog, db.session))


def build_sample_db():
    """
    Populate a small db with some example entries.
    """

    db.drop_all()
    db.create_all()


    db.session.commit()
    return


if __name__ == '__main__':

    # Build a sample db on the fly, if one does not exist yet.
    app_dir = op.realpath(os.path.dirname(__file__))
    database_path = op.join(app_dir, app.config['DATABASE_FILE'])
    if not os.path.exists(database_path):
         build_sample_db()

    # Start app
    app.run(debug=True)

p.s its my first question in stack so sorry if any thing went wrong in question


Solution

  • YA i am answering my own because i found one

    ohk so here is a file feilds.py just add it in your folder and following is the process to use it

    #import class MultipleImageUploadField from feilds.py
    from fields import MultipleImageUploadField
    import ast
    # some more method i used 
    from secrets import token_hex
    import os.path as op
    
    # Create directory to save images
    file_path = op.join(op.dirname(__file__), '../static/images/products') # path
    try:
        os.mkdir(file_path)
    except OSError:
        pass
    
    # function to change name of each image
    def prefix_name(obj, file_data):
        _, ext = op.splitext(file_data.filename)
        return token_hex(10) + ext
    
    # function to delete image and thumbnail when deleting image in edit section
    @listens_for(Product, 'after_delete') # change product with db.Model in which you are saving file names
    def del_image(mapper, connection, target):
        if target.images:
            # Delete image
            try:
                print(file_path, target.images)
                os.remove(op.join(file_path, target.images))
            except OSError:
                pass
    
            # Delete thumbnail
            try:
                os.remove(op.join(file_path,
                                  form.thumbgen_filename(target.images)))
            except OSError:
                pass
    # add this inside your ModelView class
    def _list_thumbnail(view, context, model, name):
    
            if not model.images:
                return ''
    
            def gen_img(filename):
                return '<img src="{}">'.format(url_for('static', 
                       filename="images/products/" + form.thumbgen_filename(image)))
    
            return Markup("<br />".join([gen_img(image) for image in ast.literal_eval(model.images)]))
    
    column_formatters = {'images': _list_thumbnail} # My column name is images in this case
    
    form_extra_fields = {'images': MultipleImageUploadField("Images",
                                  base_path=file_path,
                                  url_relative_path="images/products/", # relative path of your image (you need to give this in order to see thumbnail in edit secction)
                                  thumbnail_size=(200, 200, True), # need to pass to make thumbnail
                                  namegen=prefix_name) # rename each image
    

    ya so thats it hope this was helpful to you