📜 ⬆️ ⬇️

Google AppEngine from the start: Controller

We are moving forward with the speed of a reactive paravoz, and while the habra people read and comprehend the first and second parts of the article, I write a sequel with the speed of a machine gun. This time it will be about the heart of any web application -

Controller


Some time ago, we had already identified several URLs in app.yaml - it's time to figure out how to make the application respond correctly to them. This is how our mappings look like:

 # $ Id: app.yaml 4 2010-01-25 12: 14: 48Z sigizmund $

 application: helloworld
 version: 1
 runtime: python
 api_version: 1

 handlers:
 - url: / (stats | login)
   script: main.py
   login: required

 - url:. *
   script: main.py

')
As we can see, three types of URLs are defined - /stats , /login and “everything else”. All three, which is typical, will be processed by the same main.py script, but the settings are different - /stats and /login require an active user session, while for the rest it is not necessary. Let's look at the contents of the main.py script:

 #! / usr / bin / env python

 '' '
 $ Id: main.py 4 2010-01-25 12: 14: 48Z sigizmund $
 '' '

 import controller

 from google.appengine.ext import webapp
 from google.appengine.ext.webapp import util
 from google.appengine.api import users

 def main ():
   application = webapp.WSGIApplication ([('/', controller.DefaultRequestHandler), 
                                 ('/ stats', controller.StatsRequestController),
                                 ('/ login', controller.LoginController)],
                                        debug = True)
   util.run_wsgi_app (application)


 if __name__ == '__main__':
   main ()


He is pretty laconic. That is, it is extremely concise, but at the same time very important - all that it does is import the contents of the controllers and create the webapp.WSGIApplication instance that matches the request URLs and the corresponding handlers. As a rule, it is convenient to put these handlers in a separate package in order to separate the service code from the real code, which, in fact, determines the behavior of the application. Consider these handlers in turn.

DefaultRequestHandler - default handler


As can be seen from the code above, this handler will be used for all requests that came to the “main page” of the application - namely, “/”. Its code is located below:

 class DefaultRequestHandler (webapp.RequestHandler): 
     '' '
     Handles default requests - checks if user is logged in;  if it is - saves an information about
     his visit in the database.
     '' '

     def get (self):
         user = users.get_current_user ()

         page = None
         if not user:
             page = view.StartPage (self.request)
         else:
             page = view.WelcomePage (self.request)

         page.render (self.response.out)


In principle, this code is no different from the MainHandler code in the first part , except that it does not generate the page content on its own, but uses some auxiliary classes. At this stage, we will not consider what they are doing - it’s enough for us to know that they generate HTML that meets our requirements - that is, it offers to log in users who have not done so and welcome those who have successfully coped with it.

Login Handler - between two pages


Let me remind you a picture with the structure of our application:

gaehabr

As follows from the description of the /login page, the user will redirect to it automatically and also automatically redirected further to the page / . What happens on this page? For this we need to consider two classes at once.

 class LoggedInRequestHandler (webapp.RequestHandler):
     def currentVisitor (self):
         user = users.get_current_user ()

         # we shouldn't check user, as / login and / stats specifies 
         # login: required in app.yaml

         q = model.Visitor.all ()
         q.filter ('user =', user)

         qr = q.fetch (2)

         if len (qr) == 0:
             u = model.Visitor ()
         elif len (qr)> 1:
             # something is horribly wrong here, it shouldn't happen
             # but it still could
             logging.error ("Duplicating user% s in datastore"% user.nickname ())
             raise Exception ("Duplicating user% s in datastore"% user.nickname ())
         else:
             u = qr [0]

         self.currentVisitor = u
         return u


Perhaps this is not the best architectural solution, but for illustrative purposes, we declare the class and inherit it from webapp.RequestHandler . As you can see, the described class does not define the get method — that is, as an independent developer, it will be rather useless. All it does is provide the currentVisitor() method, which either gets from the Datastore or creates a new Visitor instance and returns it. Consider this code in more detail.

The described approach uses the Datastore Query , which begins with “all” objects of a given type, and then gradually “refines” the final data set by calling filter() and ancestor() . The example is very simple, but it shows almost everything you need to extract the necessary entry from the Datastore; Of course, in real applications, this query will probably be much more complicated.

Now, having such a useful class, we can easily describe our handler:

 class LoginController (LoggedInRequestHandler): 
     '' '
     We use this controller for the login event.
     '' '
     def get (self):
         u = self.currentVisitor ()

         u.hits = u.hits + 1
         u.put ()

         self.redirect ('/')

As can be seen from the code, the handler receives the Visitor instance associated with the current user (possibly creating it), increases the number of visits by one and saves the instance. After that, the user is redirected to the main page without unnecessary words You no longer need to check the user, because the URL /login marked as requiring a mandatory login (that is, if the user tries to log in without authentication, he will be automatically redirected to the login page).

StatsRequestController - statistics on request


The code of the last handler is extremely simple. It is so simple that it makes one think that there obviously should be something else:

 class StatsRequestController (LoggedInRequestHandler): 
     def get (self):
         u = self.currentVisitor ()
         page = view.StatsPage (self.request, u)

         page.render (self.response.out)

Indeed, there is something else. This is something called Presentation, it uses Django templates and will be described in the next part of this article ;-)

I remind you that the full source code can be viewed in SVN , and I am waiting for your questions and comments!

PS think up, please, as source codes with illumination in Habr to spread - and then I'll fill in all the pictures!

Source: https://habr.com/ru/post/81933/


All Articles