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!
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.
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!
Related
I have seen different "patterns" in handling this case so I am wondering if one has any drawbacks comapred to the other.
So lets assume that we wish to create a new object of class MyClass and add it to the database. We can do the following:
class MyClass:
pass
def builder_method_for_myclass():
# A lot of code here..
return MyClass()
my_object=builder_method_for_myclass()
with db.managed_session() as s:
s.add(my_object)
which seems that only keeps the session open for adding the new object but I have also seen cases where the entire builder method is called and executed within the managed session like so:
class MyClass:
pass
def builder_method_for_myclass():
# A lot of code here..
return MyClass()
with db.managed_session() as s:
my_object=builder_method_for_myclass()
are there any downsides in either of these methods and if yes what are they? Cant find something specific about this in the documentation.
When you build objects depending on objects fetched from a session you have to be in a session. So a factory function can only execute outside a session for the simplest cases. Usually you have to pass the session around or make it available on a thread local.
For example in this case to build a product I need to fetch the product category from the database into the session. So my product factory function depends on the session instance. The new product is created and added to the same session that the category is also in. An implicit commit should also occur when the session ends, ie the context manager completes.
def build_product(session, category_name):
category = session.query(ProductCategory).where(
ProductCategory.name == category_name).first()
return Product(category=category)
with db.managed_session() as s:
my_product = build_product(s, "clothing")
s.add(my_product)
I have a website which needs to have a single button which anyone who visits the site can turn on or off. I'm using google appengine. The variable is unable to change from "off" to "on"... I'm confused as to where my error is. I'm still new to this stuff.
I'm not sure how to have this universal variable.. I started by using NDB to store a variable, but I'm unable to changed the variable from "off" to "on". I'm hoping to get an outside opinion on how this might be easier to work.
This is what I have so far as my structure for the variable.
class UpdatedThing(ndb.Model):
OnOff = ndb.StringProperty(default='off')
def switch(s):
onoff = UpdatedThing()
if s == "on":
onoff.OnOff = "off"
else:
onoff.OnOff = "on"
onoff.put()
print s
The "s" variable is the current value for the on/off button, which is navigated from here.
class Switch(webapp2.RequestHandler):
def get(self):
s = models.UpdatedThing().OnOff
print s
models.switch(s)
self.redirect('/')
Thank you
Every time you call UpdateThing() you are creating a new instance of UpdateThing model , this means you will have a new one created. If you have a single entity or any number of entities you want updated you need to retrieve (get()) the entity and then update it and then save those changes to the datastore (save())
Also from a design point of view the switch function should really be a method of the Thing object, also I think your model name should be Thing rather than UpdateThing. UpdateThing isn't a noun. It implies doing something but your working with an entity.
Tim is exactly right: you need a single instance of the entity, not a new one on every setting and query. Ignoring his reasonable style criticism, and sticking to your chosen naming and structure, something like:
Assuming this part is in models.py:
import ndb
class UpdatedThing(ndb.Model):
OnOff = ndb.StringProperty(default='off')
def get_or_make_thing():
# get the existing entity
onoff = UpdatedThing.query().get()
# if none existed, make one
if onoff is None: onoff = UpdatedThing()
return onoff
def switch(s):
onoff = get_or_make_thing()
if s == "on":
onoff.OnOff = "off"
else:
onoff.OnOff = "on"
onoff.put()
# why print? if you need to see it, use logging! print s
and this part is in someother py file:
import models
import webapp2
class Switch(webapp2.RequestHandler):
def get(self):
s = models.get_or_make_thing().OnOff
# again, print in app enging makes no sense: print s
models.switch(s)
self.redirect('/')
Those prints make no sense in App Engine, so I've commented them out -- if you need to keep a log of events, import logging and call logging.info.
I am setting up an app in GAE. The app relies on up to three successive AJAX calls from the client that produces an individual string in each request. I would like to retain and combine the data on the server side in order to perform some more parsing on it.
I have been told that using global variables is really really bad, but I'm not sure how I would structure this flow without using globals to combine the data. Any suggestions? I don't want to do one single AJAX call because I would like to continue to update the user as to the progress. Abstractly it looks something like this-
finalData = ""
class func1(webapp2.RequestHandler):
def get(self):
func1data = some.command()
global finalData
finalData += func1data
class func2(webapp2.RequestHandler):
def get(self):
func2data = some.command()
global finalData
finalData += func2data
class func3(webapp2.RequestHandler):
def get(self):
func3data = some.command()
global finalData
finalData += func3data
It's a terrible idea, as in, it won't work. Or worse, it might appear to work sometimes when your requests happen to hit the same instance, but it won't work otherwise.
Store the data in an entity in the the datastore/memcache (if you use ndb it'll automatically be put in memcache), and use a get() to fetch the data on each successive request.
Alternatives are to use sessions, or return the data in a cookie to the client so it's sent back to the server with the next request (though that would insecurely allow the client to modify the value).
It seems that module variables live as long as a process lives and do not reset until the process restarts.
Here is my code which i expect to behave another way that it behaves now:
I have a module responsible for various SEO features like breadcrumbs and title, file fancy/utils.py:
class Seo:
title = ['project name']
Later in my views i can add items to Seo.title (news.views for example):
from fancy.utils import Seo
def index(request, news_id):
title.append('some specific title')
...
The point is that the variable Seo.title actually does not reset at every request, so it continues to append items to itself and it looks very strange to me (as i came from PHP).
Eventually if i hit F5 at the same page the title always grows up to smth huge and long.
What's going on and what should i do?
thanks
It seems from your comments that you have totally misunderstood the execution model of Django.
You can't have data local to the request that you can somehow access from anywhere in the code. If you need data associated with a particular request, you should store it somewhere where the code running that request can retrieve it: perhaps in the session, perhaps in a temporary dictionary added onto the request object itself. Anything you store globally will be, well, global: visible to any request running inside the same process.
Your title is a class attribute not an instance attribute. If you want to preserve settings across multiple requests you could keep a reference to it in the session.
e.g.
class Seo(object):
def __init__(self):
self.title = ['project name']
...
def index(request, news_id):
seo = request.session.get('seo', Seo())
seo.title.append('some specific title')
I love CherryPy's API for sessions, except for one detail. Instead of saying cherrypy.session["spam"] I'd like to be able to just say session["spam"].
Unfortunately, I can't simply have a global from cherrypy import session in one of my modules, because the cherrypy.session object isn't created until the first time a page request is made. Is there some way to get CherryPy to initialize its session object immediately instead of on the first page request?
I have two ugly alternatives if the answer is no:
First, I can do something like this
def import_session():
global session
while not hasattr(cherrypy, "session"):
sleep(0.1)
session = cherrypy.session
Thread(target=import_session).start()
This feels like a big kludge, but I really hate writing cherrypy.session["spam"] every time, so to me it's worth it.
My second solution is to do something like
class SessionKludge:
def __getitem__(self, name):
return cherrypy.session[name]
def __setitem__(self, name, val):
cherrypy.session[name] = val
session = SessionKludge()
but this feels like an even bigger kludge and I'd need to do more work to implement the other dictionary functions such as .get
So I'd definitely prefer a simple way to initialize the object myself. Does anyone know how to do this?
For CherryPy 3.1, you would need to find the right subclass of Session, run its 'setup' classmethod, and then set cherrypy.session to a ThreadLocalProxy. That all happens in cherrypy.lib.sessions.init, in the following chunks:
# Find the storage class and call setup (first time only).
storage_class = storage_type.title() + 'Session'
storage_class = globals()[storage_class]
if not hasattr(cherrypy, "session"):
if hasattr(storage_class, "setup"):
storage_class.setup(**kwargs)
# Create cherrypy.session which will proxy to cherrypy.serving.session
if not hasattr(cherrypy, "session"):
cherrypy.session = cherrypy._ThreadLocalProxy('session')
Reducing (replace FileSession with the subclass you want):
FileSession.setup(**kwargs)
cherrypy.session = cherrypy._ThreadLocalProxy('session')
The "kwargs" consist of "timeout", "clean_freq", and any subclass-specific entries from tools.sessions.* config.