I'm maintaining someone else's old CherryPy code, and have an interesting situation I'm trying to understand. Consider this code which demonstrates it:
import cherrypy
class HelloWorld(object):
def index(self, name):
html = """<form method="post">
<input type="text" name="name" value="%s" /></form>
Hello %s!""" % (name, name)
return html
index.exposed = True
cherrypy.quickstart(HelloWorld())
Run it with python hello.py and go to http://127.0.0.1:8080/?name=foo. The output is a text input box with "foo" followed by "Hello foo!".
But if I edit the text in the box and replace it with "bar" and hit enter (submitting the form), the result is not "bar" in the input box and "Hello bar!" below but (apologies for ascii art input box):
+---------------------+
| [u'foo', u'bar'] |
+---------------------+
Hello [u'foo', u'bar']!
It seems that CherryPy is combining the URL querystring "name" argument value with the form "name" value submitted in the body of the POST request and providing a list with the two values to the exposed index() method.
From what I can tell of the code I'm maintaining, it didn't always work this way. So that leads to my two questions:
Did CherryPy change how it handles this situation in some version prior to 3.7.0 (which is what I'm testing with)?
Can I configure CherryPy to revert to that old behavior, or at least to have the POST value override the querystring value?
(I'm not intimately familiar with the CherryPy documentation, but I couldn't find the answer there.)
I've found the answer to question 1 I posed.
The behavior changed with the release of CherryPy 3.2. The actual change was made in git commit e05feef4fee7df1ee5d25d11393f872c9ef12510 (hg:3b92b5aa76f9) on 31 May 2009, which was when the _cpreqbody module was switched in in place of the old process_body() method.
The code used to do this (where self.params is a dict, already containing the params from the query string):
self.body_params = p = httputil.params_from_CGI_form(forms)
self.params.update(p)
From 3.2, it now does this (where key and value are from the POST body being processed):
if key in params:
if not isinstance(params[key], list):
params[key] = [params[key]]
params[key].append(value)
else:
params[key] = value
It looks like there's no easy answer to my question 2, so I'll have to use cherrypy.request.body_params when I know that's where the value I'm after will be found.
I don't think it was the change in 3.x series. You can directly access GET and POST params like in the following snippet. However using unique names is more advised and less error-prone way.
import urlparse
import cherrypy
class HelloWorld:
#cherrypy.expose
def index(self, name):
postParams = cherrypy.request.body.params
getParams = urlparse.parse_qs(cherrypy.request.query_string)
print(postParams, getParams)
# ({u'name': u'bar'}, {'name': ['foo']})
Update
Good research #TimB. By skipping through the codebase I couldn't find where the thing happens. And yes CherryPy 3.2+ is like the series on its own.
CherryPy is quite configurable, and your case is not an exception, when you're able to deal with its notions. Specifically you can write a CherryPy tool to override the mixed request params with POST params. It's just a few lines.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import urlparse
import cherrypy
config = {
'global' : {
'server.socket_host' : '127.0.0.1',
'server.socket_port' : 8080,
'server.thread_pool' : 8
},
'/' : {
'tools.postoverride.on' : True,
}
}
def postOverride():
cherrypy.request.params.update(cherrypy.request.body.params)
cherrypy.tools.postoverride = cherrypy.Tool('before_handler', postOverride)
class HelloWorld:
#cherrypy.expose
def index(self, name):
html = """<form method="post">
<input type="text" name="name" value="%s" /></form>
Hello %s!""" % (name, name)
return html
if __name__ == '__main__':
cherrypy.quickstart(HelloWorld(), '/', config)
I do get your expected result. I imagine there is something wrong with your code.
In particular you use
% (name, name)
Which CANNOT resolve to two different values.
Related
I would like to pass dictionary data from Python into an index.html. (I'm using tornado here, but am open to other small frameworks that allow it.)
I first serialize the data with json.dumps, and this
def start_server():
data = {"a": 1, "b": 3}
string_data = json.dumps(data)
class IndexHandler(tornado.web.RequestHandler):
def get(self):
path = os.path.join(os.path.dirname(__file__), "web")
self.render(os.path.join(path, 'index.html'), data=string_data)
return
app = tornado.web.Application([(r"/", IndexHandler)])
app.listen(8000)
tornado.ioloop.IOLoop.current().start()
return
together with
<script>
console.log("{{data}}");
obj = JSON.parse("{{data}}");
console.log(obj);
</script>
in the index.html gives me
{"a": 1, "b": 3}
Needless to say that JSON.parse fails with those "s.
Any hint on what's going wrong here?
You should probably try {% raw data %}. However, note that since you use the string inside a Javascript quoted string, 'data' itself should contain something suitable for that: and json.dumps() output is not valid - you'd need to escape it appropriately - either in the code that calls .render() or in the template itself (the latter is preferable). Maybe this:
obj = JSON.parse({% raw json_encode(data) %});
(using the fact that json_encode() of a string value will output "escaped-data")
I'm trying to create several URLs on my serv thanks to a loop . The issue is that each function I create in a app.route can't have the same name than the others . And I don't know how to create different function names ...
Here is the code :
json_tweets = []
for line in open('C:\Users\Benjamin\Desktop\DashboardProject\last_rated_set.json',"r"):
json_tweets.append(json.loads(line,"ISO-8859-1"))
cashtag_tab = []
for tweet in json_tweets:
if not(tweet['cashtag'] in cashtag_tab) :
cashtag_tab.append(tweet['cashtag'])
for i in range(0,(len(cashtag_tab)-1)) :
var=cashtag_tab[i]
#app.route("/"+var)
def company(var) :
finance=Share(var)
datas = finance.get_historical('2014-01-01', '2014-12-31')
datas = json.dumps(datas, default=json_util.default)
return datas
I'm getting the error AssertionError : View function mapping is overwritting an existing endpoint function : company
This fails because Flask derives the endpoint name from the function by default, but it would anyway fail later because the function company requires an argument var and the route is not parameterised. The simplest option would be just checking the value inside the handler:
#api.route('/<var>')
def company(var):
if var not in cashtag_tab:
abort(404)
If you want all the routes to be in the routing map for any reason, I once needed a similar thing and came up with something like this:
def url_family(source, methods=('GET',)):
def decorator(f):
for entry in source:
# create a handler that delegates to your function
def view_func(entry=entry, **kwargs):
return f(entry, **kwargs)
endpoint = '{0}_{1}'.format(f.__name__, entry)
url = '/{0}'.format(entry)
api.add_url_rule(url,
methods=methods,
endpoint=endpoint,
view_func=view_func)
return decorator
Then you register the handlers as:
#url_family(cashtag_tab)
def company(var):
...
Assuming that you are using flask now, you should consider Custom URL Converter. Check links below
http://flask.pocoo.org/docs/0.10/api/#flask.Flask.url_map - url_map UrlConverter API
https://exploreflask.com/views.html#url-converters - example url converter
https://stackoverflow.com/a/5872904/3451543 - RegexConverter by Philip Southam
Anyway, specifying more details on your question is always helpful to get accurate answer :)
i am developing one of my site with the python django where i have been using angularjs in one of my page where i have given the user option to search (specific request). Here is my model..
class Request(models.Model):
description = models.TextField(blank=True,null=True)
category = models.ForeignKey(Category)
sub_category = models.ForeignKey(SubCategory)
In my views I am returning through the following code:
def some(code, x):
exec code
return x
def search_request(request):
page = request.GET.get('page')
term = request.GET.get('term')
i = 0
terms = "x = Request.objects.filter("
for t in term.split(" "):
i=i+1
if(len(term.split(" "))>i):
terms = terms +"Q(name__icontains='"+t+"')|"
else:
terms = terms +"Q(name__icontains='"+t+"'))"
junk = compile(terms,'<string>', 'exec')
spit = Request.objects.filter(name__icontains=term)
requests = some(junk,spit)
output = HttpResponse()
output['values']=[{'requests':r,'category':r.category,'subcategory':r.sub_category} for r in requests]
return JSONResponse(output['values'])
In my HTML code when I return using AngularJS:
$scope.search = function(){
$scope.results = $http.get("{% url 'search-requests-show' %}?term="+$scope.term).then(
function(result){
return result.data;
}
);
}
The result on the HTML Output comes as in {[{results}]}:
"[{'category': <Category: The New Category>, 'requests': <Request: Need a Table>, 'subcategory': <SubCategory: Testsdfsdfsad>}]"
The problem is that I am not being able to access using results.category because the output is in "string", so the ng-repeat="result in results" brings the result as
[ { ' c a .....
I am probably doing something wrong in view. If anybody has any suggestion then please answer.
JSONResponse is probably using standard python json encoder, which doesn't really encode the object for you, instead it outputs a string representation (repr) of it, hence the <Category: The New Category> output.
You might need to use some external serializer class to handle django objects, like:
http://web.archive.org/web/20120414135953/http://www.traddicts.org/webdevelopment/flexible-and-simple-json-serialization-for-django
If not, you should then normalize object into simple python types inside the view (dict, list, string.., the kind that json module has no problem encoding). So instead doing:
'category':r.category
you could do:
'category': {'name': r.category.name}
Also as a sidenote: using exec is super bad idea. Don't use it in production!
You should return json strings for Angular:
import json
def resource_view(request):
# something to do here
return HttpResponse(json.dumps(your_dictionary))
for better usage i recommend djangorestframework.
Off Topic:
$http.get("{% url 'search-requests-show' %}?term="+$scope.term);
you can pass 'param' object:
$http.get("{% url 'search-requests-show' %}", {param : {term:$scope.term}});
In flask, I can do this:
render_template("foo.html", messages={'main':'hello'})
And if foo.html contains {{ messages['main'] }}, the page will show hello. But what if there's a route that leads to foo:
#app.route("/foo")
def do_foo():
# do some logic here
return render_template("foo.html")
In this case, the only way to get to foo.html, if I want that logic to happen anyway, is through a redirect:
#app.route("/baz")
def do_baz():
if some_condition:
return render_template("baz.html")
else:
return redirect("/foo", messages={"main":"Condition failed on page baz"})
# above produces TypeError: redirect() got an unexpected keyword argument 'messages'
So, how can I get that messages variable to be passed to the foo route, so that I don't have to just rewrite the same logic code that that route computes before loading it up?
You could pass the messages as explicit URL parameter (appropriately encoded), or store the messages into session (cookie) variable before redirecting and then get the variable before rendering the template. For example:
from flask import session, url_for
def do_baz():
messages = json.dumps({"main":"Condition failed on page baz"})
session['messages'] = messages
return redirect(url_for('.do_foo', messages=messages))
#app.route('/foo')
def do_foo():
messages = request.args['messages'] # counterpart for url_for()
messages = session['messages'] # counterpart for session
return render_template("foo.html", messages=json.loads(messages))
(encoding the session variable might not be necessary, flask may be handling it for you, but can't recall the details)
Or you could probably just use Flask Message Flashing if you just need to show simple messages.
I found that none of the answers here applied to my specific use case, so I thought I would share my solution.
I was looking to redirect an unauthentciated user to public version of an app page with any possible URL params. Example:
/app/4903294/my-great-car?email=coolguy%40gmail.com to
/public/4903294/my-great-car?email=coolguy%40gmail.com
Here's the solution that worked for me.
return redirect(url_for('app.vehicle', vid=vid, year_make_model=year_make_model, **request.args))
Hope this helps someone!
I'm a little confused. "foo.html" is just the name of your template. There's no inherent relationship between the route name "foo" and the template name "foo.html".
To achieve the goal of not rewriting logic code for two different routes, I would just define a function and call that for both routes. I wouldn't use redirect because that actually redirects the client/browser which requires them to load two pages instead of one just to save you some coding time - which seems mean :-P
So maybe:
def super_cool_logic():
# execute common code here
#app.route("/foo")
def do_foo():
# do some logic here
super_cool_logic()
return render_template("foo.html")
#app.route("/baz")
def do_baz():
if some_condition:
return render_template("baz.html")
else:
super_cool_logic()
return render_template("foo.html", messages={"main":"Condition failed on page baz"})
I feel like I'm missing something though and there's a better way to achieve what you're trying to do (I'm not really sure what you're trying to do)
You can however maintain your code and simply pass the variables in it separated by a comma: if you're passing arguments, you should rather use render_template:
#app.route("/baz")
def do_baz():
if some_condition:
return render_template("baz.html")
else:
return render_template("/foo", messages={"main":"Condition failed on page baz"})
I have a filter that I use for lang support in my webapp. But when I publish it to GAE it keeps telling me that it the usage of CPU is to high.
I think I located the problem to my filters I use for support. I use this in my templates:
<h1>{{ "collection.header"|translate:lang }}</h1>
The filter code looks like this:
import re
from google.appengine.ext import webapp
from util import dictionary
register = webapp.template.create_template_register()
def translate(key, lang):
d = dictionary.GetDictionaryKey(lang, key)
if d == False:
return "no key for " + key
else:
return d.value
register.filter(translate)
I'm to new to Python to see what's wrong with it. Or is the the entire wrong approach?
..fredrik
Little more about what I'm trying to do: I'm trying to find away to handle language support. A user needs to be able to update text elements via an admin page. As of now I have all text elements stored in a db.model. And use a filter to get the right key based on language.
After a lot of testing I still can't get to work well enough. When published I still get error messages in the logs about to much CPU usage. A typical page has about 30-50 text elements. And according to the logs it uses about 1500ms (900ms API) for each page load. I'm starting to think this might not be the best approach?
I've tried using both memcache and indexes to get around the CPU usage. It helps a little. Should one use memcache and manually added indexes?
This is how my filter looks like:
import re
from google.appengine.ext import webapp
from google.appengine.api import memcache
from util import dictionary
register = webapp.template.create_template_register()
def translate(key, lang):
re = "no key for " + key
data = memcache.get("dictionary" + lang)
if data is None:
data = dictionary.GetDictionaryKey(lang)
memcache.add("dictionary" + lang, data, 60)
if key in data:
return data[key]
else:
return "no key for " + key
register.filter(translate)
And util.dictionary looks like this:
from google.appengine.ext import db
class DictionaryEntries(db.Model):
lang = db.StringProperty()
dkey = db.StringProperty()
value = db.TextProperty()
params = db.StringProperty()
#property
def itemid(self):
return self.key().id()
def GetDictionaryKey(lang):
entries = DictionaryEntries.all().filter("lang = ", lang)
if entries.count() > 0:
langObj = {}
for entry in entries:
langObj[entry.dkey] = entry.value
return langObj
else:
return False
Your initial question is about high cpu usage, the answer i think is simple, with GAE and databases like BigTable (non-relational) the code with entries.count() is expensive and the for entry in entrie too if you have a lot of data.
I think you must have to do a couple of things:
in your utils.py
def GetDictionaryKey(lang, key):
chache_key = 'dictionary_%s_%s' % (lang, key)
data = memcache.get(cache_key)
if not data:
entry = DictionaryEntries.all().filter("lang = ", lang).filter("value =", key).get()
if entry:
data = memcache.add(cache_key, entry.value, 60)
else:
data = 'no result for %s' % key
return data
and in your filter:
def translate(key, lang):
return dictionary.GetDictionaryKey(lang, key)
This approach is better because:
You don't make the expensive query of count
You respect the MVC pattern, because a filter is part of the Template (View in the pattern) and the method GetDictionaryKey is part of the Controler.
Besides, if you are using django i suggest you slugify your cache_key:
from django.template.defaultfilters import slugify
def GetDictionaryKey(lang, key):
chache_key = 'dictionary_%s_%s' % (slugify(lang), slugify(key))
data = memcache.get(cache_key)
if not data:
entry = DictionaryEntries.all().filter("lang = ", lang).filter("value =", key).get()
if entry:
data = memcache.add(cache_key, entry.value, 60)
else:
data = 'no result for %s' % key
return data
Have you considered switching to standard gettext methods? Gettext is a widely spread approach for internationalization and very well embedded in the Python (and the Django) world.
Some links:
Python's gettext module
Django's support for gettext with special attention to unicode
PoEdit, an editor for .po-files produced by pygettext
Your template would then look like this:
{% load i18n %}
<h1>{% trans "Header of my Collection" %}</h1>
The files for translations can be generated by manage.py:
manage.py makemessages -l fr
for generating french (fr) messages, for example.
Gettext is quite performant, so I doubt that you will experience a significant slow-down with this approach compared to your storage of the translation table in memcache. And what's more, it let's you work with "real" messages instead of abstract dictionary keys, which is, at least in my experience, ways better, if you have to read and understand the code (or if you have to find and change a certain message).