Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

webapp2.Route with optional leading part

I am learning the webapp2 framework with its powerful Route mechanism.

My application is supposed to accept URIs like these:

/poll/abc-123
/poll/abc-123/
/poll/abc-123/vote/       # post new vote
/poll/abc-123/vote/456    # view/update a vote

Polls may optionally be organized into categories, so all the above should work also like this:

/mycategory/poll/abc-123
/mycategory/poll/abc-123/
/mycategory/poll/abc-123/vote/
/mycategory/poll/abc-123/vote/456

My incorrect configuration:

app = webapp2.WSGIApplication([
    webapp2.Route('/<category>/poll/<poll_id><:/?>', PollHandler),
    webapp2.Route('/<category>/poll/<poll_id>/vote/<vote_id>', VoteHandler),
], debug=True)

Question: How could I fix my configuration?

If possible it should be optimized for GAE CPU-time/hosting fee. For example, it may be faster if I add two lines for each entry: one line with category and another one without category...

like image 202
zengabor Avatar asked Feb 24 '23 07:02

zengabor


2 Answers

webapp2 has a mechanism to reuse common prefixes, but in this case they vary, so you can't avoid duplicating those routes, as in:

app = webapp2.WSGIApplication([
    webapp2.Route('/poll/<poll_id><:/?>', PollHandler),
    webapp2.Route('/poll/<poll_id>/vote/<vote_id>', VoteHandler),
    webapp2.Route('/<category>/poll/<poll_id><:/?>', PollHandler),
    webapp2.Route('/<category>/poll/<poll_id>/vote/<vote_id>', VoteHandler),
], debug=True)

You should not worry about adding many routes. They are really cheap to build and match. Unless you have tens of thousands, reducing the number of routes won't matter.

A small note: the first route accepts an optional end slash. You could instead use the RedirectRoute to accept only one and redirect if the other is accessed, using the option strict_slash=True. This is not well documented but has been around for a while. See the explanation in the docstring.

like image 189
moraes Avatar answered Mar 05 '23 16:03

moraes


I am going to add my solution to this as a complimentary answer on top of @moraes.
So other people having problems like below can get a more complete answer.

  1. Trailing Slash Problem
  2. Optional Parameter Problem

In addition, I figured out how to route both /entity/create and /entity/edit/{id} in one regex.
Below are my routes that supports the following url patterns.

  1. /
  2. /myentities
  3. /myentities/
  4. /myentities/create
  5. /myentities/create/
  6. /myentities/edit/{entity_id}
SITE_URLS = [
    webapp2.Route(r'/', handler=HomePageHandler, name='route-home'),

    webapp2.Route(r'/myentities/<:(create/?)|edit/><entity_id:(\d*)>',
        handler=MyEntityHandler,
        name='route-entity-create-or-edit'),

    webapp2.SimpleRoute(r'/myentities/?',
        handler=MyEntityListHandler,
        name='route-entity-list'),
]

app = webapp2.WSGIApplication(SITE_URLS, debug=True)

Below is my BaseHandler that all my handlers inherit from.

class BaseHandler(webapp2.RequestHandler):
    @webapp2.cached_property
    def jinja2(self):
        # Sets the defaulte templates folder to the './app/templates' instead of 'templates'
        jinja2.default_config['template_path'] = s.path.join(
            os.path.dirname(__file__),
            'app',
            'templates'
        )

        # Returns a Jinja2 renderer cached in the app registry.
        return jinja2.get_jinja2(app=self.app)

    def render_response(self, _template, **context):
        # Renders a template and writes the result to the response.
        rv = self.jinja2.render_template(_template, **context)
        self.response.write(rv)

Below is my MyEntityHandler python class with the get() method signature for the Google App Engine Datastore API.

class MyEntityHandler(BaseHandler):
    def get(self, entity_id, **kwargs):
        if entity_id:
            entity = MyEntity.get_by_id(int(entity_id))
            template_values = {
                'field1': entity.field1,
                'field2': entity.field2
            }
        else:
            template_values = {
                'field1': '',
                'field2': ''
            }
    self.render_response('my_entity_create_edit_view_.html', **template_values)



    def post(self, entity_id, **kwargs):
        # Code to save to datastore. I am lazy to write this code.

        self.redirect('/myentities')
like image 21
3 revs Avatar answered Mar 05 '23 14:03

3 revs