3

Let's say I have a class which stores important, dynamic data which I need to render my sites. This class should be created individually per user, and the data from the class needs to get updated according to some user input, so I have at least two views:

@app.route('/')
def index():
    myclass = MyClass()
    return render_template('index.html')

@app.route('/_update', methods=['GET'])
def update():
    ret_data = {"value": request.args.get('c', type=float)}
    a = myclass.calculate(ret_data['value'])
    return jsonify(result=a)

Ofcourse it can't work this way, because myclass wouldn't exist in update() - so a working solution would be to make myclass global on creation. But this doesn't seem clean and it ruins the possibility for individual classes per session.

So, is there any clean way to access a class instance in different views, or how should I handle this, it doesn't feel like an uncommon scenario to me.

Secondly, I would like to have the class instance created for every user, but also closed when every a user closes his browser window etc. - I don't really get how to do this, I have a __del__() function in my class, but it won't be used if I set the instance to global.

Thank you very much!

2 Answers 2

4

You have a fundamental misunderstanding about how web applications work. They are stateless: nothing is shared between requests for any particular user. On the contrary, any global variable will be accessible to whichever user happens to hit the application next.

The way to store state for a user is to put it in the database. You can use a session cookie to associate an individual user with their particular data.

Sign up to request clarification or add additional context in comments.

4 Comments

Thank you, you are absolutely right, I don't know much about web applications yet. Let me try to explain a bit more my problem: The class takes a long time to start (let's say 20s) - so it should only start once per user, e.g. after he clicked a "start" button. Afterwards, the class instance is still needed, because it does quite some calculations depending on user input and sending the new data back to the client (actually, the client requests the data) - but since the class needs a lot of memory, it should be closed when the user leaves / is out for some time.
I don't see right now how a database could solve this - since my class already behaves (naively thought) like a database. I mean, I could store the initial class data in a database, but what when update() is called? I need to update the database data exactly from the same class instance.
The point is that there is no persistent connection between a user and a backend process. So even if you instantiated your class on the first request for a user, the next request from that user will probably not go to the same process; and the next request to that process will probably be from a separate user. The only way to do something like this is to use long-running connections (eg Websockets), which is a completely different way of writing apps.
Thank you for the clarification - would you mind taking a look at my solution? I'm absolutely new to this field, so it might just be really really bad.
1

As Daniel Rosemann pointed out, it's probably not how one should design a web application. There is however a way to reach that functionality using global variables plus multiple instances. I don't know enough about python to estimate how wrong (or even dangerous) the use of global variables is, but it seems working for me - I'm happy about every comment on this solution:

Setting two global dicts, one to store the class instances, one for keep track if the instance is still relevant:

global session_instances, session_alive
session_instances = {}
session_alive = {}

In my root view I create a uuid and save the class instance with it in the dict and start a thread which should close my class after some time:

@app.route('/')
def index():
    if not session.get('uid'):
        session['uid'] = uuid.uuid4()
        session_instances.update({session['uid'] : pyd2d.d2d()})
        session_alive.update({session['uid'] : 0})
        t = Thread(target=close_session, args = (session['uid'], ))
        t.start()
    return render_template('index.html')

The thread responsible for closing (e.g. 15 seconds after the last request):

def close_session(uid):
    global session_alive
    while True:
        time.sleep(15)
        if session_alive[uid] == 0:
            session_instances[uid].stop()
            break
        session_alive[uid] = 0

And finally, to update the timer anytime a request is send:

@app.before_request
def before_request():
    global session_alive
    if session.get('uid'):
        session_alive[session['uid']] = 1

This seems to work just fine. Should I feel bad about using global variables, or is it ok in cases like this? I appreciate any input!

1 Comment

Hmm, this code works perfectly fine even without the three "global" lines - I wonder why? Since the global variables are defined outside, I might access them without setting them to "global", but I thought I'm not allowed to change them inside a function (as in before_request) without using "global ..."?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.