pythondatabaseflaskflask-uploads

How to upload image files from a form to a database in Python framework Flask


I am trying to upload an image from a form to a database in Flask in a practice e-commerce website I am working on. I am using flask-uploads to handle the file uploads since it seems easier than other ways of uploading files. I am getting the following error "NameError: name 'images' is not defined". This error seems to be occurring within the routes.py file which handles the functions and routing of the website. I will list the main parts of the code below. The add_item function within the routes.py file takes what has been submitted by the form and sends that data to the database. As I said, this is where the error is occurring since images cannot be recognized but I can't figure out how to solve the issue. Init.py is a package that holds where the flask-uploads connection is made. Config.py is used to define the configurations for the application. The addItem function within the forms.py file is used to layout the structure of the form. An HTML file which is not included in this document because I don't think it is linked to the issue uses the form structure in forms.py to display the form to the screen.

I have tried looking up the solution online but there aren't very detailed solutions to this issue and the documentation of Python Flask is already limited enough but Flask-Upload does not have very good documentation especially not in regards to being used with databases. I am new to Python Flask so if anyone could help me with this issue that would be greatly appreciated.

routes.py:

    #addItem page
    @app.route('/add_Item',methods=["GET","POST"])
    add_Item():
             form = addItem()
             if form.validate_on_submit():
                 filename = images.save(request.files['image'])
                 url = images.url(filename)
                 item = Item(title=form.name.data,price=form.price.data,description=form.description.data,stock=form.stock.data,vendorid=current_user.id,image=url)
                 db.session.add(item)
                 db.session.commit()
                 flash("Congratulations, your item has been added")
                 return redirect(url_for('vendor',username=current_user.username))
              else:
                 return render_template('addItem.html', title="Add Item", form=form)

init.py:

    from flask import Flask
    from config import Config
    from flask_sqlalchemy import SQLAlchemy
    from flask_migrate import Migrate
    from flask_login import LoginManager
    from flask_uploads import UploadSet, IMAGES, configure_uploads

    app = Flask(__name__)
    app.config.from_object(Config)
    db = SQLAlchemy(app)
    migrate = Migrate(app, db)
    login = LoginManager(app)

    # Configure the image uploading via Flask-Uploads
    images = UploadSet('images', IMAGES)
    configure_uploads(app, images)

    from app import routes, models, errors

config.py

    import os
    basedir = os.path.abspath(os.path.dirname(__file__))
    TOP_LEVEL_DIR = os.path.abspath(os.curdir)

   class Config(object):
      SECRET_KEY = os.environ.get('SECRET_KEY') or 'abcdef'
      SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
         'sqlite:///' + os.path.join(basedir, 'app.db')
      SQLALCHEMY_TRACK_MODIFICATIONS = False
      UPLOADS_DEFAULT_DEST = TOP_LEVEL_DIR + '/app/static/img/'
      UPLOADS_DEFAULT_URL = 'http://localhost:5000/static/img/'
      UPLOADED_IMAGES_DEST = TOP_LEVEL_DIR + '/app/static/img/'
      UPLOADED_IMAGES_URL = 'http://localhost:5000/static/img/'

forms.py

      class addItem(FlaskForm):
        name = StringField('Name',validators=[DataRequired()])
        price = IntegerField('Price',validators=[DataRequired()])
        description = StringField('Description',validators=[DataRequired()])
        stock = IntegerField('Stock',validators=[DataRequired()])
        image = FileField('Image', validators=[FileRequired(), FileAllowed(images, 'Images only!')])
        submit = SubmitField('Submit',validators=[DataRequired()])

Solution

  • Try this

             f = request.files['image']
             f.save(secure_filename(f.filename))
             url = f.filename
    

    Here you can see the i used secured_filename() against your file name. this is to prevent directory tranversal attack.

    url is the filename and image will be saved on the same directory as your application code.

    Finally your code will look like below

    #addItem page
        @app.route('/add_Item',methods=["GET","POST"])
        add_Item():
                 form = addItem()
                 if form.validate_on_submit():
    
                     f = request.files['image']
                     f.save(secure_filename(f.filename))
                     url = f.filename
                     item = Item(title=form.name.data,price=form.price.data,description=form.description.data,stock=form.stock.data,vendorid=current_user.id,image=url)
                     db.session.add(item)
                     db.session.commit()
                     flash("Congratulations, your item has been added")
                     return redirect(url_for('vendor',username=current_user.username))
                  else:
                     return render_template('addItem.html', title="Add Item", form=form)
    

    For more advance security on files upload. then read up link