Hello Stackoverflow Guru's!
I'm a complete newb, and I've got a question that I can't seem to find the answer to (hopefully because it's so simple nobody has bothered to ask).
I'm designing a website that has a bunch of recipes using google app engine. I'd like to be able to render a bunch of the recipe pages using one handler, because I plan of having lots of recipes later and I don't want to have to make a new handler for each one. My code is below:
import urllib2
import webapp2
import jinja2
import os
JINJA_ENVIRONMENT = jinja2.Environment(
loader=jinja2.FileSystemLoader(os.path.dirname(__file__)),
extensions=['jinja2.ext.autoescape'],
autoescape=True)
class Handler(webapp2.RequestHandler):
def write(self, *a, **kw):
self.response.out.write(*a, **kw)
def render_str(self,template,**params):
t = JINJA_ENVIRONMENT.get_template(template)
return t.render(params)
def render(self,template,**kw):
self.write(self.render_str(template,**kw))
class MainHandler(Handler):
def get(self):
template = JINJA_ENVIRONMENT.get_template('main.html')
self.response.write(template.render())
class RecipeHandler(Handler, recipe):
def get(self, recipe):
recipe_pages = {
'carbonara' : 'carbonara.html'
'burger' : 'burger.html'
}
if recipe in recipe_pages:
template = JINJA_ENVIRONMENT.get_template(recipe_pages[recipe])
self.response.write(template.render())
else:
self.abort(404)
app = webapp2.WSGIApplication([
('/', MainHandler),
('/carbonara', RecipeHandler(carbonara)),
('/burger',RecipeHandler(burger)),
], debug=True)
I basically want to avoid writing out a "CarbonaraHander" and "BurgerHandler", and just use "RecipeHandler" to render both pages. I know this should be possible, but I have no idea how to do it.
Any help is appreciated!
Edit: I think I should be using something called regular expressions? But I don't really understand how they need to be used in this case.
AFAIK you can't pass args to the handler, you need to extract them from the request. This is what I'd do (pushed it a bit further to directly use the template name in the URl routing):
class RecipeHandler(Handler):
def extract_template_name_from_request(self):
return self.request.path_info[9:] # strip leading '/recipes/' (or whatever else you need)
def get(self):
template_name = self.extract_template_name_from_request()
try:
template = JINJA_ENVIRONMENT.get_template(template_name)
except Exception:
# can't locate a template matching the requested path
self.abort(404)
return
# prepare the template values as needed
values = {'recipe': {'name': template_name[:-5]}} # just an example
try:
self.response.write(template.render(values))
except Exception:
# failure rendering the template
self.abort(500)
app = webapp2.WSGIApplication([
('/recipes/.*.html', RecipeHandler), # see extract_template_name_from_request()
('/.*', MainHandler),
], debug=True)
Related
I need to do calculations among other things within lib.py and I need to access the user input data within main.py for it, because it needs to be that way. How do I do this?
doing from main import MainHandler in lib.py and then calling it "works" in the sense that it doesn't give any code errors, but displays a blank page when done that way and I get a log error saying that I cannot import it.
main.py
import webapp2
from lib import FormData
from pages import FormPage
from pages import ResultsPage
class MainHandler(webapp2.RequestHandler):
def get(self):
f = FormPage()
s = ResultsPage()
fd1 = FormData()
if self.request.GET:
fd1.name = self.request.GET['name']
fd1.email = self.request.GET['email']
fd1.weight = self.request.GET['weight']
fd1.height = self.request.GET['height']
self.response.write(s.second_page(fd1.name, fd1.email, fd1.weight, fd1.height))
else:
self.response.write(f.form_page)
app = webapp2.WSGIApplication([
('/', MainHandler)
], debug=True)
lib.py
class FormData(object):
def __init__(self):
pass
I need to be able to access:
fd1.name = self.request.GET['name']
fd1.email = self.request.GET['email']
fd1.weight = self.request.GET['weight']
fd1.height = self.request.GET['height']
In lib.py
Your if/else statement is outside the get method, so it shouldn't even work properly.
I think the most clean way would be to pass the request data to the FormData class already in the initialiser. So the code would look like this:
main.py
import webapp2
from lib import FormData
from pages import FormPage
from pages import ResultsPage
class MainHandler(webapp2.RequestHandler):
def get(self):
f = FormPage()
s = ResultsPage()
fd1 = FormData(data=self.request.GET)
if fd1.name: # This is None, if form has not been submitted
self.response.write(s.second_page(fd1.name, fd1.email, fd1.weight, fd1.height))
else:
self.response.write(f.form_page)
app = webapp2.WSGIApplication([
('/', MainHandler)
], debug=True)
lib.py
class FormData(object):
def __init__(self, data):
self.name = data.get('name')
self.email = data.get('email')
self.weight = data.get('weight')
self.height = data.get('height')
As a side note, I don't really know this webapp2 framework, so there might be a better way to do this.
I'm trying to get sessions to work with Webapp2 with GAE, but I'm stuck retrieving the stored parameters. Maybe I'm overlooking the obvious.
A dressed down version of with main.py where I store a value in the webapp2 session:
import webapp2
from webapp2_extras import sessions
other imports...
class BaseHandler(webapp2.RequestHandler): # Copied from Google's doc
def dispatch(self):
# Get a session store for this request.
self.session_store = sessions.get_store(request=self.request)
try:
# Dispatch the request.
webapp2.RequestHandler.dispatch(self)
finally:
# Save all sessions.
self.session_store.save_sessions(self.response)
#webapp2.cached_property
def session(self):
# Returns a session using the default cookie key.
return self.session_store.get_session()
class MainPage(webapp2.RequestHandler): # My main page proper
def get(self):
self.session['foo'] = 'bar' # Store somehing in the session
template_values = {
}
template = JINJA_ENVIRONMENT.get_template('index.html')
self.response.write(template.render(template_values))
application = webapp2.WSGIApplication([
('/', MainPage, ),], debug = True)
My problem is how to access the value stored in the session from another module. In test.py I do the following:
from webapp2_extras import sessions
import main
other imports ...
class Test(main.BaseHandler):
def post(self):
foo1 = self.session.get('foo')
return foo1
class ProductsPage(webapp2.RequestHandler):
def get(self):
foo2 = Test.post()
...
But I get the following error:
TypeError: unbound method post() must be called with Test instance as first argument (got nothing instead)
I just can't find the proper way to use the Test class and retrieve the value stored in the session. I must be overlooking something when using the class, but I'm stuck and can't find the way out.
Maybe somebody looking from outside will spot it easily?
Looking further I found a post that helped me
GAE webapp2 session handling not working when putting basehandler to a different file
After some tinkering here's the recipe that worked for me to setup a session in webapp2 and share the session among different .py files. Hope it saves others some time.
1) Create basehandler.py, this will need to be imported by all .py files using the session. This is straight from Google's documentation:
import webapp2
from webapp2_extras import sessions
class BaseHandler(webapp2.RequestHandler):
def dispatch(self):
# Get a session store for this request
self.session_store = sessions.get_store(request = self.request)
try:
webapp2.RequestHandler.dispatch(self)
finally:
self.session_store.save_sessions(self.response)
#webapp2.cached_property
def session(self):
# Returns a session using the default cookie key
return self.session_store.get_session()
2) Create main.py importing BaseHandler. In my case I store a value in the session:
import os
import urllib
import webapp2
import jinja2
from basehandler import BaseHandler
JINJA_ENVIRONMENT = jinja2.Environment(
loader=jinja2.FileSystemLoader(os.path.dirname(__file__)),
extensions=['jinja2.ext.autoescape'])
class MainPage(BaseHandler):
def get(self):
self.session['foo'] = 'bar' # Store a value in the session
# you can store more than one :-)
# Define values that will be passed onto the template
template_values = {
'title': 'TITLE'
}
# search for an appropriate template and render it.
template = JINJA_ENVIRONMENT.get_template('./templates/index.html')
self.response.write(template.render(template_values))
config = {}
config['webapp2_extras.sessions'] = {
'secret_key': 'my-super-secret-key'
}
application = webapp2.WSGIApplication([
('/', MainPage)
], config = config, debug = True)
3) Create another.py taking care to also import BaseHandler and use the same secret key. Here you can retrieve the value stored by main.py or any other .py file sharing the same session:
import os
import urllib
import webapp2
import jinja2
from basehandler import BaseHandler
JINJA_ENVIRONMENT = jinja2.Environment(
loader=jinja2.FileSystemLoader(os.path.dirname(__file__)),
extensions=['jinja2.ext.autoescape'])
class AnotherPage(BaseHandler):
def get(self):
foo1 = self.session.get('foo')
# Now go and do something with foo1, even store a new value as in main.py
# ...
# ...
# Define values that will be passed onto the template
template_values = {
'title': 'TITLE'
}
# search for an appropriate template and render it.
template = JINJA_ENVIRONMENT.get_template('./templates/additional.html')
self.response.write(template.render(template_values))
config = {}
config['webapp2_extras.sessions'] = {
'secret_key': 'my-super-secret-key'
}
application = webapp2.WSGIApplication([
('/', AdditionalPage)
], config = config, debug = True)
I would like to parse the URL upon load to see if it has any parameters. I'm just trying to set up a basic test to see if that's possible. What is the correct regex to send a url like http://example.com/?hiyall to ParamHandler?
class ParamHandler(webapp2.RequestHandler):
def get(self):
self.response.out.write('parameters detected')
class MainHandler(webapp2.RequestHandler):
def get(self):
self.response.out.write('Hello World')
application = webapp2.WSGIApplication ([('/', MainHandler), ('/\?.*', ParamHandler)], debug=True)
Do something like this in get(self):
class ParamHandler(webapp2.RequestHandler):
def get(self):
hyl = self.request.get("hiyall")
self.response.out.write("hiyall was: " + hyl)
Customary official docs link: https://developers.google.com/appengine/docs/python/tools/webapp/requestclass#Request_get
If you're using webapp2, you cannot route requests based on parameters.
But you can create a condition based on query_string which can check if parameters exist. Like the following:
class MainHandler(webapp2.RequestHandler):
def get(self):
if self.request.query_string:
self.response.out.write('Has parameters')
else:
self.response.out.write('No parameters')
application = webapp2.WSGIApplication ([('/', MainHandler)], debug=True)
Definitely a beginner question... basically, I want to have the same design from this website. I understand the HTML part, but the problem is that I am not completely sure how to actually render multiple html files in one view.
http://www.htmliseasy.com/frames_tutor/templates/template1.html
My webpage is in this linkk. http://everyology.appspot.com/biology
What I am using is Google app engine. Here is a partial code that is relevant to the question. My concern is with class BioPage... I wrote these and am seeing the frames but getting 404 errors in my webpage.
import os
import webapp2
from string import letters
import jinja2
template_dir = os.path.join(os.path.dirname(__file__), 'templates')
jinja_env = jinja2.Environment(loader = jinja2.FileSystemLoader(template_dir),
autoescape = True)
def render_str(template, **params):
t = jinja_env.get_template(template)
return t.render(params)
class MainHandler(webapp2.RequestHandler):
def write(self, *a, **kw):
self.response.out.write(*a, **kw)
def render_str(self, template, **params):
return render_str(template, **params)
def render(self, template, **kw):
self.write(self.render_str(template, **kw))
class MainPage(MainHandler):
def get(self):
self.render('index.html')
class BioPage(MainHandler):
def get(self):
self.render('biology.html')
self.render('doc1.html')
self.render('doc2.html')
The problem you're running in to is how <frame> tags operate. To fill in frames, browsers make an additional request per frame. Your code is trying to guess ahead, shipping all of the frame contents back in one clump. That's not how it works. You're going to need separate handlers for each of your frames, in addition to the main page. Alternatively, those pages that are completely static can be served static pages.
How can I rewrite url for python:
http://localhost:8081/?page=1
to
http://localhost:8081/1
here is my code, but it's not working:
class MainPage(webapp.RequestHandler):
def get(self, page):
mypage = self.request.get('page')
self.response.headers['Content-Type'] = 'text/plain'
if mypage == "":
self.response.out.write('Hello, webapp World!')
else:
self.response.out.write('page is ' + mypage)
application = webapp.WSGIApplication([('/', MainPage),('/(\d+)', MainPage)], debug=True)
You can use regular expressions in your controller. It's not Apache-style URL rewriting per se, but it gets the job done. The rewritten parameter is passed as an argument to the handler.
class MyRequestHandler(webapp.RequestHandler):
def get(self, page):
self.response.headers['Content-Type'] = 'text/plain'
if not page:
self.response.out.write('Hello, webapp World!')
else:
self.response.out.write('page is ' + page)
url_map = [('/(\d+)', MyRequestHandler)]
application = webapp.WSGIApplication(url_map, debug=True)
See How to configure app.yaml to support urls like /user/<user-id>? for a similar application.
Assuming you're using webapp:
class Rewriter(webapp.RequestHandler):
def get(self):
self.redirect(self.request.get('page'))
application = webapp.WSGIApplication([('/', Rewriter)],)
def main():
run_wsgi_app(application)
if __name__ == "__main__":
main()
You've defined two mappings for your MainPage handler, one that will pass in no parameters ('/'), and one that will pass in one parameter ('/(\d+)'). Your handler, however, expects exactly one argument, named page.
You either need to use two different handlers, or supply a default value for the page argument, like this:
class MainPage(webapp.RequestHandler):
def get(self, page=None):
if not page:
self.redirect('/%s', self.request.get('page'))
return
self.response.headers['Content-Type'] = 'text/plain'
self.response.out.write('page is ' + mypage)
For future reference, when you get a stacktrace, include it in your question! Saying "It's not working" and making us guess exactly what's going wrong isn't a good way to get useful answers to your question.