I'm having trouble creating admin pages on my Python Google App Engine site. I think the answer should be pretty straightforward, but honestly, I've been trying to understand how classes inheriting from other classes, or using functions to wrap other functions, and I just can't seem to get a good understanding of it.
Basically, my site has two kinds of pages: the main page, and then some pages that allow the user to perform admin actions. The main page can be seen by anyone without signing in. The other pages are for admins. The only users with accounts are admins, so I've set up webapp2 sessions, and as long as
self.sessions.get('username')
returns something that's enough to be allowed access to the other pages.
Here are my handlers:
class BaseHandler(webapp2.RequestHandler):
def write(self, *a, **kw):
self.response.out.write(*a, **kw)
def render(self, template, **kw):
self.response.out.write(render_str(template, **kw))
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 MainHandler(BaseHandler):
def get(self):
animals = Animal.query().fetch(100)
self.render('index.html',animals=animals)
class AdminHandler(BaseHandler):
def get(self):
if self.session.get('username'):
self.render('admin.html')
else:
self.render('signin.html')
class ReorderHandler(BaseHandler):
def get(self):
self.render('reorder.html')
def post(self):
#Change order of item display
self.write('OK')
class DeleteHandler(BaseHandler):
def get(self):
self.render('delete.html')
def post(self):
#Delete entry from db
self.write('OK')
class AddHandler(BaseHandler):
def get(self):
self.render('add.html')
def post(self):
#add entry to db
self.write('OK')
class SigninHandler(BaseHandler):
def post(self):
#Check username and password
if valid:
self.session['username'] = username
self.redirect('/admin')
else:
self.write('Not valid')
The AdminHandler lays out the basic logic of what these Admin pages should do. If someone is trying to access an admin pages, the handler should checks to see if user is signed in, and if so, allow access to the page. If not, it renders the sign-in page.
Reorder, Delete, and Add are all actions I want admins to be able to do, but there might be more in the future. I could add the AdminHandler logic to all the GETs and POSTs of those other handlers, but that is extremely repetitive and therefore I am sure that it is the wrong thing to do.
Looking for some guidance on how to get the logic of AdminHandler incorporated into all of the other Handlers that cover "administrative" tasks.
Update: Brent Washburne pointed me in the right direction enough to get the thing working, although I still don't feel like I understand what the decorator function actually does. Anyway, the code seems to be working, and now looks like this:
def require_user(old_func):
def new_function(self):
if not self.session.get('username'):
self.redirect('/signin')
old_func(self)
return new_function
class AdminHandler(BaseHandler):
@require_user
def get(self):
self.render('admin.html')
class AddHandler(BaseHandler):
@require_user
def get(self):
self.render('add.html')
@require_user
def post(self):
name = self.request.get('name')
qry = Animal.query(Animal.name == name).get()
if not qry:
new_animal = Animal(name=name)
new_animal.put()
self.write('OK')
And so on for all the other "admin" Handlers.
Here's a brute-force way to ensure a user is logged in for every page (except the login page), or it redirects them to the login page:
def dispatch(self):
# Get a session store for this request.
self.session_store = sessions.get_store(request=self.request)
if not self.session['username'] and self.request.get('path') != '/login':
return redirect('/login')
A better way is to add this code to the top of every get() and put() routine:
def get(self):
if not self.session['username']:
return redirect('/login')
An even better way is to turn that code into a decorator so all you need to add is one line:
@require_login
def get(self):
....