Search code examples
pythonflaskwerkzeugfile-storageflask-uploads

TypeError: storage must be a werkzeug.FileStorage in Flask Upload


So what I did is that I tried to run my flask app but suddenly I got a error which is TypeError: storage must be a werkzeug.FileStorage

This is the code that I use...

init.py

# IMPORT

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_bcrypt import Bcrypt
from flask_migrate import Migrate
from flask_login import LoginManager
from flask_uploads import UploadSet, configure_uploads, IMAGES, patch_request_class
from blogapp.config import Config

# INIT

db = SQLAlchemy()
bcrypt = Bcrypt()
login_manager = LoginManager()
migrate = Migrate()
login_manager.login_view = 'users.login'
login_manager.login_message_category = 'info'
photos = UploadSet('photos', IMAGES)

def create_app(config_class=Config):
    app = Flask(__name__)
    app.config.from_object(Config)

    db.init_app(app)
    bcrypt.init_app(app)
    login_manager.init_app(app)
    migrate.init_app(app, db)
    configure_uploads(app, photos)
    patch_request_class(app)  

    from blogapp.users.routes import users
    from blogapp.posts.routes import posts
    from blogapp.main.routes import main
    from blogapp.errors.handlers import errors
    app.register_blueprint(users)
    app.register_blueprint(posts)
    app.register_blueprint(main)
    app.register_blueprint(errors)

    return app

config.py

import os

basedir = os.path.abspath(os.path.dirname(__file__))

class Config:
    SECRET_KEY = os.environ.get('SECRET_KEY')
    SQLALCHEMY_DATABASE_URI = os.environ.get('SQLALCHEMY_DATABASE_URI')
    UPLOADED_PHOTOS_DEST = os.path.join(basedir, 'uploads')

I have a uploads folder in my parent directory

routes.py

from flask import (render_template, url_for, flash,
                   redirect, request, abort, Blueprint)
from flask_login import current_user, login_required
from blogapp import db, photos
from blogapp.models import Post
from blogapp.posts.forms import PostForm

posts = Blueprint('posts', __name__)

@posts.route("/post/new", methods=['GET', 'POST'])
@login_required
def new_post():
    form = PostForm()
    if form.validate_on_submit():
        filename = photos.save(form.photo.data)
        file_url = photos.url(filename)
    else:
        file_url = None
    if form.validate_on_submit():
        post = Post(title=form.title.data, content=form.content.data, image=form.photo.data, author=current_user)
        db.session.add(post)
        db.session.commit()
        flash('Your post has been created!', 'success')
        return redirect(url_for('main.home'))
    return render_template('create_post.html', title='New Post', 
                            form=form, file_url=file_url, legend="Post")

Can someone help me? I'm a bit confuse with how I got the error.. Did I do something wrong?

There isn't any thing I could find out there, so for me this is very confusing.. Could it be something wrong with the application factory while using flask upload?

Traceback


Solution

  • When I have a look at your routes.py, I see some possible problems.

    new_post does accept both get and post requests, but does not handle them differently.

    e.g. usually when there is a get request, you want to just render the empty form, but when there is a post request, you want to e.g. save the file.

    You have two if form.validate_on_submit statements. You can simplify them when you move the else to the bottom, just above the return statement. Actually, you do not need an else path as, when the if path is valid, you leave the function with the return redirect already.

    About your problem.

    The error message clearly says, that form.photo.data is no uploaded file.

    Please fix the above suggestions and try it again.

    Also make sure you do not send an empty form. Btw - you did not show your form template. Maybe there is no data field?

    If you followed all suggestions, but without success, please set a breakpoint just after e.g. form = PostForm() and carefully step through your program and especially inspect what value form.photo.data shows.