Ive recently gotten into Webdesign in Python, I've tried multiple frameworks but web.py seems to be my favorite except for one problem. I cant seem to figure out how to make multiple pages with multiple templates....
here is my code so far:
import web
urls = (
'/', 'index', '/login/', 'login'
)
app = web.application(urls, globals())
render = web.template.render('templates/')
class index():
def GET(self):
return render.index()
class login():
def GET(self):
return render.login()
if __name__ == '__main__':
app.run()
I get an error when I try to go to the login page :/
Try changing your url mapping:
urls = (
'/', 'index',
'/login/?', 'login',
)
/login/? will work for /login and /login/ url paths.
It will be better if you show an exception that you get.
Related
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)
I'm building the Flask app with React, I ended up having a problem with routing.
The backend is responsible to be an API, hence some routes look like:
#app.route('/api/v1/do-something/', methods=["GET"])
def do_something():
return something()
and the main route which leads to the React:
#app.route('/')
def index():
return render_template('index.html')
I'm using react-router in the React app, everything works fine, react-router takes me to /something and I get the rendered view, but when I refresh the page on /something then Flask app takes care of this call and I get Not Found error.
What is the best solution? I was thinking about redirecting all calls which are not calling /api/v1/... to / it's not ideal as I will get back the home page of my app, not rendered React view.
We used catch-all URLs for this.
from flask import Flask
app = Flask(__name__)
#app.route('/', defaults={'path': ''})
#app.route('/<path:path>')
def catch_all(path):
return 'You want path: %s' % path
if __name__ == '__main__':
app.run()
You can also go an extra mile and reuse the Flask routing system to match path to the same routes as client so you can embed the data client will need as JSON inside the HTML response.
Maybe as extension to the answers before. This solved the problem for me:
from flask import send_from_directory
#app.route('/', defaults={'path': ''})
#app.route('/<path:path>')
def serve(path):
path_dir = os.path.abspath("../build") #path react build
if path != "" and os.path.exists(os.path.join(path_dir, path)):
return send_from_directory(os.path.join(path_dir), path)
else:
return send_from_directory(os.path.join(path_dir),'index.html')
For some reason, the catch-all URLs did not work for me. I found that using the flask 404 handler results in the exact same thing. It sees the url and passes it down to react where your router will handle it.
#app.errorhandler(404)
def not_found(e):
return app.send_static_file('index.html')
Just to inform handle error 404 and render_template works perfectly for me.
#app.errorhandler(404)
def not_found(e):
return render_template("index.html")
I have to combine both catch-all and 404 handler for it to work properly. I am hosting a react-app in a subpath with its own redirection handler from react-router.
#app.route('/sub-path', defaults={'path': 'index.html'})
#app.route('/sub-path/<path:path>')
def index(path):
return send_from_directory('../react-dir/build', path)
#app.errorhandler(404)
def not_found(e):
return send_from_directory('../react-dir/build','index.html')
I just started using Tornado. All I would like to do is click the submit button on index and be sent to login via the form action (this works so far); however, I am having trouble getting the posted data to login and making it work.
First question is, why am I getting the error:
password = self.get_argument('password', None)
NameError: name 'self' is not defined
Here's the python so far:
import os
import tornado.web
import tornado.ioloop
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.render("index.html")
class LoginHandler(tornado.web.RequestHandler):
def post(self):
useremail = self.get_argument('useremail', None)
password = self.get_argument('password', None)
self.render("login.html")
def main():
application = tornado.web.Application([
(r"/", MainHandler),
(r"/login", LoginHandler),
(r"/css/(.*)", tornado.web.StaticFileHandler, {"path": os.path.join(os. path.dirname(__file__), 'css')}),
(r"/pictures/(.*)", tornado.web.StaticFileHandler, {"path": os.path.join(os.path.dirname(__file__), 'pictures')}),
])
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
if __name__ == "__main__":
main()
And secondly, is there anything else that looks glaringly wrong here? I am very uncertain as to how I'm supposed to render webpages like Login.html vs. Index.html, etc.
Syntactically, this seems to work also: return self.write(open("index.html", 'r').read())
What exactly does that do versus what I currently have?
Thanks for the help!
Your indentation is goofed, mixing spaces and tabs. Use python -tt to verify.
also if you do
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.render("index.html")
def post(self):
self.render("index.html")
it will serve index.html for POST too.
.
I'm testing web.py and forms but I cant get any value in return. This is the code:
import web
from web import form
class add:
def GET(self):
f = login()
return render.formtest(f)
def POST(self):
f = login()
print f["ip"].value
return render.formpost(f)
render = web.template.render('templates/')
login = form.Form(
form.Textbox("ip", id="ip"),
form.Textbox('snmp_community'),
)
urls = ( '/','index', '/add', 'add')
app = web.application(urls,globals())
if __name__ == "__main__": app.run()
I followed this example: http://webpy.org/form but when I print the value of f["ip"].value or f.d.ip I always get "None".
Thank you for the help.
Here is a line from the web.py doc:
Note: You cannot access form values before having validated the form!
so you'll have to call f.validates() before you can access the posted data.
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.