I'm making some Django projects using Redis as Backend Cache[1], but I can't be sure that the Redis Server will be On all the time, then I'm trying to use Redis "if" it's available otherwise use some other Backend like LocMem and so on.
The Redis Backend that I'm using[1] is full compatible so I can use Django Decorations.
I was think to create a function to be called like that:
from django.views.decorators.cache import cache_page
from utils import PingBackend
from time import time
#cache_page(60, cache=PingBackend(time()))
def index(request):
artigos = Artigo.objects.filter(ativo=1)
return render_to_response('index.html', {'artigos':artigos}, RequestContext(request))
The problem is that Django (Internals I guess) Caches the response of PingBackend() and call it just the first time, even if I drop the RedisServer Django tells that the ping process was successfully.
It occurs even with DEBUG=True and 'default' CacheBackend to dummy.
def PingBackend(time):
print time
response = None
try:
con = StrictRedis(host=settings.REDIS_HOST, port=settings.REDIS_PORT, db=0)
# Execute some action
con.ping()
# If not give an exception, use redis
response = 'redis'
except:
response = 'default' #dummy
return last_response
I'm passing time() just to create some differentiation as a try solve the cache problm.
The big picture is that the function PingBackend() aren't executing for each request, just for the first the I can't monitor the Redis Server.
Thank you!
[1] - https://github.com/niwibe/django-redis
This is not about Django internals, this is about how decorators work. When you define your view like this:
#cache_page(60, cache=PingBackend(time()))
def index(request):
blah blah
it is exactly equivalent to this:
def index(request):
blah blah
index = cache_page(60, cache=PingBackend(time()))(index)
You are invoking cache_page only one, passing it a cache argument that you got by invoking PingBackend once. It isn't even executing just for the first request, it is executing once when the view function is defined.
You should write your own cache backend that uses Redis if it's available, or something else if it is not.
Related
I have a Flask app, which is running a web-app to control an alarm. I use apscheduler to trigger a function triggerAlarm() and within that I want to have the logic to actually trigger it, but also I want to have the web-page to display another alarm.html, no matter which site the user is currently on.
The idea is to show the user, that the alarm went off by opening another html page (possibly the main page, which has context dependent text and layout)! Also the page content is outdated after an alarm. The client must reload the page, if the after-alarm info should be displayed/updated.
This is the key problem: in my head it is necessary to redirect every client to the alarm page at this event. Maybe there is a better solution all along. (I don't want to use JavaScript, since I use 4 languages already besides the 3 I use regularly.)
The problem is, that the function itself has no "route" and is not part of the app (this is what I think happens). Also I cannot easily make it a route, since it is activated not by a html request, but by the scheduler.
How can I trigger a function and redirect to a specified page?
*I tried redirect() and RequestRedirect() w/ and w/o url_for() and return render_template('index.html', **templateData) ... but I cannot load the desired page.
The code looks something like this:
from apscheduler.schedulers.background import BackgroundScheduler
# Web Server Gateway WSGI
from flask import Flask, render_template, request, url_for
scheduler = BackgroundScheduler()
scheduler.start()
scheduler.add_job(triggerAlarm,'date',run_date=somedate)
#app.route("/")
def index():
# prepare templateData
return render_template('index.html', **templateData)
def triggerAlarm():
# this doesn't work, since it is called by the scheduler and has no route!
return redirect(url_for("/"))
If you have defined a method, "triggerAlarm()", and want to make it available to a route, then create your route like:
#app.route("/alarm-url")
def web_alarm():
triggerAlarm()
return redirect(url_for("/"))
This will cause the same thing that happens when the scheduler runs triggerAlarm() to happen when a user hits the route /alarm-url, and then return them home.
The takeaway is that you can define methods outside of flask route, in other modules, etc, and then call those methods in a flask route.
Update
If you have to keep triggerAlarm() separate from any routes you could do something like:
class StatusDenied(Exception):
pass
#app.errorhandler(StatusDenied)
def web_alarm(error):
return redirect(url_for("/"))
def triggerAlarm():
...
raise StatusDenied
I'm creating a large number of Flask routes using regular expressions. I'd like to have a unit test that checks that the correct routes exist and that incorrect routes 404.
One way of doing this would be to spin up a local server and use urllib2.urlopen or the like. However, I'd like to be able to run this test on Travis, and I'm assuming that's not an option.
Is there another way for me to test routes on my application?
Use the Flask.test_client() object in your unittests. The method returns a FlaskClient instance (a werkzeug.test.TestClient subclass), making it trivial to test routes.
The result of a call to the TestClient is a Response object, to see if it as 200 or 404 response test the Response.status_code attribute:
with app.test_client() as c:
response = c.get('/some/path/that/exists')
self.assertEquals(response.status_code, 200)
or
with app.test_client() as c:
response = c.get('/some/path/that/doesnt/exist')
self.assertEquals(response.status_code, 404)
See the Testing Flask Applications chapter of the Flask documentation.
Martjin's answer surely solve your issue, but some times you don't have the time (or will) to mock all the components you call in a route you want to test for existence.
And why would you need to mock? Well, the call get('some_route') will first check for this route to exists and then ... it will be executed!
If the view is a complex one and you need to have fixtures, envs variables and any other preparation just for test if the route exists, then you need to think again about your test design.
How to avoid this:
You can list all the routes in your app. An check the one you're testing is in the list.
In the following example, you can see this in practice with the implementation of a site-map.
from flask import Flask, url_for
app = Flask(__name__)
def has_no_empty_params(rule):
defaults = rule.defaults if rule.defaults is not None else ()
arguments = rule.arguments if rule.arguments is not None else ()
return len(defaults) >= len(arguments)
#app.route("/site-map")
def site_map():
links = []
for rule in app.url_map.iter_rules():
# Filter out rules we can't navigate to in a browser
# and rules that require parameters
if "GET" in rule.methods and has_no_empty_params(rule):
url = url_for(rule.endpoint, **(rule.defaults or {}))
links.append((url, rule.endpoint))
# links is now a list of url, endpoint tuples
references:
get a list of all routes defined in the app
Helper to list routes (like Rail's rake routes)
Another way of testing a URL without executing the attached view function is using the method match of MapAdapter.
from flask import Flask
app = Flask(__name__)
#app.route('/users')
def list_users():
return ''
#app.route('/users/<int:id>')
def get_user(id):
return ''
Testing
# Get a new MapAdapter instance. For testing purpose, an empty string is fine
# for the server name.
adapter = app.url_map.bind('')
# This raise werkzeug.exceptions.NotFound.
adapter.match('/unknown')
# This raises werkzeug.exceptions.MethodNotAllowed,
# Although the route exists, the POST method was not defined.
adapter.match('/users', method='POST')
# No exception occurs when there is a match..
adapter.match('/users')
adapter.match('/users/1')
From Werkzeug documentation:
you get a tuple in the form (endpoint, arguments) if there is a match.
Which may be useful in certain testing scenarios.
I have a Flask app that takes parameters from a web form, queries a DB with SQL Alchemy and returns Jinja-generated HTML showing a table with the results. I want to cache the calls to the DB. I looked into Redis (Using redis as an LRU cache for postgres), which led me to http://pythonhosted.org/Flask-Cache/.
Now I am trying to use Redis + Flask-Cache to cache the calls to the DB. Based on the Flask-Cache docs, it seems like I need to set up a custom Redis cache.
class RedisCache(BaseCache):
def __init__(self, servers, default_timeout=500):
pass
def redis(app, config, args, kwargs):
args.append(app.config['REDIS_SERVERS'])
return RedisCache(*args, **kwargs)
From there I would need to something like:
# not sure what to put for args or kwargs
cache = redis(app, config={'CACHE_TYPE': 'redis'})
app = Flask(__name__)
cache.init_app(app)
I have two questions:
What do I put for args and kwargs? What do these mean? How do I set up a Redis cache with Flask-Cache?
Once the cache is set up, it seems like I would want to somehow "memoize" the calls the DB so that if the method gets the same query it has the output cached. How do I do this? My best guess would be to wrap the call the SQL Alchemy in a method that could then be given memoize decorator? That way if two identical queries were passed to the method, Flask-Cache would recognize this and return to the appropriate response. I'm guessing that it would look like this:
#cache.memoize(timeout=50)
def queryDB(q):
return q.all()
This seems like a fairly common use of Redis + Flask + Flask-Cache + SQL Alchemy, but I am unable to find a complete example to follow. If someone could post one, that would be super helpful -- but for me and for others down the line.
You don't need to create custom RedisCache class. The docs is just teaching how you would create new backends that are not available in flask-cache. But RedisCache is already available in werkzeug >= 0.7, which you might have already installed because it is one of the core dependencies of flask.
This is how I could run the flask-cache with redis backend:
import time
from flask import Flask
from flask_cache import Cache
app = Flask(__name__)
cache = Cache(app, config={'CACHE_TYPE': 'redis'})
#cache.memoize(timeout=60)
def query_db():
time.sleep(5)
return "Results from DB"
#app.route('/')
def index():
return query_db()
app.run(debug=True)
The reason you're getting "ImportError: redis is not a valid FlaskCache backend" is probably because you don't have redis (python library) installed which you can simply install by:
pip install redis.
your redis args would look something like this:
cache = Cache(app, config={
'CACHE_TYPE': 'redis',
'CACHE_KEY_PREFIX': 'fcache',
'CACHE_REDIS_HOST': 'localhost',
'CACHE_REDIS_PORT': '6379',
'CACHE_REDIS_URL': 'redis://localhost:6379'
})
Putting the #cache.memoize over a method that grabs the info from the DB should work.
Server.Transfer is sort of like a Redirect except instead of requesting the browser to do another page fetch, it triggers an internal request that makes the request handler "go to" another request handler.
Is there a Python equivalent to this in Google App Engine?
Edit: webapp2
With most Python frameworks the request handler is simply a function: I should imagine you can just import the actual handler function you want to use and pass it the parameters you received in the current handler function.
In Django (for example): you usually have a function that takes at least 1 parameter, the request object. You should be able to simply import the next handler and then return the result of executing it. Something like:
def actual_update_app_queue_settings(request):
return HttpResponse()
def update_app_queue_settings(request):
return actual_update_app_queue_settings(request):
For the framework you've mentioned, probably something like this:
class ProductHandler(webapp2.RequestHandler):
def get(self, product_id):
self.response.write('You requested product %r.' % product_id)
class ProductHandler2(webapp2.RequestHandler):
def get(self, product_id):
nph = ProductHandler()
nph.initialize(request, response)
nph.get(product_id)
I'm fudging that by looking at http://webapp-improved.appspot.com/guide/handlers.html: it looks reasonable. If you're using route annotations I'm honestly not sure what you do, but that might do it.
Usually, you just have to call the corresponding method.
For being more specific... Which flavour of AppEngine are you using? Java, Python, Go... Php?
If you are using java/servlet, then the "forward" is
protected void doGet(HttpServletRequest request, HttpServletResponse response){
request.getRequestDispatcher("/newurl").forward(request, response);
}
I am trying to use session to pass some data from one page to another page.
Here is the code i wrote in ajax.py.
def save_cookie(request, query):
request.session['query'] = query
But when i call this dajaxice function.An error will occurred. As we all know that when we try to use dajaxice in html page, the error msg always is "sth goes wrong".
I tried to debug save_cookie, but the mock request object i created has no session attr. However, if i do request.session="blah", it worked. If I directly use save_cookie(request,query). It will pop up the error msg that request object has no attr seesion...
The code is right straight forward. I didn't see any mistake in it. Does anyone know the cause?
Never used dajaxice / dajax so I can't really help here. Just a few points:
did you enable (and properly configue) the session support ? https://docs.djangoproject.com/en/1.3/topics/http/sessions/
you can use the logging module (or a plain "print" statement but then you won't have the whole traceback) to trace the exception, ie :
def save_cookie(request, query):
try:
request.session['query'] = query
except Exception, e:
print e
raise
The output of the print statement should now appear in the shell you started the dev server from (assuming you're working with the dev server... you ARE workin with the dev server, aren't you ?)
still using the dev server, you can use pdb to switch to interactive debugging:
def save_cookie(request, query):
import pdb; pdb.set_trace()
request.session['query'] = query
then try to access the url in your browser, switch back to your shell and you're in a pdb session where you can inspect the request and (if there's one) request.session object etc.
NB : don't do this if running behind Apache or any other web server - only with the builtin dev server.
"request.session='blah'" will create the "session" attribute on the "request" object if it doesn't exist (and possibly replace the real "session" object if it already existed), so it's neither a valid test nor something sensible to do
My 2 cents...
Disclaimer: I don't know anything about dajaxice.
The following will work on a mock request object:
def save_cookie(request, query):
if not hasattr(request, 'session'):
request.session = dict()
request.session['query'] = query