Where does GAE store permalinks?
Question: So I have the following code GAE code in python and it is a little long but
basically it allows a user to post to a blog and then saves each blog post to
a permalink which the user is redirected to and the user can relink to
forever. The permalink url is just blog/(the blog post ID). So the permalink
is being created in the NewPost handler by the redirect method (correct me if
I am wrong) and and then where is the list of these permalinks being stored?
Is it accessible somehow? Because I cannot see it visually this part is
tripping me up a little.

I learned all this code from Udacity.com but they didn't really explain this
point and I can get it to work but don't understand how it is working.

    
    
    import os
    import re
    from string import letters
    
    import webapp2
    import jinja2
    
    from google.appengine.ext import db
    
    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 BlogHandler(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))
    
    def render_post(response, post):
        response.out.write('<b>' + post.subject + '</b><br>')
        response.out.write(post.content)
    
    #front page
    class MainPage(BlogHandler):
      def get(self):
          self.write('Hello, Udacity!')
    
    def blog_key(name = 'default'):
        return db.Key.from_path('blogs', name)
    
    #creates the database
    class Post(db.Model):
        subject = db.StringProperty(required = True)
        content = db.TextProperty(required = True)
        created = db.DateTimeProperty(auto_now_add = True)
        last_modified = db.DateTimeProperty(auto_now = True)
    
        def render(self):
            self._render_text = self.content.replace('\n', '<br>')
            return render_str("post.html", p = self)
    
    #front of blog with list of last 10 posts
    class BlogFront(BlogHandler):
        def get(self):
            posts = db.GqlQuery("select * from Post order by created desc limit 10")
            self.render('front.html', posts = posts)
    
    #displays the permalink page
    class PostPage(BlogHandler):
        def get(self, post_id):
            key = db.Key.from_path('Post', int(post_id), parent=blog_key())
            post = db.get(key)
    
            if not post:
                self.error(404)
                return
    
            self.render("permalink.html", post = post)
    
    #create a new post
    class NewPost(BlogHandler):
        def get(self):
            self.render("newpost.html")
    
        def post(self):
            subject = self.request.get('subject')
            content = self.request.get('content')
    
            if subject and content:
                p = Post(parent = blog_key(), subject = subject, content = content)
                p.put()
                self.redirect('/blog/%s' % str(p.key().id()))
            else:
                error = "subject and content, please!"
                self.render("newpost.html", subject=subject, content=content, error=error)
    
    
    app = webapp2.WSGIApplication([('/', MainPage),
                                   ('/unit2/welcome', Welcome),
                                   ('/blog/?', BlogFront),
                                   ('/blog/([0-9]+)', PostPage),
                                   ('/blog/newpost', NewPost),
                                   ],
                                  debug=True)
    

UPDATE:

Would it be possible to change the code in the following way to make it a
little more search engine friendy:

    
    
    class NewPost(BlogHandler):
        def get(self):
            self.render("newpost.html")
    
        def post(self):
            subject = self.request.get('subject')
            content = self.request.get('content')
    
            if subject and content:
                p = Post(parent = blog_key(), subject = subject, content = content)
                p.put()
                #the change is in the following line:
                self.redirect('/blog/%s/%s' % (str(p.key().id(), str(p.subject)))
            else:
                error = "subject and content, please!"
                self.render("newpost.html", subject=subject, content=content, error=error)
    

FINAL UPDATE:

I solved the problem of making it more search engine friendly with the
following bit of code:

    
    
     def post(self):
                subject = self.request.get('subject')
                content = self.request.get('content')
    
            if subject and content:
                p = Post(parent = blog_key(), subject = subject, content = content)
                p.put()
                subject = p.subject
                subject = subject.replace(' ', '25fdsa67ggggsd5')
                subject = ''.join(e for e in subject if e.isalnum())
                subject = subject.replace('25fdsa67ggggsd5', '-')
                subject = subject.lower()
                self.redirect('/blog/%s/%s' % (str(p.key().id(), str(subject)))
            else:
                error = "subject and content, please!"
                self.render("newpost.html", subject=subject, content=content, error=error)
    


Answer: The ID of the blog post is all that is needed to retrieve it front the
datastore, so there is no need for a mapping of any kind. A request comes in
with the permalink URL, the ID is extracted from the URL, the post is
retrieved from the datastore using the ID, the blog post is rendered.

It is a simple and effective strategy, but one area where it falls flat is
search engine optimization. Most search engines would prefer a URL that has
words in it rather than just a string of numbers and characters.

This is commonly called slugging, and it would require you to provide a
mapping between the ID and a string. Several years ago, I wrote a mixin class
that provided easy slugging for AppEngine/Python programs: [sluggable-
mixin](http://code.google.com/p/sluggable-mixin/). It should still work.

