0

I'm working on a small web app to create some diagrams and I need to create a variable to hold a unique file name for each web app session so that users don't end up getting the wrong file when they save the diagram as a pdf. To do this I've wrapped the related views in a class using flask_classful and created an instance variable to hold the file name.

class PiperView(FlaskView):
    route_base = '/'

    def __init__(self):
        self.piper_name = '_init_.pdf'

        self.tst_index = 0
        self.tst_plot = 0
        self.tst_download = 0
        self.tst_master = 0

    @route('/',methods=['GET','POST'])
    @route('/index/',methods=['GET','POST'],endpoint='index')
    @nocache
    def index(self):
        self.piper_name = '_piper.pdf'

        #test Code
        #=======================================================================
        file = open(fpath+'index.txt','a')
        self.tst_index += 1
        self.tst_master += 1
        file.write(str(self.tst_index)+"."+str(self.tst_master)+") " +str(self.piper_name)+', ')
        file.close() 
        #=======================================================================

        plot_data = np.loadtxt('piper_data.csv', delimiter=',', skiprows=1 )
        html_plot = Markup(piper(plot_data, ' ', alphalevel=1.0, color=False, file_nam=self.piper_name))
        return render_template('plot.html',title='The Plot', figure=html_plot)

    @route('/plot',methods=['GET','POST'],endpoint='plot')
    @nocache
    def plot(self):
        self.piper_name = str(random.randint(0,10000001))+'_piper.pdf'

        #test Code
        #=======================================================================
        file = open(fpath+'plot.txt','a')
        self.tst_plot += 1
        self.tst_master += 1
        file.write(str(self.tst_plot)+"."+str(self.tst_master)+" ) " +str(self.piper_name)+', ')
        file.close() 
        #=======================================================================

        try:
            f = request.files['data_file']
            plot_data = np.loadtxt(f, delimiter=',', skiprows=1 )
            html_plot = Markup(piper( plot_data, ' ', alphalevel=1.0, color=False, file_nam=self.piper_name))
            return render_template('plot.html',title='The Plot', figure=html_plot)
        except:
            return render_template('plot.html',title='The Plot', figure="There Seems To Be A Problem With Your Data")     


    @route('/download',methods=['GET','POST'],endpoint='download')
    @nocache
    def download(self):
        #test Code
        #=======================================================================
        file = open(fpath+'download.txt','a')
        self.tst_download += 1
        self.tst_master += 1
        file.write(str(self.tst_download)+"."+str(self.tst_master)+") " +str(self.piper_name)+', ')
        file.close()
        #=======================================================================

        return send_from_directory(directory=fpath,filename=self.piper_name)

The problem is that the instance variable that holds the file name doesn't get shared between methods. I added some test code to try and figure out what was happening. The 'tst_index', 'tst_plot' and 'tst_download' each behave as expected in that they get incremented but the 'tst_master' does not get incremented between method calls.

The output from the test code is:

index.txt
1.1) _piper.pdf,

plot.txt
1.1 ) 7930484_piper.pdf, 2.2 ) 9579691_piper.pdf,

download.txt
1.1) init.pdf, 2.2) init.pdf,

when I call the index view one (1) time, the plot view two (2) times and the download view (2) times. As you can see the 'tst_master' instance variable is not getting updated between method calls.

I know this would work in plain python as I tested it but what am I missing about flask and flask_classful that is causing this?

davidism
  • 121,510
  • 29
  • 395
  • 339
  • your web application is probably being served across multiple threads or even processes, so different instances of your class are handling different kinds of requests. – avigil Feb 22 '18 at 04:01

2 Answers2

0

Embedding state like that in your application is generally a bad idea. There is no guarantee that the view instance the generates a response will persist for more than that one request. Store your data outside of flask- server-side on a database, a key-value store, or even a file on disk somewhere, or client-side in the browser. Flask has a number of plugins that make that easier (flask-sqlalchemy, flask-session, flask-redis, etc).

Flask natively offers the flask.session object, which stores information in cookies on the client side. flask-session would probably do what you want without much additional overhead if you were concerned with storing things server side. Just configure it with the File System session interface and you get a flask.session variable that handles all the magic of linking user requests to data stored on the filesystem.

avigil
  • 2,218
  • 11
  • 18
0

You are overcomplicating your task. You probably don't need to use flask-classful for it.

You can use ordinary flask sessions. Session is unique for each user. The only thing you need is to use some unique ID for each file. This file id can be user id if your users log in into your web app and their credentials are stored in the db. Or you can randomly generate this file id. Then you can store the filename in the flask session like this:

from flask import session
...

def plot(...):

    session['user_file_name'] = user_file_name


def download(...):

    user_file_name = session['user_file_name']

Hope this helps.

Nurjan
  • 5,889
  • 5
  • 34
  • 54